]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #101248 - RalfJung:miri, r=RalfJung
authorbors <bors@rust-lang.org>
Fri, 2 Sep 2022 00:53:50 +0000 (00:53 +0000)
committerbors <bors@rust-lang.org>
Fri, 2 Sep 2022 00:53:50 +0000 (00:53 +0000)
update Miri

r? `@ghost`
Fixes https://github.com/rust-lang/rust/issues/101067

669 files changed:
.gitignore
Cargo.lock
RELEASES.md
compiler/rustc_ast/Cargo.toml
compiler/rustc_ast/src/ast.rs
compiler/rustc_ast/src/ast_traits.rs
compiler/rustc_ast/src/lib.rs
compiler/rustc_ast/src/tokenstream.rs
compiler/rustc_ast/src/util/literal.rs
compiler/rustc_ast_lowering/Cargo.toml
compiler/rustc_ast_lowering/src/expr.rs
compiler/rustc_ast_lowering/src/index.rs
compiler/rustc_ast_lowering/src/lib.rs
compiler/rustc_ast_lowering/src/path.rs
compiler/rustc_ast_passes/src/ast_validation.rs
compiler/rustc_ast_passes/src/feature_gate.rs
compiler/rustc_ast_passes/src/lib.rs
compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
compiler/rustc_borrowck/src/diagnostics/region_errors.rs
compiler/rustc_borrowck/src/diagnostics/region_name.rs
compiler/rustc_borrowck/src/region_infer/mod.rs
compiler/rustc_borrowck/src/region_infer/opaque_types.rs
compiler/rustc_borrowck/src/universal_regions.rs
compiler/rustc_builtin_macros/Cargo.toml
compiler/rustc_builtin_macros/src/assert/context.rs
compiler/rustc_builtin_macros/src/deriving/clone.rs
compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
compiler/rustc_builtin_macros/src/deriving/default.rs
compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
compiler/rustc_builtin_macros/src/global_allocator.rs
compiler/rustc_builtin_macros/src/lib.rs
compiler/rustc_builtin_macros/src/standard_library_imports.rs
compiler/rustc_builtin_macros/src/test.rs
compiler/rustc_builtin_macros/src/test_harness.rs
compiler/rustc_codegen_cranelift/src/abi/mod.rs
compiler/rustc_codegen_cranelift/src/constant.rs
compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
compiler/rustc_codegen_llvm/src/asm.rs
compiler/rustc_codegen_llvm/src/back/archive.rs
compiler/rustc_codegen_llvm/src/back/lto.rs
compiler/rustc_codegen_llvm/src/back/write.rs
compiler/rustc_codegen_llvm/src/builder.rs
compiler/rustc_codegen_llvm/src/callee.rs
compiler/rustc_codegen_llvm/src/common.rs
compiler/rustc_codegen_llvm/src/consts.rs
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
compiler/rustc_codegen_llvm/src/declare.rs
compiler/rustc_codegen_llvm/src/lib.rs
compiler/rustc_codegen_llvm/src/llvm_util.rs
compiler/rustc_codegen_llvm/src/mono_item.rs
compiler/rustc_codegen_llvm/src/type_of.rs
compiler/rustc_codegen_ssa/src/back/symbol_export.rs
compiler/rustc_codegen_ssa/src/mir/mod.rs
compiler/rustc_codegen_ssa/src/mir/place.rs
compiler/rustc_const_eval/src/const_eval/eval_queries.rs
compiler/rustc_const_eval/src/const_eval/valtrees.rs
compiler/rustc_const_eval/src/interpret/intern.rs
compiler/rustc_const_eval/src/interpret/intrinsics.rs
compiler/rustc_const_eval/src/interpret/memory.rs
compiler/rustc_const_eval/src/interpret/projection.rs
compiler/rustc_const_eval/src/interpret/step.rs
compiler/rustc_const_eval/src/interpret/terminator.rs
compiler/rustc_const_eval/src/interpret/traits.rs
compiler/rustc_data_structures/Cargo.toml
compiler/rustc_data_structures/src/fingerprint.rs
compiler/rustc_data_structures/src/lib.rs
compiler/rustc_data_structures/src/map_in_place.rs
compiler/rustc_data_structures/src/sorted_map.rs
compiler/rustc_data_structures/src/thin_vec.rs [deleted file]
compiler/rustc_data_structures/src/thin_vec/tests.rs [deleted file]
compiler/rustc_driver/Cargo.toml
compiler/rustc_driver/src/pretty.rs
compiler/rustc_driver/src/session_diagnostics.rs
compiler/rustc_error_messages/locales/en-US/driver.ftl
compiler/rustc_error_messages/locales/en-US/query_system.ftl [new file with mode: 0644]
compiler/rustc_error_messages/src/lib.rs
compiler/rustc_errors/src/diagnostic.rs
compiler/rustc_errors/src/diagnostic_builder.rs
compiler/rustc_errors/src/emitter.rs
compiler/rustc_errors/src/translation.rs
compiler/rustc_expand/src/lib.rs
compiler/rustc_expand/src/mbe/macro_parser.rs
compiler/rustc_expand/src/mbe/macro_rules.rs
compiler/rustc_expand/src/proc_macro_server.rs
compiler/rustc_hir/src/definitions.rs
compiler/rustc_hir/src/lib.rs
compiler/rustc_infer/src/infer/combine.rs
compiler/rustc_infer/src/infer/error_reporting/mod.rs
compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
compiler/rustc_infer/src/infer/higher_ranked/mod.rs
compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
compiler/rustc_infer/src/infer/mod.rs
compiler/rustc_infer/src/infer/nll_relate/mod.rs
compiler/rustc_infer/src/infer/opaque_types.rs
compiler/rustc_infer/src/infer/opaque_types/table.rs
compiler/rustc_infer/src/infer/outlives/mod.rs
compiler/rustc_infer/src/infer/outlives/obligations.rs
compiler/rustc_infer/src/infer/outlives/test_type_match.rs
compiler/rustc_infer/src/infer/undo_log.rs
compiler/rustc_interface/src/interface.rs
compiler/rustc_interface/src/lib.rs
compiler/rustc_interface/src/passes.rs
compiler/rustc_interface/src/queries.rs
compiler/rustc_interface/src/util.rs
compiler/rustc_lint/src/builtin.rs
compiler/rustc_lint/src/context.rs
compiler/rustc_lint/src/early.rs
compiler/rustc_lint/src/internal.rs
compiler/rustc_lint/src/late.rs
compiler/rustc_lint/src/levels.rs
compiler/rustc_lint/src/lib.rs
compiler/rustc_lint/src/types.rs
compiler/rustc_lint/src/unused.rs
compiler/rustc_lint_defs/src/builtin.rs
compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
compiler/rustc_metadata/src/creader.rs
compiler/rustc_metadata/src/dependency_format.rs
compiler/rustc_metadata/src/lib.rs
compiler/rustc_metadata/src/locator.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_metadata/src/rmeta/table.rs
compiler/rustc_middle/Cargo.toml
compiler/rustc_middle/src/mir/basic_blocks.rs
compiler/rustc_middle/src/mir/mod.rs
compiler/rustc_middle/src/mir/syntax.rs
compiler/rustc_middle/src/query/mod.rs
compiler/rustc_middle/src/traits/specialization_graph.rs
compiler/rustc_middle/src/ty/consts.rs
compiler/rustc_middle/src/ty/context.rs
compiler/rustc_middle/src/ty/fold.rs
compiler/rustc_middle/src/ty/generics.rs
compiler/rustc_middle/src/ty/layout.rs
compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
compiler/rustc_middle/src/ty/parameterized.rs
compiler/rustc_middle/src/ty/trait_def.rs
compiler/rustc_middle/src/ty/util.rs
compiler/rustc_middle/src/ty/visit.rs
compiler/rustc_mir_build/src/build/matches/mod.rs
compiler/rustc_mir_build/src/thir/cx/expr.rs
compiler/rustc_mir_build/src/thir/cx/mod.rs
compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
compiler/rustc_monomorphize/src/collector.rs
compiler/rustc_monomorphize/src/polymorphize.rs
compiler/rustc_monomorphize/src/util.rs
compiler/rustc_parse/src/lexer/mod.rs
compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
compiler/rustc_parse/src/parser/attr.rs
compiler/rustc_parse/src/parser/attr_wrapper.rs
compiler/rustc_parse/src/parser/diagnostics.rs
compiler/rustc_parse/src/parser/expr.rs
compiler/rustc_parse/src/parser/item.rs
compiler/rustc_parse/src/parser/mod.rs
compiler/rustc_parse/src/parser/path.rs
compiler/rustc_passes/src/hir_stats.rs
compiler/rustc_passes/src/liveness.rs
compiler/rustc_privacy/src/lib.rs
compiler/rustc_query_impl/Cargo.toml
compiler/rustc_query_impl/src/plumbing.rs
compiler/rustc_query_system/Cargo.toml
compiler/rustc_query_system/src/error.rs [new file with mode: 0644]
compiler/rustc_query_system/src/lib.rs
compiler/rustc_query_system/src/query/job.rs
compiler/rustc_query_system/src/query/mod.rs
compiler/rustc_query_system/src/query/plumbing.rs
compiler/rustc_resolve/src/access_levels.rs
compiler/rustc_resolve/src/build_reduced_graph.rs
compiler/rustc_resolve/src/def_collector.rs
compiler/rustc_resolve/src/diagnostics.rs
compiler/rustc_resolve/src/ident.rs
compiler/rustc_resolve/src/imports.rs
compiler/rustc_resolve/src/late.rs
compiler/rustc_resolve/src/late/diagnostics.rs
compiler/rustc_resolve/src/late/lifetimes.rs
compiler/rustc_resolve/src/lib.rs
compiler/rustc_resolve/src/macros.rs
compiler/rustc_save_analysis/src/dump_visitor.rs
compiler/rustc_save_analysis/src/lib.rs
compiler/rustc_serialize/Cargo.toml
compiler/rustc_serialize/src/collection_impls.rs
compiler/rustc_session/src/cgu_reuse_tracker.rs
compiler/rustc_session/src/config.rs
compiler/rustc_session/src/filesearch.rs
compiler/rustc_session/src/lib.rs
compiler/rustc_span/src/hygiene.rs
compiler/rustc_span/src/lib.rs
compiler/rustc_span/src/source_map.rs
compiler/rustc_symbol_mangling/src/legacy.rs
compiler/rustc_symbol_mangling/src/lib.rs
compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs
compiler/rustc_target/src/spec/android_base.rs
compiler/rustc_target/src/spec/apple_base.rs
compiler/rustc_target/src/spec/avr_gnu_base.rs
compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
compiler/rustc_target/src/spec/l4re_base.rs
compiler/rustc_target/src/spec/mod.rs
compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs
compiler/rustc_target/src/spec/tests/tests_impl.rs
compiler/rustc_target/src/spec/uefi_msvc_base.rs
compiler/rustc_target/src/spec/windows_gnullvm_base.rs
compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs
compiler/rustc_target/src/spec/x86_64_unknown_none.rs
compiler/rustc_trait_selection/src/traits/codegen.rs
compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
compiler/rustc_trait_selection/src/traits/fulfill.rs
compiler/rustc_trait_selection/src/traits/project.rs
compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
compiler/rustc_trait_selection/src/traits/select/mod.rs
compiler/rustc_trait_selection/src/traits/wf.rs
compiler/rustc_traits/src/chalk/lowering.rs
compiler/rustc_transmute/src/layout/tree.rs
compiler/rustc_transmute/src/maybe_transmutable/mod.rs
compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
compiler/rustc_ty_utils/src/instance.rs
compiler/rustc_ty_utils/src/ty.rs
compiler/rustc_typeck/src/astconv/mod.rs
compiler/rustc_typeck/src/check/_match.rs
compiler/rustc_typeck/src/check/check.rs
compiler/rustc_typeck/src/check/closure.rs
compiler/rustc_typeck/src/check/compare_method.rs
compiler/rustc_typeck/src/check/demand.rs
compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
compiler/rustc_typeck/src/check/generator_interior.rs
compiler/rustc_typeck/src/check/method/mod.rs
compiler/rustc_typeck/src/check/method/probe.rs
compiler/rustc_typeck/src/check/mod.rs
compiler/rustc_typeck/src/check/wfcheck.rs
compiler/rustc_typeck/src/check/writeback.rs
compiler/rustc_typeck/src/collect/type_of.rs
compiler/rustc_typeck/src/expr_use_visitor.rs
compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
config.toml.example
library/alloc/src/collections/linked_list.rs
library/alloc/src/vec/mod.rs
library/core/src/hint.rs
library/core/src/intrinsics.rs
library/core/src/macros/mod.rs
library/core/src/num/nonzero.rs
library/core/src/ptr/const_ptr.rs
library/core/src/ptr/mod.rs
library/core/src/ptr/mut_ptr.rs
library/core/src/slice/iter.rs
library/core/src/slice/mod.rs
library/core/src/time.rs
library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs
library/proc_macro/src/bridge/client.rs
library/proc_macro/src/bridge/mod.rs
library/proc_macro/src/bridge/server.rs
library/proc_macro/src/diagnostic.rs
library/std/src/io/stdio.rs
library/std/src/keyword_docs.rs
library/std/src/sys/unix/locks/fuchsia_mutex.rs
library/std/src/sys/unix/locks/futex_mutex.rs
library/std/src/sys/unix/locks/futex_rwlock.rs
library/std/src/sys/windows/cmath.rs
library/std/src/sys/windows/fs.rs
library/std/src/sys/windows/path/tests.rs
src/bootstrap/bootstrap.py
src/bootstrap/builder.rs
src/bootstrap/config.rs
src/bootstrap/lib.rs
src/bootstrap/native.rs
src/bootstrap/test.rs
src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile
src/ci/docker/host-x86_64/i686-gnu/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
src/doc/rustc/src/platform-support/armv4t-none-eabi.md [new file with mode: 0644]
src/doc/rustc/src/platform-support/armv4t_none_eabi.md [deleted file]
src/doc/rustc/src/platform-support/fuchsia.md
src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
src/doc/rustc/src/platform-support/pc-windows-gnullvm.md
src/doc/rustc/src/platform-support/unknown-uefi.md
src/doc/rustc/src/platform-support/wasm64-unknown-unknown.md
src/doc/unstable-book/src/compiler-flags/check-cfg.md
src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md
src/etc/cpu-usage-over-time-plot.sh
src/etc/htmldocck.py
src/librustdoc/Cargo.toml
src/librustdoc/clean/auto_trait.rs
src/librustdoc/clean/blanket_impl.rs
src/librustdoc/clean/inline.rs
src/librustdoc/clean/mod.rs
src/librustdoc/clean/types.rs
src/librustdoc/clean/utils.rs
src/librustdoc/html/format.rs
src/librustdoc/html/highlight.rs
src/librustdoc/html/render/mod.rs
src/librustdoc/html/static/css/rustdoc.css
src/librustdoc/passes/collect_intra_doc_links.rs
src/librustdoc/passes/propagate_doc_cfg.rs
src/librustdoc/passes/stripper.rs
src/test/codegen/abi-efiapi.rs
src/test/codegen/avr/avr-func-addrspace.rs
src/test/codegen/issue-34634.rs
src/test/codegen/unwind-abis/aapcs-unwind-abi.rs
src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs
src/test/codegen/unwind-abis/c-unwind-abi.rs
src/test/codegen/unwind-abis/cdecl-unwind-abi.rs
src/test/codegen/unwind-abis/fastcall-unwind-abi.rs
src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs
src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs
src/test/codegen/unwind-abis/nounwind.rs
src/test/codegen/unwind-abis/stdcall-unwind-abi.rs
src/test/codegen/unwind-abis/system-unwind-abi.rs
src/test/codegen/unwind-abis/sysv64-unwind-abi.rs
src/test/codegen/unwind-abis/thiscall-unwind-abi.rs
src/test/codegen/unwind-abis/vectorcall-unwind-abi.rs
src/test/codegen/unwind-abis/win64-unwind-abi.rs
src/test/codegen/unwind-extern-exports.rs
src/test/incremental/hygiene/load_cached_hygiene.rs
src/test/incremental/issue-49043.rs
src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs
src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs
src/test/mir-opt/dest-prop/union.rs
src/test/run-make-fulldeps/issue-64153/Makefile
src/test/run-make/coverage-reports/Makefile
src/test/run-make/coverage-reports/expected_show_coverage.async.txt
src/test/run-make/coverage/async.rs
src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh
src/test/rustdoc-gui/src/lib2/lib.rs
src/test/rustdoc-gui/type-declation-overflow.goml
src/test/rustdoc-gui/where-whitespace.goml [new file with mode: 0644]
src/test/rustdoc-ui/normalize-cycle.rs
src/test/rustdoc/const-generics/const-generics-docs.rs
src/test/rustdoc/doc_auto_cfg_nested_impl.rs [new file with mode: 0644]
src/test/rustdoc/elided-lifetime.rs
src/test/rustdoc/generic-associated-types/gats.rs
src/test/rustdoc/higher-ranked-trait-bounds.rs
src/test/rustdoc/impl-parts.rs
src/test/rustdoc/infinite-redirection.rs
src/test/rustdoc/issue-20727-4.rs
src/test/rustdoc/issue-21801.rs
src/test/rustdoc/issue-29503.rs
src/test/rustdoc/issue-34928.rs
src/test/rustdoc/issue-50159.rs
src/test/rustdoc/issue-51236.rs
src/test/rustdoc/issue-54705.rs
src/test/rustdoc/issue-98697.rs
src/test/rustdoc/macro-document-private-duplicate.rs
src/test/rustdoc/primitive-slice-auto-trait.rs
src/test/rustdoc/synthetic_auto/basic.rs
src/test/rustdoc/synthetic_auto/complex.rs
src/test/rustdoc/synthetic_auto/lifetimes.rs
src/test/rustdoc/synthetic_auto/manual.rs
src/test/rustdoc/synthetic_auto/nested.rs
src/test/rustdoc/synthetic_auto/no-redundancy.rs
src/test/rustdoc/synthetic_auto/project.rs
src/test/rustdoc/synthetic_auto/self-referential.rs
src/test/rustdoc/synthetic_auto/static-region.rs
src/test/rustdoc/where-clause-order.rs
src/test/rustdoc/where.rs
src/test/ui-fulldeps/internal-lints/diagnostics.rs
src/test/ui-fulldeps/internal-lints/diagnostics.stderr
src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
src/test/ui/allocator/no_std-alloc-error-handler-custom.rs
src/test/ui/allocator/no_std-alloc-error-handler-default.rs
src/test/ui/associated-type-bounds/elision.rs
src/test/ui/borrowck/do-not-suggest-adding-move-when-closure-is-already-marked-as-move.rs [new file with mode: 0644]
src/test/ui/borrowck/do-not-suggest-adding-move-when-closure-is-already-marked-as-move.stderr [new file with mode: 0644]
src/test/ui/borrowck/two-phase-nonrecv-autoref.rs
src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs
src/test/ui/check-cfg/invalid-arguments.names_simple_ident.stderr
src/test/ui/check-cfg/invalid-arguments.values_simple_ident.stderr
src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs
src/test/ui/closures/2229_closure_analysis/repr_packed.rs
src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs
src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs
src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs
src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs
src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
src/test/ui/closures/issue-84128.rs
src/test/ui/command/command-current-dir.rs
src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr
src/test/ui/const-generics/issues/issue-83466.rs
src/test/ui/consts/const-eval/ub-enum.rs
src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs
src/test/ui/issues/issue-23611-enum-swap-in-drop.rs
src/test/ui/iterators/issue-58952-filter-type-length.rs
src/test/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs
src/test/ui/modules/auxiliary/dummy_lib.rs [new file with mode: 0644]
src/test/ui/modules/special_module_name.rs [new file with mode: 0644]
src/test/ui/modules/special_module_name.stderr [new file with mode: 0644]
src/test/ui/modules/special_module_name_ignore.rs [new file with mode: 0644]
src/test/ui/never_type/fallback-closure-wrap.rs
src/test/ui/nll/issue-21232-partial-init-and-use.rs
src/test/ui/nll/polonius/assignment-kills-loans.rs
src/test/ui/pattern/rest-pat-semantic-disallowed.rs
src/test/ui/proc-macro/crt-static.rs
src/test/ui/regions/issue-56537-closure-uses-region-from-container.rs
src/test/ui/resolve/issue-73427.rs
src/test/ui/resolve/issue-73427.stderr
src/test/ui/resolve/privacy-enum-ctor.stderr
src/test/ui/specialization/issue-33017.rs
src/test/ui/suggestions/issue-89064.rs [new file with mode: 0644]
src/test/ui/suggestions/issue-89064.stderr [new file with mode: 0644]
src/test/ui/suggestions/suggest-blanket-impl-local-trait.rs
src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.rs
src/test/ui/type/type-alias-bounds.rs
src/test/ui/unpretty/avoid-crash.rs [new file with mode: 0644]
src/test/ui/unpretty/avoid-crash.stderr [new file with mode: 0644]
src/test/ui/variance/variance-use-contravariant-struct-1.rs
src/test/ui/variance/variance-use-contravariant-struct-2.rs
src/test/ui/variance/variance-use-invariant-struct-1.rs
src/tools/clippy/.github/workflows/clippy.yml
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/clippy_dev/src/bless.rs
src/tools/clippy/clippy_dev/src/fmt.rs
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/src/as_underscore.rs [deleted file]
src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs [deleted file]
src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
src/tools/clippy/clippy_lints/src/bytecount.rs [deleted file]
src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs [deleted file]
src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs [deleted file]
src/tools/clippy/clippy_lints/src/casts/as_underscore.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs
src/tools/clippy/clippy_lints/src/duplicate_mod.rs
src/tools/clippy/clippy_lints/src/escape.rs
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/format_args.rs
src/tools/clippy/clippy_lints/src/format_impl.rs
src/tools/clippy/clippy_lints/src/functions/mod.rs
src/tools/clippy/clippy_lints/src/functions/result.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs [deleted file]
src/tools/clippy/clippy_lints/src/get_first.rs [deleted file]
src/tools/clippy/clippy_lints/src/if_let_mutex.rs
src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
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_correctness.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/loops/needless_collect.rs
src/tools/clippy/clippy_lints/src/manual_async_fn.rs
src/tools/clippy/clippy_lints/src/manual_ok_or.rs [deleted file]
src/tools/clippy/clippy_lints/src/manual_string_new.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/map_clone.rs [deleted file]
src/tools/clippy/clippy_lints/src/map_err_ignore.rs [deleted file]
src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
src/tools/clippy/clippy_lints/src/matches/needless_match.rs
src/tools/clippy/clippy_lints/src/methods/bytecount.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/expect_used.rs
src/tools/clippy/clippy_lints/src/methods/get_first.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/map_clone.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/open_options.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/repeat_once.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
src/tools/clippy/clippy_lints/src/methods/unit_hash.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs
src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs
src/tools/clippy/clippy_lints/src/multi_assignments.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs [deleted file]
src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
src/tools/clippy/clippy_lints/src/open_options.rs [deleted file]
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs [deleted file]
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
src/tools/clippy/clippy_lints/src/redundant_slicing.rs
src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
src/tools/clippy/clippy_lints/src/repeat_once.rs [deleted file]
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/self_named_constructors.rs
src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs [deleted file]
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/transmute/mod.rs
src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/transmuting_null.rs [deleted file]
src/tools/clippy/clippy_lints/src/unicode.rs
src/tools/clippy/clippy_lints/src/uninit_vec.rs
src/tools/clippy/clippy_lints/src/unit_hash.rs [deleted file]
src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs [deleted file]
src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
src/tools/clippy/clippy_lints/src/unused_peekable.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/unused_rounding.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
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/vec_resize_to_zero.rs [deleted file]
src/tools/clippy/clippy_lints/src/verbose_file_reads.rs [deleted file]
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/macros.rs
src/tools/clippy/clippy_utils/src/msrvs.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/rustc_tools_util/src/lib.rs
src/tools/clippy/tests/check-fmt.rs
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/integration.rs
src/tools/clippy/tests/lint_message_convention.rs
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs
src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr
src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/collapsible_str_replace.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/collapsible_str_replace.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/collapsible_str_replace.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/expect.rs
src/tools/clippy/tests/ui/expect.stderr
src/tools/clippy/tests/ui/floating_point_exp.fixed
src/tools/clippy/tests/ui/floating_point_exp.rs
src/tools/clippy/tests/ui/floating_point_exp.stderr
src/tools/clippy/tests/ui/floating_point_log.fixed
src/tools/clippy/tests/ui/floating_point_log.rs
src/tools/clippy/tests/ui/floating_point_log.stderr
src/tools/clippy/tests/ui/floating_point_logbase.fixed
src/tools/clippy/tests/ui/floating_point_logbase.rs
src/tools/clippy/tests/ui/floating_point_logbase.stderr
src/tools/clippy/tests/ui/floating_point_powf.fixed
src/tools/clippy/tests/ui/floating_point_powf.rs
src/tools/clippy/tests/ui/floating_point_powf.stderr
src/tools/clippy/tests/ui/floating_point_powi.fixed
src/tools/clippy/tests/ui/floating_point_powi.rs
src/tools/clippy/tests/ui/floating_point_powi.stderr
src/tools/clippy/tests/ui/floating_point_rad.fixed
src/tools/clippy/tests/ui/floating_point_rad.rs
src/tools/clippy/tests/ui/floating_point_rad.stderr
src/tools/clippy/tests/ui/format.fixed
src/tools/clippy/tests/ui/format.rs
src/tools/clippy/tests/ui/format_args.fixed
src/tools/clippy/tests/ui/format_args.rs
src/tools/clippy/tests/ui/format_args.stderr
src/tools/clippy/tests/ui/identity_op.fixed
src/tools/clippy/tests/ui/identity_op.rs
src/tools/clippy/tests/ui/if_let_mutex.rs
src/tools/clippy/tests/ui/if_let_mutex.stderr
src/tools/clippy/tests/ui/if_then_some_else_none.stderr
src/tools/clippy/tests/ui/indexing_slicing_index.rs
src/tools/clippy/tests/ui/iter_on_empty_collections.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_on_empty_collections.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_on_empty_collections.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_on_single_items.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_on_single_items.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_on_single_items.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_string_new.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_string_new.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_string_new.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
src/tools/clippy/tests/ui/multi_assignments.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/multi_assignments.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_borrow.stderr
src/tools/clippy/tests/ui/needless_collect_indirect.rs
src/tools/clippy/tests/ui/needless_collect_indirect.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_return.fixed
src/tools/clippy/tests/ui/needless_return.rs
src/tools/clippy/tests/ui/only_used_in_recursion.rs
src/tools/clippy/tests/ui/only_used_in_recursion.stderr
src/tools/clippy/tests/ui/only_used_in_recursion2.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/only_used_in_recursion2.stderr [new file with mode: 0644]
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/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.rs
src/tools/clippy/tests/ui/or_fun_call.stderr
src/tools/clippy/tests/ui/partialeq_to_none.fixed
src/tools/clippy/tests/ui/partialeq_to_none.rs
src/tools/clippy/tests/ui/partialeq_to_none.stderr
src/tools/clippy/tests/ui/positional_named_format_parameters.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/positional_named_format_parameters.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/positional_named_format_parameters.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/question_mark.fixed
src/tools/clippy/tests/ui/question_mark.rs
src/tools/clippy/tests/ui/regex.rs
src/tools/clippy/tests/ui/result_large_err.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/result_large_err.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/same_item_push.rs
src/tools/clippy/tests/ui/string_add.rs
src/tools/clippy/tests/ui/string_add_assign.fixed
src/tools/clippy/tests/ui/string_add_assign.rs
src/tools/clippy/tests/ui/suspicious_to_owned.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/suspicious_to_owned.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed [new file with mode: 0644]
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/trait_duplication_in_bounds_unfixable.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/transmute_undefined_repr.rs
src/tools/clippy/tests/ui/transmute_undefined_repr.stderr
src/tools/clippy/tests/ui/unicode.fixed
src/tools/clippy/tests/ui/unicode.rs
src/tools/clippy/tests/ui/unicode.stderr
src/tools/clippy/tests/ui/unnecessary_cast.fixed
src/tools/clippy/tests/ui/unnecessary_cast.rs
src/tools/clippy/tests/ui/unnecessary_cast.stderr
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr
src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
src/tools/clippy/tests/ui/unnecessary_to_owned.rs
src/tools/clippy/tests/ui/unused_peekable.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/unused_peekable.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/unwrap.rs
src/tools/clippy/tests/ui/unwrap.stderr
src/tools/clippy/tests/ui/unwrap_expect_used.rs
src/tools/clippy/tests/ui/unwrap_expect_used.stderr
src/tools/clippy/tests/ui/useless_conversion_try.rs
src/tools/clippy/tests/ui/useless_conversion_try.stderr
src/tools/clippy/tests/ui/vec_resize_to_zero.rs
src/tools/clippy/tests/ui/vec_resize_to_zero.stderr
src/tools/clippy/tests/ui/verbose_file_reads.rs
src/tools/clippy/tests/workspace.rs
src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs
src/tools/tidy/src/deps.rs

index b16fb6341c2e50a2f37f04e563017a588365a4d7..37972532b361429f281f8d52f34ea4d1ca621540 100644 (file)
@@ -46,6 +46,7 @@ no_llvm_build
 /dist/
 /unicode-downloads
 /target
+/src/bootstrap/target
 /src/tools/x/target
 # Created by default with `src/ci/docker/run.sh`
 /obj/
index 9c9848a8563c9a5ec80a000bb641d655dddc83d3..8ae166de1c58c7f898f9d836236121ba185e559f 100644 (file)
@@ -727,6 +727,7 @@ version = "0.1.65"
 dependencies = [
  "arrayvec",
  "if_chain",
+ "itertools",
  "rustc-semver",
 ]
 
@@ -3177,6 +3178,7 @@ dependencies = [
  "rustc_serialize",
  "rustc_span",
  "smallvec",
+ "thin-vec",
  "tracing",
 ]
 
@@ -3198,6 +3200,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "smallvec",
+ "thin-vec",
  "tracing",
 ]
 
@@ -3292,6 +3295,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "smallvec",
+ "thin-vec",
  "tracing",
 ]
 
@@ -3416,6 +3420,7 @@ dependencies = [
  "stable_deref_trait",
  "stacker",
  "tempfile",
+ "thin-vec",
  "tracing",
  "winapi",
 ]
@@ -3798,6 +3803,7 @@ dependencies = [
  "rustc_target",
  "rustc_type_ir",
  "smallvec",
+ "thin-vec",
  "tracing",
 ]
 
@@ -3987,6 +3993,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
+ "thin-vec",
  "tracing",
 ]
 
@@ -4009,6 +4016,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "smallvec",
+ "thin-vec",
  "tracing",
 ]
 
@@ -4064,6 +4072,7 @@ dependencies = [
  "indexmap",
  "rustc_macros",
  "smallvec",
+ "thin-vec",
 ]
 
 [[package]]
@@ -4300,6 +4309,7 @@ dependencies = [
  "serde_json",
  "smallvec",
  "tempfile",
+ "thin-vec",
  "tracing",
  "tracing-subscriber",
  "tracing-tree",
@@ -4853,6 +4863,12 @@ version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
 
+[[package]]
+name = "thin-vec"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "104c2cb3180b6fb6d5b2278768e9b88b578d32ba751ea6e8d026688a40d7ed87"
+
 [[package]]
 name = "thiserror"
 version = "1.0.30"
index 147ff3561a30bbac29174727bfb3b5a189e05b6b..72b2c16a01f83b63ce9d1f448ab652c21aeb5450 100644 (file)
@@ -1442,7 +1442,7 @@ Compatibility Notes
 - [Mixing Option and Result via `?` is no longer permitted in closures for inferred types.][86831]
 - [Previously unsound code is no longer permitted where different constructors in branches
   could require different lifetimes.][85574]
-- As previously mentioned the [`std::arch` instrinsics now uses stricter const checking][83278]
+- As previously mentioned the [`std::arch` intrinsics now uses stricter const checking][83278]
   than before and may reject some previously accepted code.
 - [`i128` multiplication on Cortex M0+ platforms currently unconditionally causes overflow
    when compiled with `codegen-units = 1`.][86063]
@@ -2520,7 +2520,7 @@ Compatibility Notes
 - [Fixed a regression parsing `{} && false` in tail expressions.][74650]
 - [Added changes to how proc-macros are expanded in `macro_rules!` that should
   help to preserve more span information.][73084] These changes may cause
-  compiliation errors if your macro was unhygenic or didn't correctly handle
+  compilation errors if your macro was unhygenic or didn't correctly handle
   `Delimiter::None`.
 - [Moved support for the CloudABI target to tier 3.][75568]
 - [`linux-gnu` targets now require minimum kernel 2.6.32 and glibc 2.11.][74163]
index 9822e9864e2103774c7cf92baa8bf5d05ffad23f..c24180bacfc1b05207ebfbc0b7ccbfd06a558678 100644 (file)
@@ -7,12 +7,13 @@ edition = "2021"
 doctest = false
 
 [dependencies]
-rustc_serialize = { path = "../rustc_serialize" }
-tracing = "0.1"
-rustc_span = { path = "../rustc_span" }
+bitflags = "1.2.1"
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_index = { path = "../rustc_index" }
 rustc_lexer = { path = "../rustc_lexer" }
 rustc_macros = { path = "../rustc_macros" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_span = { path = "../rustc_span" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-bitflags = "1.2.1"
+thin-vec = "0.2.8"
+tracing = "0.1"
index fb521073a428d2802bfe0fe994ecd16edce9c8e3..3af6dee9e43f7fb888e6d5f5a95c1babc17b33a4 100644 (file)
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Delimiter};
 use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream};
-
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::Lrc;
-use rustc_data_structures::thin_vec::ThinVec;
 use rustc_macros::HashStable_Generic;
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_span::source_map::{respan, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
-
 use std::cmp::Ordering;
 use std::convert::TryFrom;
 use std::fmt;
 use std::mem;
+use thin_vec::ThinVec;
 
 /// A "Label" is an identifier of some point in sources,
 /// e.g. in the following code:
index 0947a71b8243261a7e7efad25f1599b7a92fb943..79f5820230ed67b597111d7f7df406c10122be91 100644 (file)
@@ -279,6 +279,7 @@ macro_rules! impl_has_attrs {
             impl HasAttrs for $T {
                 const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner;
 
+                #[inline]
                 fn attrs(&self) -> &[Attribute] {
                     &self.attrs
                 }
index 27061f300a2b20191e5906ed47ac7a683cfd8aa8..e5435e3a3d4fad4037927e7eaae246f1d8652f7e 100644 (file)
@@ -26,6 +26,9 @@
 #[macro_use]
 extern crate rustc_macros;
 
+#[macro_use]
+extern crate tracing;
+
 pub mod util {
     pub mod classify;
     pub mod comments;
index 9e4a22e1fa3cd19551ddeda23c2609dc172f4953..b6684c0669b4cd87737155d3004697b4a2704b1d 100644 (file)
@@ -426,7 +426,8 @@ fn opt_from_ast(node: &(impl HasAttrs + HasTokens)) -> Option<TokenStream> {
         let attr_annotated = if attrs.is_empty() {
             tokens.create_token_stream()
         } else {
-            let attr_data = AttributesData { attrs: attrs.to_vec().into(), tokens: tokens.clone() };
+            let attr_data =
+                AttributesData { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() };
             AttrAnnotatedTokenStream::new(vec![(
                 AttrAnnotatedTokenTree::Attributes(attr_data),
                 Spacing::Alone,
@@ -555,7 +556,7 @@ pub fn build(self) -> TokenStream {
 
                 // Get the first stream, which will become the result stream.
                 // If it's `None`, create an empty stream.
-                let mut iter = streams.drain(..);
+                let mut iter = streams.into_iter();
                 let mut res_stream_lrc = iter.next().unwrap().0;
 
                 // Append the subsequent elements to the result stream, after
index 69a78d165ef5895ad654b51103bd6efa21f5d016..6a1578498e6894468552c4729ecb0cf2c4b9531e 100644 (file)
@@ -9,7 +9,6 @@
 use rustc_span::Span;
 
 use std::ascii;
-use tracing::debug;
 
 pub enum LitError {
     NotLiteral,
index 474aff2e2aac06fabc947ac8c733fa227c732e65..ce1c8d4997d745b3c4760649db7786ef48bfccef 100644 (file)
@@ -8,17 +8,18 @@ doctest = false
 
 [dependencies]
 rustc_arena = { path = "../rustc_arena" }
-tracing = "0.1"
+rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_hir = { path = "../rustc_hir" }
-rustc_target = { path = "../rustc_target" }
 rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_query_system = { path = "../rustc_query_system" }
-rustc_span = { path = "../rustc_span" }
-rustc_errors = { path = "../rustc_errors" }
 rustc_session = { path = "../rustc_session" }
-rustc_ast = { path = "../rustc_ast" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
+tracing = "0.1"
index 176047616881f4a62da9610497adfe3f2b13832b..7df3520422c18b38cb14cb1ba979eb0024d487c3 100644 (file)
@@ -7,7 +7,6 @@
 use super::ResolverAstLoweringExt;
 use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
 use crate::{FnDeclKind, ImplTraitPosition};
-
 use rustc_ast::attr;
 use rustc_ast::ptr::P as AstP;
 use rustc_ast::*;
@@ -18,6 +17,7 @@
 use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::DUMMY_SP;
+use thin_vec::thin_vec;
 
 impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
@@ -1541,7 +1541,7 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind<'hir>
             };
             attr::mk_attr_outer(allow)
         };
-        let attrs: AttrVec = vec![attr].into();
+        let attrs: AttrVec = thin_vec![attr];
 
         // `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
         let continue_arm = {
index 2b7431f0990570a004902099db789a36f65cd989..219e1b81d1ea64d3226d9eb93787b1ed2d98fe4d 100644 (file)
@@ -11,8 +11,6 @@
 use rustc_span::source_map::SourceMap;
 use rustc_span::{Span, DUMMY_SP};
 
-use tracing::debug;
-
 /// A visitor that walks over the HIR and collects `Node`s into a HIR map.
 pub(super) struct NodeCollector<'a, 'hir> {
     /// Source map
@@ -31,7 +29,7 @@ pub(super) struct NodeCollector<'a, 'hir> {
     definitions: &'a definitions::Definitions,
 }
 
-#[tracing::instrument(level = "debug", skip(sess, definitions, bodies))]
+#[instrument(level = "debug", skip(sess, definitions, bodies))]
 pub(super) fn index_hir<'hir>(
     sess: &Session,
     definitions: &definitions::Definitions,
@@ -67,7 +65,7 @@ pub(super) fn index_hir<'hir>(
 }
 
 impl<'a, 'hir> NodeCollector<'a, 'hir> {
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) {
         debug_assert_eq!(self.owner, hir_id.owner);
         debug_assert_ne!(hir_id.local_id.as_u32(), 0);
@@ -142,7 +140,7 @@ fn visit_param(&mut self, param: &'hir Param<'hir>) {
         });
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn visit_item(&mut self, i: &'hir Item<'hir>) {
         debug_assert_eq!(i.def_id, self.owner);
         self.with_parent(i.hir_id(), |this| {
@@ -156,7 +154,7 @@ fn visit_item(&mut self, i: &'hir Item<'hir>) {
         });
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) {
         debug_assert_eq!(fi.def_id, self.owner);
         self.with_parent(fi.hir_id(), |this| {
@@ -175,7 +173,7 @@ fn visit_const_param_default(&mut self, param: HirId, ct: &'hir AnonConst) {
         })
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
         debug_assert_eq!(ti.def_id, self.owner);
         self.with_parent(ti.hir_id(), |this| {
@@ -183,7 +181,7 @@ fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
         });
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) {
         debug_assert_eq!(ii.def_id, self.owner);
         self.with_parent(ii.hir_id(), |this| {
index 30a87740c7f9b16c56f2d0261b61cdf761448d14..9a960356a85f4066f628ea59a101e29c4b6dc306 100644 (file)
@@ -220,7 +220,7 @@ fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind {
     /// Panics if no map has been pushed.
     /// Remapping is used when creating lowering `-> impl Trait` return
     /// types to create the resulting opaque type.
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId) {
         self.generics_def_id_map.last_mut().expect("no map pushed").insert(from, to);
     }
@@ -771,7 +771,7 @@ fn lower_ident(&self, ident: Ident) -> Ident {
     }
 
     /// Converts a lifetime into a new generic parameter.
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn lifetime_res_to_generic_param(
         &mut self,
         ident: Ident,
@@ -815,7 +815,7 @@ fn lifetime_res_to_generic_param(
     /// name resolver owing to lifetime elision; this also populates the resolver's node-id->def-id
     /// map, so that later calls to `opt_node_id_to_def_id` that refer to these extra lifetime
     /// parameters will be successful.
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     #[inline]
     fn lower_lifetime_binder(
         &mut self,
@@ -1179,7 +1179,7 @@ fn lower_path_ty(
     ) -> hir::Ty<'hir> {
         // Check whether we should interpret this as a bare trait object.
         // This check mirrors the one in late resolution.  We only introduce this special case in
-        // the rare occurence we need to lower `Fresh` anonymous lifetimes.
+        // the rare occurrence we need to lower `Fresh` anonymous lifetimes.
         // The other cases when a qpath should be opportunistically made a trait object are handled
         // by `ty_path`.
         if qself.is_none()
@@ -1385,7 +1385,7 @@ fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir>
     /// added explicitly in the HIR). But this includes all the lifetimes, and we only want to
     /// capture the lifetimes that are referenced in the bounds. Therefore, we add *extra* lifetime parameters
     /// for the lifetimes that get captured (`'x`, in our example above) and reference those.
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn lower_opaque_impl_trait(
         &mut self,
         span: Span,
@@ -1621,7 +1621,7 @@ fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
     // `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future<Output = T>` in the
     //      return type. This is used for `async fn` declarations. The `NodeId` is the ID of the
     //      return type `impl Trait` item.
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn lower_fn_decl(
         &mut self,
         decl: &FnDecl,
@@ -1730,7 +1730,7 @@ fn lower_fn_decl(
     // `output`: unlowered output type (`T` in `-> T`)
     // `fn_def_id`: `DefId` of the parent function (used to create child impl trait definition)
     // `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn lower_async_fn_ret_ty(
         &mut self,
         output: &FnRetTy,
@@ -2013,7 +2013,7 @@ fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
         self.new_named_lifetime(l.id, l.id, span, ident)
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn new_named_lifetime_with_res(
         &mut self,
         id: NodeId,
@@ -2044,7 +2044,7 @@ fn new_named_lifetime_with_res(
         hir::Lifetime { hir_id: self.lower_node_id(id), span: self.lower_span(span), name }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn new_named_lifetime(
         &mut self,
         id: NodeId,
@@ -2132,7 +2132,7 @@ fn lower_trait_ref(&mut self, p: &TraitRef, itctx: ImplTraitContext) -> hir::Tra
         hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn lower_poly_trait_ref(
         &mut self,
         p: &PolyTraitRef,
index 5874d08a94fe06bac55a516e8abef538f386aa58..897c7215805e0753e784601eb16518b94e9556bc 100644 (file)
@@ -13,7 +13,6 @@
 use rustc_span::{BytePos, Span, DUMMY_SP};
 
 use smallvec::smallvec;
-use tracing::debug;
 
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
     #[instrument(level = "trace", skip(self))]
index c36c4ad54da4878c4c8b25ebb01d7196bd2fe2c2..23464bf0a5a283e50039bf38ba77f2e9e741d673 100644 (file)
@@ -1800,7 +1800,7 @@ pub(crate) enum ForbiddenLetReason {
     NotSupportedOr(Span),
     /// A let chain with invalid parentheses
     ///
-    /// For exemple, `let 1 = 1 && (expr && expr)` is allowed
+    /// For example, `let 1 = 1 && (expr && expr)` is allowed
     /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
     NotSupportedParentheses(Span),
 }
index 6f7e88eb86f195e074a38cab814758c0f57d54d0..ca5b7a64155157f80c250cd9e8c8e07b9897ebbe 100644 (file)
@@ -11,8 +11,6 @@
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 
-use tracing::debug;
-
 macro_rules! gate_feature_fn {
     ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
         let (visitor, has_feature, span, name, explain, help) =
index f282ff251bda181ca8c7f97c961382c1cd1c1b95..af25982e2887bb33656b99017083f8c460d7a619 100644 (file)
@@ -12,6 +12,9 @@
 #![feature(let_else)]
 #![recursion_limit = "256"]
 
+#[macro_use]
+extern crate tracing;
+
 pub mod ast_validation;
 mod errors;
 pub mod feature_gate;
index d359d7efb626801905958a91c983f866105a1fce..35c3df768995a0d2907db1cb174122666596a9cc 100644 (file)
@@ -6,7 +6,6 @@
 use rustc_middle::ty::RegionVid;
 use smallvec::SmallVec;
 use std::collections::BTreeMap;
-use tracing::debug;
 
 use crate::MirBorrowckCtxt;
 
index 00fdf331ca60ce8c270a3ce98ec6a41d35baafa6..082dd86902354843822df54b8cee1b6dd56b63a3 100644 (file)
@@ -902,7 +902,11 @@ fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
             hir::ExprKind::MethodCall(.., args, _) => {
                 // only the first closre parameter of the method. args[0] is MethodCall PathSegment
                 for i in 1..args.len() {
-                    if let hir::ExprKind::Closure(..) = args[i].kind {
+                    if let hir::ExprKind::Closure(hir::Closure {
+                        capture_clause: hir::CaptureBy::Ref,
+                        ..
+                    }) = args[i].kind
+                    {
                         closure_span = Some(args[i].span.shrink_to_lo());
                         break;
                     }
@@ -911,7 +915,11 @@ fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
             hir::ExprKind::Block(blk, _) => {
                 if let Some(ref expr) = blk.expr {
                     // only when the block is a closure
-                    if let hir::ExprKind::Closure(..) = expr.kind {
+                    if let hir::ExprKind::Closure(hir::Closure {
+                        capture_clause: hir::CaptureBy::Ref,
+                        ..
+                    }) = expr.kind
+                    {
                         closure_span = Some(expr.span.shrink_to_lo());
                     }
                 }
@@ -921,7 +929,7 @@ fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
         if let Some(closure_span) = closure_span {
             diag.span_suggestion_verbose(
                 closure_span,
-                format!("consider adding 'move' keyword before the nested closure"),
+                "consider adding 'move' keyword before the nested closure",
                 "move ",
                 Applicability::MaybeIncorrect,
             );
index a87e8bd5ba16fc276183bc1366a121107141dd94..75fde53b6cdecd24452e0293db2118ad598f9620 100644 (file)
@@ -265,7 +265,7 @@ pub(crate) fn give_region_a_name(&self, fr: RegionVid) -> Option<RegionName> {
     /// *user* has a name for. In that case, we'll be able to map
     /// `fr` to a `Region<'tcx>`, and that region will be one of
     /// named variants.
-    #[tracing::instrument(level = "trace", skip(self))]
+    #[instrument(level = "trace", skip(self))]
     fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> {
         let error_region = self.to_error_region(fr)?;
 
@@ -373,7 +373,7 @@ fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> {
     ///  | fn foo(x: &u32) { .. }
     ///           ------- fully elaborated type of `x` is `&'1 u32`
     /// ```
-    #[tracing::instrument(level = "trace", skip(self))]
+    #[instrument(level = "trace", skip(self))]
     fn give_name_if_anonymous_region_appears_in_arguments(
         &self,
         fr: RegionVid,
@@ -662,7 +662,7 @@ fn try_match_adt_and_generic_args<'hir>(
     ///  | let x = Some(&22);
     ///        - fully elaborated type of `x` is `Option<&'1 u32>`
     /// ```
-    #[tracing::instrument(level = "trace", skip(self))]
+    #[instrument(level = "trace", skip(self))]
     fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option<RegionName> {
         let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?;
         let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region(
@@ -682,7 +682,7 @@ fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Opti
     /// must be a closure since, in a free fn, such an argument would
     /// have to either also appear in an argument (if using elision)
     /// or be early bound (named, not in argument).
-    #[tracing::instrument(level = "trace", skip(self))]
+    #[instrument(level = "trace", skip(self))]
     fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
         let tcx = self.infcx.tcx;
         let hir = tcx.hir();
@@ -814,7 +814,7 @@ fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::
         }
     }
 
-    #[tracing::instrument(level = "trace", skip(self))]
+    #[instrument(level = "trace", skip(self))]
     fn give_name_if_anonymous_region_appears_in_yield_ty(
         &self,
         fr: RegionVid,
index f5bd5cd3beaed0ab0006c2ba688a5fc8009f743a..8dc9368a0b994d197943ea914fa19b6578b554dc 100644 (file)
@@ -1139,7 +1139,7 @@ fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
     ///   include the CFG anyhow.
     /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding
     ///   a result `'y`.
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self), level = "debug", ret)]
     pub(crate) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
         debug!(r = %self.region_value_str(r));
 
@@ -1151,8 +1151,6 @@ pub(crate) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
             lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
         }
 
-        debug!(?lub);
-
         lub
     }
 
@@ -1333,15 +1331,15 @@ fn eval_equal(&self, r1: RegionVid, r2: RegionVid) -> bool {
     }
 
     // Evaluate whether `sup_region: sub_region`.
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self), level = "debug", ret)]
     fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
         debug!(
-            "eval_outlives: sup_region's value = {:?} universal={:?}",
+            "sup_region's value = {:?} universal={:?}",
             self.region_value_str(sup_region),
             self.universal_regions.is_universal_region(sup_region),
         );
         debug!(
-            "eval_outlives: sub_region's value = {:?} universal={:?}",
+            "sub_region's value = {:?} universal={:?}",
             self.region_value_str(sub_region),
             self.universal_regions.is_universal_region(sub_region),
         );
@@ -1354,7 +1352,7 @@ fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
         // true if `'sup` outlives static.
         if !self.universe_compatible(sub_region_scc, sup_region_scc) {
             debug!(
-                "eval_outlives: sub universe `{sub_region_scc:?}` is not nameable \
+                "sub universe `{sub_region_scc:?}` is not nameable \
                 by super `{sup_region_scc:?}`, promoting to static",
             );
 
@@ -1375,9 +1373,7 @@ fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
             });
 
         if !universal_outlives {
-            debug!(
-                "eval_outlives: returning false because sub region contains a universal region not present in super"
-            );
+            debug!("sub region contains a universal region not present in super");
             return false;
         }
 
@@ -1386,15 +1382,13 @@ fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
 
         if self.universal_regions.is_universal_region(sup_region) {
             // Micro-opt: universal regions contain all points.
-            debug!(
-                "eval_outlives: returning true because super is universal and hence contains all points"
-            );
+            debug!("super is universal and hence contains all points");
             return true;
         }
 
-        let result = self.scc_values.contains_points(sup_region_scc, sub_region_scc);
-        debug!("returning {} because of comparison between points in sup/sub", result);
-        result
+        debug!("comparison between points in sup/sub");
+
+        self.scc_values.contains_points(sup_region_scc, sub_region_scc)
     }
 
     /// Once regions have been propagated, this method is used to see
@@ -1971,7 +1965,7 @@ pub(crate) fn find_constraint_paths_between_regions(
     }
 
     /// Finds some region R such that `fr1: R` and `R` is live at `elem`.
-    #[instrument(skip(self), level = "trace")]
+    #[instrument(skip(self), level = "trace", ret)]
     pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
         trace!(scc = ?self.constraint_sccs.scc(fr1));
         trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]);
index 127cb4e408372e800e9ef7380f04c8ee59cf9e8d..0392367288c40544cad175913f68626a3ca9684d 100644 (file)
@@ -60,7 +60,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
     /// which has no `external_name` in which case we use `'empty` as the
     /// region to pass to `infer_opaque_definition_from_instantiation`.
-    #[instrument(level = "debug", skip(self, infcx))]
+    #[instrument(level = "debug", skip(self, infcx), ret)]
     pub(crate) fn infer_opaque_types(
         &self,
         infcx: &InferCtxt<'_, 'tcx>,
index 2a7713bc4df3b6d1e8be939ef07799a5c412f820..b9b181681ec4b7495cf63f925799d205cdaff281 100644 (file)
@@ -768,10 +768,9 @@ fn replace_late_bound_regions_with_nll_infer_vars(
         mir_def_id: LocalDefId,
         indices: &mut UniversalRegionIndices<'tcx>,
     ) {
-        debug!("replace_late_bound_regions_with_nll_infer_vars(mir_def_id={:?})", mir_def_id);
         let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id());
         for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| {
-            debug!("replace_late_bound_regions_with_nll_infer_vars: r={:?}", r);
+            debug!(?r);
             if !indices.indices.contains_key(&r) {
                 let region_vid = self.next_nll_region_var(FR);
                 debug!(?region_vid);
index 8d8e9d9b5ff4e03a5b8496f36b406a1a7746da3b..6469d0d7b88a68440c2b27735de6aede13a93f39 100644 (file)
@@ -7,20 +7,21 @@ edition = "2021"
 doctest = false
 
 [dependencies]
-rustc_parse_format = { path = "../rustc_parse_format" }
-tracing = "0.1"
+rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
+rustc_expand = { path = "../rustc_expand" }
 rustc_feature = { path = "../rustc_feature" }
 rustc_lexer = { path = "../rustc_lexer" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
 rustc_macros = { path = "../rustc_macros" }
+rustc_parse_format = { path = "../rustc_parse_format" }
 rustc_parse = { path = "../rustc_parse" }
-rustc_target = { path = "../rustc_target" }
 rustc_session = { path = "../rustc_session" }
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-rustc_ast = { path = "../rustc_ast" }
-rustc_expand = { path = "../rustc_expand" }
 rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
+tracing = "0.1"
index d2ee4249989ee396a8195ae9030b70be35b55564..973a8cb85c2b9c12b652f8f60e81cda603377550 100644 (file)
@@ -13,6 +13,7 @@
     symbol::{sym, Ident, Symbol},
     Span,
 };
+use thin_vec::thin_vec;
 
 pub(super) struct Context<'cx, 'a> {
     // An optimization.
@@ -116,11 +117,10 @@ fn build_initial_imports(&self) -> Stmt {
             self.cx.item(
                 self.span,
                 Ident::empty(),
-                vec![self.cx.attribute(attr::mk_list_item(
+                thin_vec![self.cx.attribute(attr::mk_list_item(
                     Ident::new(sym::allow, self.span),
                     vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))],
-                ))]
-                .into(),
+                ))],
                 ItemKind::Use(UseTree {
                     prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),
                     kind: UseTreeKind::Nested(vec![
index dd7989cf48c37b784cd37fdc7020e71bcea334bc..c7f2d95e72f0c45e817a06b5e149ecdd0f1ec1cf 100644 (file)
@@ -1,12 +1,12 @@
 use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::path_std;
-
 use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Span;
+use thin_vec::thin_vec;
 
 pub fn expand_deriving_clone(
     cx: &mut ExtCtxt<'_>,
@@ -68,7 +68,7 @@ pub fn expand_deriving_clone(
     }
 
     let inline = cx.meta_word(span, sym::inline);
-    let attrs = vec![cx.attribute(inline)].into();
+    let attrs = thin_vec![cx.attribute(inline)];
     let trait_def = TraitDef {
         span,
         path: path_std!(clone::Clone),
index 9b6d3e5032f9495f92caad1231e79e01680e7d05..5b556c5c9b9d1848c25193dcc1ecb64d0010c0a8 100644 (file)
@@ -7,6 +7,7 @@
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
+use thin_vec::thin_vec;
 
 pub fn expand_deriving_eq(
     cx: &mut ExtCtxt<'_>,
@@ -20,7 +21,7 @@ pub fn expand_deriving_eq(
     let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span));
     let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]);
     let no_coverage = cx.meta_word(span, sym::no_coverage);
-    let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)].into();
+    let attrs = thin_vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)];
     let trait_def = TraitDef {
         span,
         path: path_std!(cmp::Eq),
index 0e17b95178759368b59147dfca1fda6751b78066..72625869558119ec908051a5e21ca8d9e718c574 100644 (file)
@@ -1,11 +1,11 @@
 use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::path_std;
-
 use rustc_ast::MetaItem;
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
+use thin_vec::thin_vec;
 
 pub fn expand_deriving_ord(
     cx: &mut ExtCtxt<'_>,
@@ -15,7 +15,7 @@ pub fn expand_deriving_ord(
     push: &mut dyn FnMut(Annotatable),
 ) {
     let inline = cx.meta_word(span, sym::inline);
-    let attrs = vec![cx.attribute(inline)].into();
+    let attrs = thin_vec![cx.attribute(inline)];
     let trait_def = TraitDef {
         span,
         path: path_std!(cmp::Ord),
index ac1325b92a6f38ed38acd385baded5839cdb076f..42ee65b570a2a95a2fff7ee0f6ffe794661673ae 100644 (file)
@@ -1,12 +1,12 @@
 use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::{path_local, path_std};
-
 use rustc_ast::ptr::P;
 use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
+use thin_vec::thin_vec;
 
 pub fn expand_deriving_partial_eq(
     cx: &mut ExtCtxt<'_>,
@@ -68,7 +68,7 @@ fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOr
     // No need to generate `ne`, the default suffices, and not generating it is
     // faster.
     let inline = cx.meta_word(span, sym::inline);
-    let attrs = vec![cx.attribute(inline)].into();
+    let attrs = thin_vec![cx.attribute(inline)];
     let methods = vec![MethodDef {
         name: sym::eq,
         generics: Bounds::empty(),
index 7763e55401783b19670703c070c72fa418e2117f..516892aeda96f9da303e693e041542bf6ab8916b 100644 (file)
@@ -1,11 +1,11 @@
 use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::{path_std, pathvec_std};
-
 use rustc_ast::MetaItem;
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
+use thin_vec::thin_vec;
 
 pub fn expand_deriving_partial_ord(
     cx: &mut ExtCtxt<'_>,
@@ -19,7 +19,7 @@ pub fn expand_deriving_partial_ord(
         Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std));
 
     let inline = cx.meta_word(span, sym::inline);
-    let attrs = vec![cx.attribute(inline)].into();
+    let attrs = thin_vec![cx.attribute(inline)];
 
     let partial_cmp_def = MethodDef {
         name: sym::partial_cmp,
index f316f01ef66152e7bb1b3308ce9c924b0d80b6e2..a94c8a996e6423fbf1151ace70f135a89f6cd6f0 100644 (file)
@@ -1,6 +1,5 @@
 use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
-
 use rustc_ast as ast;
 use rustc_ast::{walk_list, EnumDef, VariantData};
 use rustc_errors::Applicability;
@@ -9,6 +8,7 @@
 use rustc_span::symbol::{kw, sym};
 use rustc_span::Span;
 use smallvec::SmallVec;
+use thin_vec::thin_vec;
 
 pub fn expand_deriving_default(
     cx: &mut ExtCtxt<'_>,
@@ -20,7 +20,7 @@ pub fn expand_deriving_default(
     item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
 
     let inline = cx.meta_word(span, sym::inline);
-    let attrs = vec![cx.attribute(inline)].into();
+    let attrs = thin_vec![cx.attribute(inline)];
     let trait_def = TraitDef {
         span,
         path: Path::new(vec![kw::Default, sym::Default]),
index ecaafd0fc2652946fd2afb73a79b1d4592477b77..adffebd3fd2850af0f0206ac03391d12940270a7 100644 (file)
 pub use StaticFields::*;
 pub use SubstructureFields::*;
 
-use std::cell::RefCell;
-use std::iter;
-use std::vec;
-
+use crate::deriving;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, EnumDef, Expr, Generics, PatKind};
 use rustc_ast::{GenericArg, GenericParamKind, VariantData};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
-
+use std::cell::RefCell;
+use std::iter;
+use std::vec;
+use thin_vec::thin_vec;
 use ty::{Bounds, Path, Ref, Self_, Ty};
 
-use crate::deriving;
-
 pub mod ty;
 
 pub struct TraitDef<'a> {
@@ -715,7 +713,7 @@ fn create_derived_impl(
         let self_type = cx.ty_path(path);
 
         let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived));
-        let attrs = vec![attr].into();
+        let attrs = thin_vec![attr];
         let opt_trait_ref = Some(trait_ref);
 
         cx.item(
index 2bad9bbce6650b30af53e970bd90a0ae21287552..45b9b8ab6b6410e0034793b06f8f77cfc4888504 100644 (file)
@@ -9,6 +9,7 @@
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
+use thin_vec::thin_vec;
 
 pub fn expand(
     ecx: &mut ExtCtxt<'_>,
@@ -116,7 +117,7 @@ fn call_allocator(&self, method: Symbol, mut args: Vec<P<Expr>>) -> P<Expr> {
     fn attrs(&self) -> AttrVec {
         let special = sym::rustc_std_internal_symbol;
         let special = self.cx.meta_word(self.span, special);
-        vec![self.cx.attribute(special)].into()
+        thin_vec![self.cx.attribute(special)]
     }
 
     fn arg_ty(
index 11565ba72d7555f1fb157468fd4c83b957eb873e..280fa70451141f85f821c2f2b02e0f87f82d5a83 100644 (file)
@@ -16,6 +16,9 @@
 
 extern crate proc_macro;
 
+#[macro_use]
+extern crate tracing;
+
 use crate::deriving::*;
 
 use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind};
index 90ea1e457ba8031885f3941cb605e00e52590c0f..49ef538f04e14ab83ffe4ed173e5646b7ca40d30 100644 (file)
@@ -6,6 +6,7 @@
 use rustc_span::hygiene::AstPass;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::DUMMY_SP;
+use thin_vec::thin_vec;
 
 pub fn inject(
     mut krate: ast::Crate,
@@ -51,7 +52,7 @@ pub fn inject(
             cx.item(
                 span,
                 ident,
-                vec![cx.attribute(cx.meta_word(span, sym::macro_use))].into(),
+                thin_vec![cx.attribute(cx.meta_word(span, sym::macro_use))],
                 ast::ItemKind::ExternCrate(None),
             ),
         );
@@ -78,7 +79,7 @@ pub fn inject(
     let use_item = cx.item(
         span,
         Ident::empty(),
-        vec![cx.attribute(cx.meta_word(span, sym::prelude_import))].into(),
+        thin_vec![cx.attribute(cx.meta_word(span, sym::prelude_import))],
         ast::ItemKind::Use(ast::UseTree {
             prefix: cx.path(span, import_path),
             kind: ast::UseTreeKind::Glob,
index 03c84f5ec2a740fe1f158099885719b5369477c6..7efb6cc61eecbb2325525c4696b5b5b7c9040c8a 100644 (file)
@@ -1,7 +1,6 @@
 /// The expansion from a test function to the appropriate test struct for libtest
 /// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
 use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
-
 use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::ptr::P;
@@ -11,8 +10,8 @@
 use rustc_session::Session;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
-
 use std::iter;
+use thin_vec::thin_vec;
 
 // #[test_case] is used by custom test authors to mark tests
 // When building for test, it needs to make the item public and gensym the name
@@ -219,7 +218,7 @@ pub fn expand_test_or_bench(
     let mut test_const = cx.item(
         sp,
         Ident::new(item.ident.name, sp),
-        vec![
+        thin_vec![
             // #[cfg(test)]
             cx.attribute(attr::mk_list_item(
                 Ident::new(sym::cfg, attr_sp),
@@ -227,8 +226,7 @@ pub fn expand_test_or_bench(
             )),
             // #[rustc_test_marker]
             cx.attribute(cx.meta_word(attr_sp, sym::rustc_test_marker)),
-        ]
-        .into(),
+        ],
         // const $ident: test::TestDescAndFn =
         ast::ItemKind::Const(
             ast::Defaultness::Final,
@@ -337,7 +335,7 @@ pub fn expand_test_or_bench(
     // extern crate test
     let test_extern = cx.item(sp, test_id, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None));
 
-    tracing::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
+    debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
 
     if is_stmt {
         vec![
index 093f0f10a3867e591108604f1547841a960ff125..079c6ff37cfb53222caeaf79014ebda64fa65716 100644 (file)
@@ -14,7 +14,7 @@
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::spec::PanicStrategy;
 use smallvec::{smallvec, SmallVec};
-use tracing::debug;
+use thin_vec::thin_vec;
 
 use std::{iter, mem};
 
@@ -335,7 +335,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
 
     let main = P(ast::Item {
         ident: main_id,
-        attrs: vec![main_attr].into(),
+        attrs: thin_vec![main_attr],
         id: ast::DUMMY_NODE_ID,
         kind: main,
         vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
index 815450f689e4ab50c4957a35d5ee90faf22ca7e9..0497c2570e6228b3984b639805ebbdef0bd6d07b 100644 (file)
@@ -342,7 +342,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
 
     let ret_place = codegen_place(fx, destination);
 
-    // Handle special calls like instrinsics and empty drop glue.
+    // Handle special calls like intrinsics and empty drop glue.
     let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() {
         let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs)
             .unwrap()
index cb5d73a7e0ba9bd277c6318a70da19d284360bb4..9224f499339cbd8e14cf6d5d75e8adaf608362a0 100644 (file)
@@ -59,7 +59,7 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool {
                         ErrorHandled::TooGeneric => {
                             span_bug!(
                                 constant.span,
-                                "codgen encountered polymorphic constant: {:?}",
+                                "codegen encountered polymorphic constant: {:?}",
                                 err
                             );
                         }
index 95239f415a99b5e88c965b37aab3cc860cdf97a3..39e9e784a478b12a68268e2fa6c0b6209f864365 100644 (file)
@@ -203,7 +203,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>(
             sym::transmute => {
                 crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info);
             }
-            _ => unimplemented!("unsupported instrinsic {}", intrinsic),
+            _ => unimplemented!("unsupported intrinsic {}", intrinsic),
         }
         return;
     };
index 2a6612eb86f12c62b41f516b41dfe18c5cd40806..5202ac697e9496f4551bb265de5732ed542fed72 100644 (file)
@@ -19,7 +19,6 @@
 
 use libc::{c_char, c_uint};
 use smallvec::SmallVec;
-use tracing::debug;
 
 impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
     fn codegen_inline_asm(
index 2e614e5dd88e7fc18068974f22e1e7a3a8a34df7..38a366095b41da97c9fe8906b8ab626be71935e5 100644 (file)
@@ -190,10 +190,10 @@ fn create_dll_import_lib(
 
             let output_path_z = rustc_fs_util::path_to_c_string(&output_path);
 
-            tracing::trace!("invoking LLVMRustWriteImportLibrary");
-            tracing::trace!("  dll_name {:#?}", dll_name_z);
-            tracing::trace!("  output_path {}", output_path.display());
-            tracing::trace!(
+            trace!("invoking LLVMRustWriteImportLibrary");
+            trace!("  dll_name {:#?}", dll_name_z);
+            trace!("  output_path {}", output_path.display());
+            trace!(
                 "  import names: {}",
                 dll_imports
                     .iter()
index e4af6269abc5305870515a832baf27da3111b677..a89df00e248e30e385fe2bc35202e4879d866780 100644 (file)
@@ -18,7 +18,6 @@
 use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
 use rustc_session::cgu_reuse_tracker::CguReuse;
 use rustc_session::config::{self, CrateType, Lto};
-use tracing::{debug, info};
 
 use std::ffi::{CStr, CString};
 use std::fs::File;
index 740a68d0772c1632ba54061cc8bb783591eecfd6..a695df8409bb4621326aa87929be8851337a418a 100644 (file)
@@ -28,7 +28,6 @@
 use rustc_span::symbol::sym;
 use rustc_span::InnerSpan;
 use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo};
-use tracing::debug;
 
 use libc::{c_char, c_int, c_uint, c_void, size_t};
 use std::ffi::CString;
index e7e373bf45d11043f9e3ba32bd18d09f10313322..63b63c6a1fab5d42d89c48ca7dea9dfc90ddd69e 100644 (file)
@@ -27,7 +27,6 @@
 use std::iter;
 use std::ops::Deref;
 use std::ptr;
-use tracing::{debug, instrument};
 
 // All Builders must have an llfn associated with them
 #[must_use]
index d55f995b933a6f6ec4935c8bf712247a0fcd9762..b83c1e8f08f3182ee2178faa1a03b98862d3985c 100644 (file)
@@ -11,7 +11,6 @@
 use crate::llvm;
 use crate::value::Value;
 use rustc_codegen_ssa::traits::*;
-use tracing::debug;
 
 use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
 use rustc_middle::ty::{self, Instance, TypeVisitable};
index 63d3bb40a3fe0c448463f9d32253b984b7476424..13e437cfbf7fb1b52d06589eb20c068ad140a131 100644 (file)
@@ -21,7 +21,6 @@
 
 use libc::{c_char, c_uint};
 use std::fmt::Write;
-use tracing::debug;
 
 /*
 * A note on nomenclature of linking: "extern", "foreign", and "upcall".
index d3e33da27993c6b3286765128fd0b7ca6ce04952..a559f7f3d57035a58da56542d948fdad98a9274a 100644 (file)
@@ -23,7 +23,6 @@
     AddressSpace, Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange,
 };
 use std::ops::Range;
-use tracing::debug;
 
 pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value {
     let alloc = alloc.inner();
index 58f391692c49c109bb59b7011109403a09625943..0d1df6fb1acd99e437c9fbcf3d4bc59b984b4578 100644 (file)
@@ -16,8 +16,6 @@
 
 use std::ffi::CString;
 
-use tracing::debug;
-
 /// Generates and exports the Coverage Map.
 ///
 /// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
index 98ba38356a4c30c8cafcd806115ebff394581817..964a632b6eeddc77a68711eed82bc4eef8c20e73 100644 (file)
@@ -28,7 +28,6 @@
 use std::ffi::CString;
 
 use std::iter;
-use tracing::debug;
 
 pub mod mapgen;
 
index d0a6f216858b61575238b6101f2a34fd151d8bd0..163ccd9460c54b58c9f1326fd3d186ae60496d9f 100644 (file)
@@ -42,7 +42,6 @@
 use rustc_symbol_mangling::typeid_for_trait_ref;
 use rustc_target::abi::{Align, Size};
 use smallvec::smallvec;
-use tracing::debug;
 
 use libc::{c_char, c_longlong, c_uint};
 use std::borrow::Cow;
@@ -51,7 +50,6 @@
 use std::iter;
 use std::path::{Path, PathBuf};
 use std::ptr;
-use tracing::instrument;
 
 impl PartialEq for llvm::Metadata {
     fn eq(&self, other: &Self) -> bool {
index cf591295b84680b57c660219f07e891a0c09a821..b23fe3fc9d5575ae8971c6dbb4b61cd1905512aa 100644 (file)
@@ -39,7 +39,6 @@
 use std::cell::OnceCell;
 use std::cell::RefCell;
 use std::iter;
-use tracing::debug;
 
 mod create_scope_map;
 pub mod gdb;
index 8f24367390775e1a8b43cac1024b7d093b17e4d3..a40cfc8b23fb32217f5b6b0897bea0e4f9e27373 100644 (file)
@@ -6,7 +6,7 @@
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
 use rustc_middle::ty::{self, DefIdTree, Ty};
-use tracing::trace;
+use trace;
 
 use crate::common::CodegenCx;
 use crate::llvm;
index fa0ecd18fc895bcd7908250d6b970a0834677243..0f663a26732bba8abf5094a095a859784f6f9012 100644 (file)
@@ -22,7 +22,6 @@
 use rustc_middle::ty::Ty;
 use rustc_symbol_mangling::typeid::typeid_for_fnabi;
 use smallvec::SmallVec;
-use tracing::debug;
 
 /// Declare a function.
 ///
index 636d689a34b5a900ef1ad0ae41484ac5147b4729..334425ae55b43300c1f3f7e06463b235a5ad3a28 100644 (file)
@@ -16,6 +16,8 @@
 
 #[macro_use]
 extern crate rustc_macros;
+#[macro_use]
+extern crate tracing;
 
 use back::write::{create_informational_target_machine, create_target_machine};
 
index f5d676c44e3428b83677bd237daf7e5ad9ca2782..1b049dfe9790444e300423cbbef45ccb27370168 100644 (file)
@@ -15,7 +15,6 @@
 use rustc_target::spec::{MergeFunctions, PanicStrategy};
 use smallvec::{smallvec, SmallVec};
 use std::ffi::{CStr, CString};
-use tracing::debug;
 
 use std::mem;
 use std::path::Path;
index 6e94284852f357970663599fbf08766576eef75c..1eceb7f5c87beb362760851e5ce31ae40a66c75f 100644 (file)
@@ -11,7 +11,6 @@
 use rustc_middle::ty::{self, Instance, TypeVisitable};
 use rustc_session::config::CrateType;
 use rustc_target::spec::RelocModel;
-use tracing::debug;
 
 impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> {
     fn predefine_static(
index 9f0e6c80b19a7f0108620d5fa07275f7dd90aaee..dc1165835e7ca271b38986619f20801fb8c5a71e 100644 (file)
@@ -11,7 +11,6 @@
 use rustc_target::abi::{Int, Pointer, F32, F64};
 use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants};
 use smallvec::{smallvec, SmallVec};
-use tracing::debug;
 
 use std::fmt::Write;
 
index 32b340832ce289625a283503b9bdf090c2a608c6..8d7e2c5cf3939a5cc5d907751f18ba1c94df1355 100644 (file)
@@ -540,7 +540,7 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
         .map(|fnabi| (fnabi.conv, &fnabi.args[..]))
         .unwrap_or((Conv::Rust, &[]));
 
-    // Decorate symbols with prefices, suffices and total number of bytes of arguments.
+    // Decorate symbols with prefixes, suffixes and total number of bytes of arguments.
     // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
     let (prefix, suffix) = match conv {
         Conv::X86Fastcall => ("@", "@"),
index d6bbcd99234553bf3173620c5fdc8d7c0a419674..2b931bfc91d63b05c93717a42b522d87a9a4b127 100644 (file)
@@ -191,7 +191,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 // errored or at least linted
                 ErrorHandled::Reported(_) | ErrorHandled::Linted => {}
                 ErrorHandled::TooGeneric => {
-                    span_bug!(const_.span, "codgen encountered polymorphic constant: {:?}", err)
+                    span_bug!(const_.span, "codegen encountered polymorphic constant: {:?}", err)
                 }
             }
         }
index 268c4d765030568f074d975153a2bd6fa30a8f1b..04b8c8636f634ddeaebdaf90e7417939ae10c9ba 100644 (file)
@@ -4,7 +4,6 @@
 use crate::common::IntPredicate;
 use crate::glue;
 use crate::traits::*;
-use crate::MemFlags;
 
 use rustc_middle::mir;
 use rustc_middle::mir::tcx::PlaceTy;
@@ -343,16 +342,6 @@ pub fn codegen_set_discr<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
                 ..
             } => {
                 if variant_index != dataful_variant {
-                    if bx.cx().sess().target.arch == "arm"
-                        || bx.cx().sess().target.arch == "aarch64"
-                    {
-                        // FIXME(#34427): as workaround for LLVM bug on ARM,
-                        // use memset of 0 before assigning niche value.
-                        let fill_byte = bx.cx().const_u8(0);
-                        let size = bx.cx().const_usize(self.layout.size.bytes());
-                        bx.memset(self.llval, fill_byte, size, self.align, MemFlags::empty());
-                    }
-
                     let niche = self.project_field(bx, tag_field);
                     let niche_llty = bx.cx().immediate_backend_type(niche.layout);
                     let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
index b46f71fc78a3b9164919ce9c3e2097e7764875d9..a2f14e753aebe7ca5cb6bf7b049123f7c746ff5d 100644 (file)
@@ -197,7 +197,7 @@ pub(super) fn op_to_const<'tcx>(
     }
 }
 
-#[instrument(skip(tcx), level = "debug")]
+#[instrument(skip(tcx), level = "debug", ret)]
 pub(crate) fn turn_into_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
     constant: ConstAlloc<'tcx>,
@@ -224,10 +224,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
     );
 
     // Turn this into a proper constant.
-    let const_val = op_to_const(&ecx, &mplace.into());
-    debug!(?const_val);
-
-    const_val
+    op_to_const(&ecx, &mplace.into())
 }
 
 #[instrument(skip(tcx), level = "debug")]
index 373b139c86e424d941b779aaddbe7e5912db6301..8b7c3cf3377cc9108bc7a9e9f1f7f4763b35cdf8 100644 (file)
@@ -204,7 +204,7 @@ fn get_info_on_unsized_field<'tcx>(
     (unsized_inner_ty, num_elems)
 }
 
-#[instrument(skip(ecx), level = "debug")]
+#[instrument(skip(ecx), level = "debug", ret)]
 fn create_pointee_place<'tcx>(
     ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
     ty: Ty<'tcx>,
@@ -237,14 +237,11 @@ fn create_pointee_place<'tcx>(
         let ptr = ecx.allocate_ptr(size, align, MemoryKind::Stack).unwrap();
         debug!(?ptr);
 
-        let place = MPlaceTy::from_aligned_ptr_with_meta(
+        MPlaceTy::from_aligned_ptr_with_meta(
             ptr.into(),
             layout,
             MemPlaceMeta::Meta(Scalar::from_machine_usize(num_elems as u64, &tcx)),
-        );
-        debug!(?place);
-
-        place
+        )
     } else {
         create_mplace_from_layout(ecx, ty)
     }
@@ -253,7 +250,7 @@ fn create_pointee_place<'tcx>(
 /// Converts a `ValTree` to a `ConstValue`, which is needed after mir
 /// construction has finished.
 // FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
-#[instrument(skip(tcx), level = "debug")]
+#[instrument(skip(tcx), level = "debug", ret)]
 pub fn valtree_to_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -294,7 +291,7 @@ pub fn valtree_to_const_value<'tcx>(
             dump_place(&ecx, place.into());
             intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
 
-            let const_val = match ty.kind() {
+            match ty.kind() {
                 ty::Ref(_, _, _) => {
                     let ref_place = place.to_ref(&tcx);
                     let imm =
@@ -303,10 +300,7 @@ pub fn valtree_to_const_value<'tcx>(
                     op_to_const(&ecx, &imm.into())
                 }
                 _ => op_to_const(&ecx, &place.into()),
-            };
-            debug!(?const_val);
-
-            const_val
+            }
         }
         ty::Never
         | ty::Error(_)
index 66ab3f15716f2e5d7eb93782df478f32bf5f50e2..24dbc769529c3665a6e30da12c750226a21ffd19 100644 (file)
@@ -334,7 +334,7 @@ pub enum InternKind {
 /// tracks where in the value we are and thus can show much better error messages.
 /// Any errors here would anyway be turned into `const_err` lints, whereas validation failures
 /// are hard errors.
-#[tracing::instrument(level = "debug", skip(ecx))]
+#[instrument(level = "debug", skip(ecx))]
 pub fn intern_const_alloc_recursive<
     'mir,
     'tcx: 'mir,
index a8ec8447f64a4d1206fdf16aead8229ad09c38dd..adda9639990d0e38934100bce1856cb15c67c518 100644 (file)
@@ -320,7 +320,7 @@ pub fn emulate_intrinsic(
                 let (a_offset, b_offset) =
                     match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) {
                         (Err(a), Err(b)) => {
-                            // Neither poiner points to an allocation.
+                            // Neither pointer points to an allocation.
                             // If these are inequal or null, this *will* fail the deref check below.
                             (a, b)
                         }
index 69dbc9592fa87a73bd11b0a4e38805260e30c8df..ed155fbfef087e3fab4b188b53efcda489cc295b 100644 (file)
@@ -437,7 +437,7 @@ fn check_offset_align<'tcx>(offset: u64, align: Align) -> InterpResult<'tcx> {
                         msg,
                     })
                 }
-                // Ensure we never consider the null pointer dereferencable.
+                // Ensure we never consider the null pointer dereferenceable.
                 if M::Provenance::OFFSET_IS_ADDR {
                     assert_ne!(ptr.addr(), Size::ZERO);
                 }
@@ -914,7 +914,7 @@ pub fn write_ptr_sized(&mut self, offset: Size, val: Scalar<Prov>) -> InterpResu
         self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val)
     }
 
-    /// Mark the entire referenced range as uninitalized
+    /// Mark the entire referenced range as uninitialized
     pub fn write_uninit(&mut self) -> InterpResult<'tcx> {
         Ok(self
             .alloc
index 67dc9011ea209ace27078a8460ecc627feb1091b..77da8f1041eeaf961e70e661ba911878e265a35c 100644 (file)
@@ -1,6 +1,6 @@
 //! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy.
 //!
-//! OpTy and PlaceTy genrally work by "let's see if we are actually an MPlaceTy, and do something custom if not".
+//! OpTy and PlaceTy generally work by "let's see if we are actually an MPlaceTy, and do something custom if not".
 //! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway.
 //! For OpTy, the custom thing on field pojections has to be pretty clever (since `Operand::Immediate` can have fields),
 //! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial,
index 683e11ff7e0e50fc97448cff63ad10185b48e03c..6b827149f505ea60f6c7c44e1d7fb39652eb67fa 100644 (file)
@@ -251,8 +251,8 @@ pub fn eval_rvalue_into_place(
 
             Len(place) => {
                 let src = self.eval_place(place)?;
-                let mplace = self.force_allocation(&src)?;
-                let len = mplace.len(self)?;
+                let op = self.place_to_op(&src)?;
+                let len = op.len(self)?;
                 self.write_scalar(Scalar::from_machine_usize(len, self), &dest)?;
             }
 
index a71a5d4b833c8c43d2681f92afe6544e6c45ae4e..ea366eba77245b90ec7a44dad831c7a3c68d60b4 100644 (file)
@@ -217,7 +217,7 @@ fn check_argument_compat(
         // When comparing the PassMode, we have to be smart about comparing the attributes.
         let arg_attr_compat = |a1: &ArgAttributes, a2: &ArgAttributes| {
             // There's only one regular attribute that matters for the call ABI: InReg.
-            // Everything else is things like noalias, dereferencable, nonnull, ...
+            // Everything else is things like noalias, dereferenceable, nonnull, ...
             // (This also applies to pointee_size, pointee_align.)
             if a1.regular.contains(ArgAttribute::InReg) != a2.regular.contains(ArgAttribute::InReg)
             {
@@ -556,7 +556,7 @@ pub(crate) fn eval_fn_call(
                     .tcx
                     .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
                 let ty::Dynamic(data, ..) = receiver_tail.kind() else {
-                    span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail)
+                    span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail)
                 };
 
                 // Get the required information from the vtable.
index b3a511d5a492bdd146f3e36cda800d14a3fd032e..cab23b7241ffc2ea2649103b83552c091d00119a 100644 (file)
@@ -32,7 +32,7 @@ pub fn get_vtable_ptr(
         Ok(vtable_ptr.into())
     }
 
-    /// Returns a high-level representation of the entires of the given vtable.
+    /// Returns a high-level representation of the entries of the given vtable.
     pub fn get_vtable_entries(
         &self,
         vtable: Pointer<Option<M::Provenance>>,
index 5c641f54f686d8a99b3abd71c8507dfadbaaaeb4..2d8658db5e6aacb45eed5283d42b1c67d148b41d 100644 (file)
@@ -8,25 +8,26 @@ doctest = false
 
 [dependencies]
 arrayvec = { version = "0.7", default-features = false }
+bitflags = "1.2.1"
+cfg-if = "0.1.2"
 ena = "0.14"
 indexmap = { version = "1.9.1" }
-tracing = "0.1"
 jobserver_crate = { version = "0.1.13", package = "jobserver" }
-rustc_serialize = { path = "../rustc_serialize" }
-rustc_macros = { path = "../rustc_macros" }
-rustc_graphviz = { path = "../rustc_graphviz" }
-cfg-if = "0.1.2"
-stable_deref_trait = "1.0.0"
-rayon = { version = "0.4.0", package = "rustc-rayon", optional = true }
+libc = "0.2"
+measureme = "10.0.0"
 rayon-core = { version = "0.4.0", package = "rustc-rayon-core", optional = true }
+rayon = { version = "0.4.0", package = "rustc-rayon", optional = true }
+rustc_graphviz = { path = "../rustc_graphviz" }
 rustc-hash = "1.1.0"
-smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] }
 rustc_index = { path = "../rustc_index", package = "rustc_index" }
-bitflags = "1.2.1"
-measureme = "10.0.0"
-libc = "0.2"
+rustc_macros = { path = "../rustc_macros" }
+rustc_serialize = { path = "../rustc_serialize" }
+smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] }
+stable_deref_trait = "1.0.0"
 stacker = "0.1.14"
 tempfile = "3.2"
+thin-vec = "0.2.8"
+tracing = "0.1"
 
 [dependencies.parking_lot]
 version = "0.11"
index 5ff2d18dd2be375bd77725bb8f217b25110840cf..a39178016ce20617dda20a517959ae7843ade27b 100644 (file)
@@ -29,7 +29,7 @@ pub fn to_smaller_hash(&self) -> u64 {
         // quality hash values, let's still combine the two values because the
         // Fingerprints in DefPathHash have the StableCrateId portion which is
         // the same for all DefPathHashes from the same crate. Combining the
-        // two halfs makes sure we get a good quality hash in such cases too.
+        // two halves makes sure we get a good quality hash in such cases too.
         self.0.wrapping_mul(3).wrapping_add(self.1)
     }
 
@@ -120,7 +120,7 @@ fn write_fingerprint(&mut self, fingerprint: &Fingerprint) {
         // quality hash values, let's still combine the two values because the
         // Fingerprints in DefPathHash have the StableCrateId portion which is
         // the same for all DefPathHashes from the same crate. Combining the
-        // two halfs makes sure we get a good quality hash in such cases too.
+        // two halves makes sure we get a good quality hash in such cases too.
         //
         // Since `Unhasher` is used only in the context of HashMaps, it is OK
         // to combine the two components in an order-independent way (which is
index c8b09cffe0136e0ef5cfb8b4d5c9a4262fb0c0ea..a7429ed008fb8d1d496c0b4f6e58ac5b96579c46 100644 (file)
@@ -75,7 +75,6 @@ pub fn cold_path<F: FnOnce() -> R, R>(f: F) -> R {
 pub mod sharded;
 pub mod stack;
 pub mod sync;
-pub mod thin_vec;
 pub mod tiny_list;
 pub mod transitive_relation;
 pub mod vec_linked_list;
index d912211443a893813d6b5c4fda225f2487765289..a0d4b7ade1f33385e751e48b4379fa9323183f1c 100644 (file)
@@ -1,6 +1,6 @@
-use crate::thin_vec::ThinVec;
 use smallvec::{Array, SmallVec};
 use std::ptr;
+use thin_vec::ThinVec;
 
 pub trait MapInPlace<T>: Sized {
     fn map_in_place<F>(&mut self, mut f: F)
index 9efea1228ab29e8ad42783aee9adc7165ef2f5a8..937cb671573a1c97b2096d3dab4382075365a97e 100644 (file)
@@ -164,7 +164,7 @@ pub fn offset_keys<F>(&mut self, f: F)
     /// It is up to the caller to make sure that the elements are sorted by key
     /// and that there are no duplicates.
     #[inline]
-    pub fn insert_presorted(&mut self, mut elements: Vec<(K, V)>) {
+    pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) {
         if elements.is_empty() {
             return;
         }
@@ -173,28 +173,28 @@ pub fn insert_presorted(&mut self, mut elements: Vec<(K, V)>) {
 
         let start_index = self.lookup_index_for(&elements[0].0);
 
-        let drain = match start_index {
+        let elements = match start_index {
             Ok(index) => {
-                let mut drain = elements.drain(..);
-                self.data[index] = drain.next().unwrap();
-                drain
+                let mut elements = elements.into_iter();
+                self.data[index] = elements.next().unwrap();
+                elements
             }
             Err(index) => {
                 if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 {
                     // We can copy the whole range without having to mix with
                     // existing elements.
-                    self.data.splice(index..index, elements.drain(..));
+                    self.data.splice(index..index, elements.into_iter());
                     return;
                 }
 
-                let mut drain = elements.drain(..);
-                self.data.insert(index, drain.next().unwrap());
-                drain
+                let mut elements = elements.into_iter();
+                self.data.insert(index, elements.next().unwrap());
+                elements
             }
         };
 
         // Insert the rest
-        for (k, v) in drain {
+        for (k, v) in elements {
             self.insert(k, v);
         }
     }
diff --git a/compiler/rustc_data_structures/src/thin_vec.rs b/compiler/rustc_data_structures/src/thin_vec.rs
deleted file mode 100644 (file)
index fce42e7..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-use crate::stable_hasher::{HashStable, StableHasher};
-
-use std::iter::FromIterator;
-
-/// A vector type optimized for cases where this size is usually 0 (cf. `SmallVec`).
-/// The `Option<Box<..>>` wrapping allows us to represent a zero sized vector with `None`,
-/// which uses only a single (null) pointer.
-#[derive(Clone, Encodable, Decodable, Debug, Hash, Eq, PartialEq)]
-pub struct ThinVec<T>(Option<Box<Vec<T>>>);
-
-impl<T> ThinVec<T> {
-    pub fn new() -> Self {
-        ThinVec(None)
-    }
-
-    pub fn iter(&self) -> std::slice::Iter<'_, T> {
-        self.into_iter()
-    }
-
-    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
-        self.into_iter()
-    }
-
-    pub fn push(&mut self, item: T) {
-        match *self {
-            ThinVec(Some(ref mut vec)) => vec.push(item),
-            ThinVec(None) => *self = vec![item].into(),
-        }
-    }
-
-    /// Note: if `set_len(0)` is called on a non-empty `ThinVec`, it will
-    /// remain in the `Some` form. This is required for some code sequences
-    /// (such as the one in `flat_map_in_place`) that call `set_len(0)` before
-    /// an operation that might panic, and then call `set_len(n)` again
-    /// afterwards.
-    pub unsafe fn set_len(&mut self, new_len: usize) {
-        match *self {
-            ThinVec(None) => {
-                // A prerequisite of `Vec::set_len` is that `new_len` must be
-                // less than or equal to capacity(). The same applies here.
-                if new_len != 0 {
-                    panic!("unsafe ThinVec::set_len({})", new_len);
-                }
-            }
-            ThinVec(Some(ref mut vec)) => vec.set_len(new_len),
-        }
-    }
-
-    pub fn insert(&mut self, index: usize, value: T) {
-        match *self {
-            ThinVec(None) => {
-                if index == 0 {
-                    *self = vec![value].into();
-                } else {
-                    panic!("invalid ThinVec::insert");
-                }
-            }
-            ThinVec(Some(ref mut vec)) => vec.insert(index, value),
-        }
-    }
-
-    pub fn remove(&mut self, index: usize) -> T {
-        match self {
-            ThinVec(None) => panic!("invalid ThinVec::remove"),
-            ThinVec(Some(vec)) => vec.remove(index),
-        }
-    }
-
-    pub fn as_slice(&self) -> &[T] {
-        match self {
-            ThinVec(None) => &[],
-            ThinVec(Some(vec)) => vec.as_slice(),
-        }
-    }
-}
-
-impl<T> From<Vec<T>> for ThinVec<T> {
-    fn from(vec: Vec<T>) -> Self {
-        if vec.is_empty() { ThinVec(None) } else { ThinVec(Some(Box::new(vec))) }
-    }
-}
-
-impl<T> Into<Vec<T>> for ThinVec<T> {
-    fn into(self) -> Vec<T> {
-        match self {
-            ThinVec(None) => Vec::new(),
-            ThinVec(Some(vec)) => *vec,
-        }
-    }
-}
-
-impl<T> ::std::ops::Deref for ThinVec<T> {
-    type Target = [T];
-    fn deref(&self) -> &[T] {
-        match *self {
-            ThinVec(None) => &[],
-            ThinVec(Some(ref vec)) => vec,
-        }
-    }
-}
-
-impl<T> ::std::ops::DerefMut for ThinVec<T> {
-    fn deref_mut(&mut self) -> &mut [T] {
-        match *self {
-            ThinVec(None) => &mut [],
-            ThinVec(Some(ref mut vec)) => vec,
-        }
-    }
-}
-
-impl<T> FromIterator<T> for ThinVec<T> {
-    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
-        // `Vec::from_iter()` should not allocate if the iterator is empty.
-        let vec: Vec<_> = iter.into_iter().collect();
-        if vec.is_empty() { ThinVec(None) } else { ThinVec(Some(Box::new(vec))) }
-    }
-}
-
-impl<T> IntoIterator for ThinVec<T> {
-    type Item = T;
-    type IntoIter = std::vec::IntoIter<T>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        // This is still performant because `Vec::new()` does not allocate.
-        self.0.map_or_else(Vec::new, |ptr| *ptr).into_iter()
-    }
-}
-
-impl<'a, T> IntoIterator for &'a ThinVec<T> {
-    type Item = &'a T;
-    type IntoIter = std::slice::Iter<'a, T>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        self.as_ref().iter()
-    }
-}
-
-impl<'a, T> IntoIterator for &'a mut ThinVec<T> {
-    type Item = &'a mut T;
-    type IntoIter = std::slice::IterMut<'a, T>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        self.as_mut().iter_mut()
-    }
-}
-
-impl<T> Extend<T> for ThinVec<T> {
-    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
-        match *self {
-            ThinVec(Some(ref mut vec)) => vec.extend(iter),
-            ThinVec(None) => *self = iter.into_iter().collect::<Vec<_>>().into(),
-        }
-    }
-
-    fn extend_one(&mut self, item: T) {
-        self.push(item)
-    }
-
-    fn extend_reserve(&mut self, additional: usize) {
-        match *self {
-            ThinVec(Some(ref mut vec)) => vec.reserve(additional),
-            ThinVec(None) => *self = Vec::with_capacity(additional).into(),
-        }
-    }
-}
-
-impl<T: HashStable<CTX>, CTX> HashStable<CTX> for ThinVec<T> {
-    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
-        (**self).hash_stable(hcx, hasher)
-    }
-}
-
-impl<T> Default for ThinVec<T> {
-    fn default() -> Self {
-        Self(None)
-    }
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/compiler/rustc_data_structures/src/thin_vec/tests.rs b/compiler/rustc_data_structures/src/thin_vec/tests.rs
deleted file mode 100644 (file)
index 0221b99..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-use super::*;
-
-impl<T> ThinVec<T> {
-    fn into_vec(self) -> Vec<T> {
-        self.into()
-    }
-}
-
-#[test]
-fn test_from_iterator() {
-    assert_eq!(std::iter::empty().collect::<ThinVec<String>>().into_vec(), Vec::<String>::new());
-    assert_eq!(std::iter::once(42).collect::<ThinVec<_>>().into_vec(), vec![42]);
-    assert_eq!([1, 2].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2]);
-    assert_eq!([1, 2, 3].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2, 3]);
-}
-
-#[test]
-fn test_into_iterator_owned() {
-    assert_eq!(ThinVec::new().into_iter().collect::<Vec<String>>(), Vec::<String>::new());
-    assert_eq!(ThinVec::from(vec![1]).into_iter().collect::<Vec<_>>(), vec![1]);
-    assert_eq!(ThinVec::from(vec![1, 2]).into_iter().collect::<Vec<_>>(), vec![1, 2]);
-    assert_eq!(ThinVec::from(vec![1, 2, 3]).into_iter().collect::<Vec<_>>(), vec![1, 2, 3]);
-}
-
-#[test]
-fn test_into_iterator_ref() {
-    assert_eq!(ThinVec::new().iter().collect::<Vec<&String>>(), Vec::<&String>::new());
-    assert_eq!(ThinVec::from(vec![1]).iter().collect::<Vec<_>>(), vec![&1]);
-    assert_eq!(ThinVec::from(vec![1, 2]).iter().collect::<Vec<_>>(), vec![&1, &2]);
-    assert_eq!(ThinVec::from(vec![1, 2, 3]).iter().collect::<Vec<_>>(), vec![&1, &2, &3]);
-}
-
-#[test]
-fn test_into_iterator_ref_mut() {
-    assert_eq!(ThinVec::new().iter_mut().collect::<Vec<&mut String>>(), Vec::<&mut String>::new());
-    assert_eq!(ThinVec::from(vec![1]).iter_mut().collect::<Vec<_>>(), vec![&mut 1]);
-    assert_eq!(ThinVec::from(vec![1, 2]).iter_mut().collect::<Vec<_>>(), vec![&mut 1, &mut 2]);
-    assert_eq!(
-        ThinVec::from(vec![1, 2, 3]).iter_mut().collect::<Vec<_>>(),
-        vec![&mut 1, &mut 2, &mut 3],
-    );
-}
index 4570c1448337eec7b661868deb0d81d64f219916..d1d02ed73f9595a3847d38d96eb8665fc56badbc 100644 (file)
@@ -7,7 +7,7 @@ edition = "2021"
 crate-type = ["dylib"]
 
 [dependencies]
-tracing = { version = "0.1.28" }
+tracing = { version = "0.1.35" }
 serde_json = "1.0.59"
 rustc_log = { path = "../rustc_log" }
 rustc_middle = { path = "../rustc_middle" }
index f66b1a2976f1cd0427da976c31a92d50fddcef88..faeacd3e41040d317183beaacfbe7137ed4e1177 100644 (file)
@@ -1,5 +1,6 @@
 //! The various pretty-printing routines.
 
+use crate::session_diagnostics::UnprettyDumpFail;
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_errors::ErrorGuaranteed;
@@ -357,12 +358,15 @@ fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
     (src, src_name)
 }
 
-fn write_or_print(out: &str, ofile: Option<&Path>) {
+fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) {
     match ofile {
         None => print!("{}", out),
         Some(p) => {
             if let Err(e) = std::fs::write(p, out) {
-                panic!("print-print failed to write {} due to {}", p.display(), e);
+                sess.emit_fatal(UnprettyDumpFail {
+                    path: p.display().to_string(),
+                    err: e.to_string(),
+                });
             }
         }
     }
@@ -402,7 +406,7 @@ pub fn print_after_parsing(
         _ => unreachable!(),
     };
 
-    write_or_print(&out, ofile);
+    write_or_print(&out, ofile, sess);
 }
 
 pub fn print_after_hir_lowering<'tcx>(
@@ -468,7 +472,7 @@ pub fn print_after_hir_lowering<'tcx>(
         _ => unreachable!(),
     };
 
-    write_or_print(&out, ofile);
+    write_or_print(&out, ofile, tcx.sess);
 }
 
 // In an ideal world, this would be a public function called by the driver after
@@ -512,7 +516,7 @@ fn print_with_analysis(
         _ => unreachable!(),
     };
 
-    write_or_print(&out, ofile);
+    write_or_print(&out, ofile, tcx.sess);
 
     Ok(())
 }
index fe64d0fca9b20c58771ce834bdb5c28a594e3912..e9696792d051f59bcc891e5e87f2f5a6cd9cf265 100644 (file)
@@ -31,3 +31,10 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> {
 #[derive(SessionDiagnostic)]
 #[diag(driver::rlink_no_a_file)]
 pub(crate) struct RlinkNotAFile;
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::unpretty_dump_fail)]
+pub(crate) struct UnprettyDumpFail {
+    pub path: String,
+    pub err: String,
+}
index 73f084cf3290bf7d1d8e7a0af03b4a35410aafa4..8ad198c86c933460a359e9e8b9aaaae7b7b05a44 100644 (file)
@@ -9,3 +9,5 @@ driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding
 driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
 
 driver_rlink_no_a_file = rlink must be a file
+
+driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
diff --git a/compiler/rustc_error_messages/locales/en-US/query_system.ftl b/compiler/rustc_error_messages/locales/en-US/query_system.ftl
new file mode 100644 (file)
index 0000000..167704e
--- /dev/null
@@ -0,0 +1,25 @@
+query_system_reentrant = internal compiler error: re-entrant incremental verify failure, suppressing message
+
+query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node}
+    .help = This is a known issue with the compiler. Run {$run_cmd} to allow your project to compile
+
+query_system_increment_compilation_note1 = Please follow the instructions below to create a bug report with the provided information
+query_system_increment_compilation_note2 = See <https://github.com/rust-lang/rust/issues/84970> for more information
+
+query_system_cycle = cycle detected when {$stack_bottom}
+
+query_system_cycle_usage = cycle used when {$usage}
+
+query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again
+
+query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle
+
+query_system_cycle_recursive_ty_alias = type aliases cannot be recursive
+query_system_cycle_recursive_ty_alias_help1 = consider using a struct, enum, or union instead to break the cycle
+query_system_cycle_recursive_ty_alias_help2 = see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
+
+query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive
+
+query_system_cycle_which_requires = ...which requires {$desc}...
+
+query_system_query_overflow = queries overflow the depth limit!
index 42fb2d538b04d2027cbb3b841cbf3fcd334b83f6..ed5e092814f157ef222eb75753232a5a481e12ae 100644 (file)
@@ -5,6 +5,9 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
+#[macro_use]
+extern crate tracing;
+
 use fluent_bundle::FluentResource;
 use fluent_syntax::parser::ParserError;
 use rustc_data_structures::sync::Lrc;
@@ -16,7 +19,6 @@
 use std::fs;
 use std::io;
 use std::path::{Path, PathBuf};
-use tracing::{instrument, trace};
 
 #[cfg(not(parallel_compiler))]
 use std::cell::LazyCell as Lazy;
@@ -50,6 +52,7 @@
     passes => "../locales/en-US/passes.ftl",
     plugin_impl => "../locales/en-US/plugin_impl.ftl",
     privacy => "../locales/en-US/privacy.ftl",
+    query_system => "../locales/en-US/query_system.ftl",
     save_analysis => "../locales/en-US/save_analysis.ftl",
     ty_utils => "../locales/en-US/ty_utils.ftl",
     typeck => "../locales/en-US/typeck.ftl",
index f75e2596f361b7549233878bccf2aa570baf3c5e..95ae9765a4816fcd7a44102fb21c06545b69c962 100644 (file)
@@ -974,12 +974,12 @@ pub fn sub(
     fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
         &mut self,
         level: Level,
-        mut message: Vec<(M, Style)>,
+        message: Vec<(M, Style)>,
         span: MultiSpan,
         render_span: Option<MultiSpan>,
     ) {
         let message = message
-            .drain(..)
+            .into_iter()
             .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
             .collect();
         let sub = SubDiagnostic { level, message, span, render_span };
index 61d767a1cc6b4f97d806c45b8a0d3a127ec2d59a..7e29dc207accecca9a86275ec477cb9426c2b087 100644 (file)
@@ -12,7 +12,6 @@
 use std::marker::PhantomData;
 use std::ops::{Deref, DerefMut};
 use std::thread::panicking;
-use tracing::debug;
 
 /// Used for emitting structured error messages and other diagnostic information.
 ///
index 6c1bfcb9919eb9df89a3124f90bf16a655da2645..e79ce11a6fc075a8bdf162f7b63256b066c89a29 100644 (file)
@@ -34,7 +34,6 @@
 use std::path::Path;
 use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream};
 use termcolor::{Buffer, Color, WriteColor};
-use tracing::*;
 
 /// Default column width, used in tests and when terminal dimensions cannot be determined.
 const DEFAULT_COLUMN_WIDTH: usize = 140;
index 65338f56d9ccb5ea545c607095c61966e8191f4e..4f407badb3f9eb777e3857ac835dccc9cdab1993 100644 (file)
@@ -21,7 +21,7 @@ pub trait Translate {
     /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
     /// passed around as a reference thereafter.
     fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
-        FromIterator::from_iter(args.to_vec().drain(..))
+        FromIterator::from_iter(args.iter().cloned())
     }
 
     /// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
index 75dcbd69674d652c5897b8db9c596fc74307eabc..ac0e200b1b73ee783e22affe3e6b48ab2b4a435f 100644 (file)
@@ -15,6 +15,9 @@
 #[macro_use]
 extern crate rustc_macros;
 
+#[macro_use]
+extern crate tracing;
+
 extern crate proc_macro as pm;
 
 mod placeholders;
index 4fa91dfeaea27847b274c5242bce21202f1f0011..c8bdc39311c6550c7c206eb0b24e6bc1fc099b22 100644 (file)
@@ -430,7 +430,7 @@ fn parse_tt_inner(
                     }
                 }
                 MatcherLoc::Delimited => {
-                    // Entering the delimeter is trivial.
+                    // Entering the delimiter is trivial.
                     mp.idx += 1;
                     self.cur_mps.push(mp);
                 }
index e009e4f7c68d09388b4e9c26388fdae6755528fd..7764ffd246e323d205c992bd61627c9652ce3b05 100644 (file)
@@ -32,7 +32,6 @@
 use std::borrow::Cow;
 use std::collections::hash_map::Entry;
 use std::{mem, slice};
-use tracing::debug;
 
 pub(crate) struct ParserAnyMacro<'a> {
     parser: Parser<'a>,
@@ -976,7 +975,7 @@ fn replace_with_irrelevant(&mut self) {
         self.maybe_empty = false;
     }
 
-    // Adds `tok` to the set for `self`, marking sequence as non-empy.
+    // Adds `tok` to the set for `self`, marking sequence as non-empty.
     fn add_one(&mut self, tt: TtHandle<'tt>) {
         if !self.tokens.contains(&tt) {
             self.tokens.push(tt);
index beb33c05913cf8c9e5ee81c064db5f1dd729c9c9..59a7b668a83ce96d1da7811bef686cd23980a242 100644 (file)
@@ -6,7 +6,7 @@
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Diagnostic, MultiSpan, PResult};
+use rustc_errors::{MultiSpan, PResult};
 use rustc_parse::lexer::nfc_normalize;
 use rustc_parse::parse_stream_from_source_str;
 use rustc_session::parse::ParseSess;
@@ -15,7 +15,7 @@
 use rustc_span::{BytePos, FileName, Pos, SourceFile, Span};
 
 use pm::bridge::{
-    server, DelimSpan, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree,
+    server, DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree,
 };
 use pm::{Delimiter, Level, LineColumn};
 use std::ops::Bound;
@@ -368,8 +368,6 @@ impl server::Types for Rustc<'_, '_> {
     type FreeFunctions = FreeFunctions;
     type TokenStream = TokenStream;
     type SourceFile = Lrc<SourceFile>;
-    type MultiSpan = Vec<Span>;
-    type Diagnostic = Diagnostic;
     type Span = Span;
     type Symbol = Symbol;
 }
@@ -436,6 +434,21 @@ fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symb
             span: self.call_site,
         })
     }
+
+    fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Self::Span>) {
+        let mut diag =
+            rustc_errors::Diagnostic::new(diagnostic.level.to_internal(), diagnostic.message);
+        diag.set_span(MultiSpan::from_spans(diagnostic.spans));
+        for child in diagnostic.children {
+            diag.sub(
+                child.level.to_internal(),
+                child.message,
+                MultiSpan::from_spans(child.spans),
+                None,
+            );
+        }
+        self.sess().span_diagnostic.emit_diagnostic(&mut diag);
+    }
 }
 
 impl server::TokenStream for Rustc<'_, '_> {
@@ -583,38 +596,6 @@ fn is_real(&mut self, file: &Self::SourceFile) -> bool {
     }
 }
 
-impl server::MultiSpan for Rustc<'_, '_> {
-    fn new(&mut self) -> Self::MultiSpan {
-        vec![]
-    }
-
-    fn push(&mut self, spans: &mut Self::MultiSpan, span: Self::Span) {
-        spans.push(span)
-    }
-}
-
-impl server::Diagnostic for Rustc<'_, '_> {
-    fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
-        let mut diag = Diagnostic::new(level.to_internal(), msg);
-        diag.set_span(MultiSpan::from_spans(spans));
-        diag
-    }
-
-    fn sub(
-        &mut self,
-        diag: &mut Self::Diagnostic,
-        level: Level,
-        msg: &str,
-        spans: Self::MultiSpan,
-    ) {
-        diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None);
-    }
-
-    fn emit(&mut self, mut diag: Self::Diagnostic) {
-        self.sess().span_diagnostic.emit_diagnostic(&mut diag);
-    }
-}
-
 impl server::Span for Rustc<'_, '_> {
     fn debug(&mut self, span: Self::Span) -> String {
         if self.ecx.ecfg.span_debug {
index c2c551e78a41105af9ee32fe96fbabbd750a3b7e..d85ac960f9b2f10b415cb0a43d2eaa7de8bfc102 100644 (file)
@@ -15,7 +15,6 @@
 
 use std::fmt::{self, Write};
 use std::hash::Hash;
-use tracing::debug;
 
 /// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa.
 /// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey`
index 092029ef09ec80e6cab087b6b63838e94ed25f4e..1b33cb9c2da9cbcebbe4be41ca0bd6d1d7665e08 100644 (file)
@@ -17,6 +17,9 @@
 #[macro_use]
 extern crate rustc_macros;
 
+#[macro_use]
+extern crate tracing;
+
 #[macro_use]
 extern crate rustc_data_structures;
 
index 8bf1de34a9b5cace73b66a13e3f888ebafc6fbb9..d4350aa5734dee4aefe5583a5f1e6233d0cf2303 100644 (file)
@@ -391,7 +391,7 @@ pub fn instantiate(
     /// Preconditions:
     ///
     /// - `for_vid` is a "root vid"
-    #[instrument(skip(self), level = "trace")]
+    #[instrument(skip(self), level = "trace", ret)]
     fn generalize(
         &self,
         ty: Ty<'tcx>,
@@ -435,15 +435,8 @@ fn generalize(
             cache: SsoHashMap::new(),
         };
 
-        let ty = match generalize.relate(ty, ty) {
-            Ok(ty) => ty,
-            Err(e) => {
-                debug!(?e, "failure");
-                return Err(e);
-            }
-        };
+        let ty = generalize.relate(ty, ty)?;
         let needs_wf = generalize.needs_wf;
-        trace!(?ty, ?needs_wf, "success");
         Ok(Generalization { ty, needs_wf })
     }
 
@@ -499,6 +492,7 @@ struct Generalizer<'cx, 'tcx> {
 /// Result from a generalization operation. This includes
 /// not only the generalized type, but also a bool flag
 /// indicating whether further WF checks are needed.
+#[derive(Debug)]
 struct Generalization<'tcx> {
     ty: Ty<'tcx>,
 
@@ -856,10 +850,9 @@ fn binders<T>(
         Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
         debug_assert_eq!(t, _t);
-        debug!("ConstInferUnifier: t={:?}", t);
 
         match t.kind() {
             &ty::Infer(ty::TyVar(vid)) => {
@@ -883,12 +876,7 @@ fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
                             .borrow_mut()
                             .type_variables()
                             .new_var(self.for_universe, origin);
-                        let u = self.tcx().mk_ty_var(new_var_id);
-                        debug!(
-                            "ConstInferUnifier: replacing original vid={:?} with new={:?}",
-                            vid, u
-                        );
-                        Ok(u)
+                        Ok(self.tcx().mk_ty_var(new_var_id))
                     }
                 }
             }
@@ -932,14 +920,13 @@ fn regions(
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn consts(
         &mut self,
         c: ty::Const<'tcx>,
         _c: ty::Const<'tcx>,
     ) -> RelateResult<'tcx, ty::Const<'tcx>> {
         debug_assert_eq!(c, _c);
-        debug!("ConstInferUnifier: c={:?}", c);
 
         match c.kind() {
             ty::ConstKind::Infer(InferConst::Var(vid)) => {
index 3a6119a627368fb18da0c9e7f4638546ce339428..6dad9873d613404a124bac052236be887ecdb7f0 100644 (file)
@@ -1434,7 +1434,7 @@ fn lifetime_display(lifetime: Region<'_>) -> String {
     /// the message in `secondary_span` as the primary label, and apply the message that would
     /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on
     /// E0271, like `src/test/ui/issues/issue-39970.stderr`.
-    #[tracing::instrument(
+    #[instrument(
         level = "debug",
         skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label)
     )]
index e990fe7ecb50427edb13538bd0a4a0b985cf7fa0..91a05367eee0242565e6cda3d03e4731e55d2a1a 100644 (file)
@@ -199,7 +199,7 @@ fn ty_to_string<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String {
 }
 
 /// We don't want to directly use `ty_to_string` for closures as their type isn't really
-/// something users are familar with. Directly printing the `fn_sig` of closures also
+/// something users are familiar with. Directly printing the `fn_sig` of closures also
 /// doesn't work as they actually use the "rust-call" API.
 fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String {
     let ty::Closure(_, substs) = ty.kind() else { unreachable!() };
index d0d9efe152cc04109799f78aa8df246f6db4a191..67426fcf0feda6c5a7664772c7dfffd8cd941ae1 100644 (file)
@@ -69,7 +69,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// For more details visit the relevant sections of the [rustc dev guide].
     ///
     /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
-    #[instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T
     where
         T: TypeFoldable<'tcx> + Copy,
@@ -104,9 +104,8 @@ pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T
             },
         };
 
-        let result = self.tcx.replace_bound_vars_uncached(binder, delegate);
-        debug!(?next_universe, ?result);
-        result
+        debug!(?next_universe);
+        self.tcx.replace_bound_vars_uncached(binder, delegate)
     }
 
     /// See [RegionConstraintCollector::leak_check][1].
index 3783cfb4cc5c85c85aaf53e674bdfb7916a95ba8..13b7e8eb9643611e0a5678f9b936250e347ac216 100644 (file)
@@ -333,9 +333,9 @@ fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool {
     ///
     /// Neither `a` nor `b` may be an inference variable (hence the
     /// term "concrete regions").
-    #[instrument(level = "trace", skip(self))]
+    #[instrument(level = "trace", skip(self), ret)]
     fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
-        let r = match (*a, *b) {
+        match (*a, *b) {
             (ReLateBound(..), _) | (_, ReLateBound(..)) | (ReErased, _) | (_, ReErased) => {
                 bug!("cannot relate region: LUB({:?}, {:?})", a, b);
             }
@@ -399,11 +399,7 @@ fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx>
                     self.tcx().lifetimes.re_static
                 }
             }
-        };
-
-        debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);
-
-        r
+        }
     }
 
     /// After expansion is complete, go and check upper bounds (i.e.,
index 60ebf8b949d26a6ae72868d758b7de75931a4ff4..fe037a458a7f85106d756c92aef897a779552738 100644 (file)
@@ -1333,7 +1333,7 @@ pub fn resolve_regions(
     /// `resolve_vars_if_possible` as well as `fully_resolve`.
     ///
     /// Make sure to call [`InferCtxt::process_registered_region_obligations`]
-    /// first, or preferrably use [`InferCtxt::check_region_obligations_and_report_errors`]
+    /// first, or preferably use [`InferCtxt::check_region_obligations_and_report_errors`]
     /// to do both of these operations together.
     pub fn resolve_regions_and_report_errors(
         &self,
index e7e93116a66d146c9d3b74ad4bb5cd79324cc06b..bb6f6ae60e26ac27c05ad3e59c889122e90d6ef3 100644 (file)
@@ -542,7 +542,7 @@ fn a_is_expected(&self) -> bool {
         true
     }
 
-    #[instrument(skip(self, info), level = "trace")]
+    #[instrument(skip(self, info), level = "trace", ret)]
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
@@ -560,8 +560,6 @@ fn relate_with_variance<T: Relate<'tcx>>(
 
         self.ambient_variance = old_ambient_variance;
 
-        debug!(?r);
-
         Ok(r)
     }
 
index 233a5004a3931f53444eaf75538edc8f89d65e6a..d45adf43abfcc0040f0feeb235604eff4cc422a9 100644 (file)
@@ -390,7 +390,7 @@ pub fn register_member_constraints(
         });
     }
 
-    #[instrument(skip(self), level = "trace")]
+    #[instrument(skip(self), level = "trace", ret)]
     pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> {
         let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
         let parent_def_id = match self.defining_use_anchor {
@@ -421,16 +421,14 @@ pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<Opaqu
         in_definition_scope.then_some(*origin)
     }
 
-    #[instrument(skip(self), level = "trace")]
+    #[instrument(skip(self), level = "trace", ret)]
     fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin {
-        let origin = match self.tcx.hir().expect_item(def_id).kind {
+        match self.tcx.hir().expect_item(def_id).kind {
             hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin,
             ref itemkind => {
                 span_bug!(span, "weird opaque type: {:?}, {:#?}", def_id, itemkind)
             }
-        };
-        trace!(?origin);
-        origin
+        }
     }
 }
 
index fb12da0cc13f0aeb3661ff34d8c7781d92058d9f..4d124554afb94d3bb119275e9c415932dbc8131b 100644 (file)
@@ -29,7 +29,7 @@ pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'tcx>, idx: Option<OpaqueHidd
         }
     }
 
-    #[instrument(level = "debug")]
+    #[instrument(level = "debug", ret)]
     pub fn take_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> {
         std::mem::take(&mut self.opaque_types)
     }
index 2a085288fb7c069c8d58d8a776b9e758d0b4078c..2d19d1823fdfc2b04816cde419dd59aa3361009b 100644 (file)
@@ -9,7 +9,7 @@
 use rustc_middle::traits::query::OutlivesBound;
 use rustc_middle::ty;
 
-#[instrument(level = "debug", skip(param_env))]
+#[instrument(level = "debug", skip(param_env), ret)]
 pub fn explicit_outlives_bounds<'tcx>(
     param_env: ty::ParamEnv<'tcx>,
 ) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
index fe78890ff6ed7648b309d0b095b284b25c0dd4b3..74c8bd88d275dbfb79589c41d21eab3bd91d169b 100644 (file)
@@ -313,7 +313,7 @@ fn param_ty_must_outlive(
         self.delegate.push_verify(origin, generic, region, verify_bound);
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn projection_must_outlive(
         &mut self,
         origin: infer::SubregionOrigin<'tcx>,
index be03c8b5bae98a507cec3b8581bd874cea47338c..a5c21f0fb9b50eadf775aac2a6840500f388a00d 100644 (file)
@@ -34,7 +34,7 @@
 /// like are used. This is a particular challenge since this function is invoked
 /// very late in inference and hence cannot make use of the normal inference
 /// machinery.
-#[tracing::instrument(level = "debug", skip(tcx, param_env))]
+#[instrument(level = "debug", skip(tcx, param_env))]
 pub fn extract_verify_if_eq<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -71,7 +71,7 @@ pub fn extract_verify_if_eq<'tcx>(
 }
 
 /// True if a (potentially higher-ranked) outlives
-#[tracing::instrument(level = "debug", skip(tcx, param_env))]
+#[instrument(level = "debug", skip(tcx, param_env))]
 pub(super) fn can_match_erased_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -110,7 +110,7 @@ fn no_match<T>(&self) -> RelateResult<'tcx, T> {
 
     /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern
     /// is already bound to a different value.
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn bind(
         &mut self,
         br: ty::BoundRegion,
index 74a26ebc39f817c0673c1e86e9595312c4d9d8b1..611961ab1cc94afb98501fb1b205e4502af4199a 100644 (file)
@@ -100,7 +100,7 @@ fn default() -> Self {
 }
 
 /// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any
-/// action that is convertable into an UndoLog (per the From impls above).
+/// action that is convertible into an UndoLog (per the From impls above).
 impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx>
 where
     UndoLog<'tcx>: From<T>,
index dc4799e4afce14a373336a7c44ca9212c6cf15fd..949bd02ad6839a8f1535e453efc2d08d1a704dcf 100644 (file)
@@ -176,7 +176,7 @@ macro_rules! error {
                                         let ident = arg.ident().expect("multi-segment cfg key");
                                         names_valid.insert(ident.name.to_string());
                                     } else {
-                                        error!("`names()` arguments must be simple identifers");
+                                        error!("`names()` arguments must be simple identifiers");
                                     }
                                 }
                                 continue 'specs;
@@ -204,7 +204,7 @@ macro_rules! error {
                                         continue 'specs;
                                     } else {
                                         error!(
-                                            "`values()` first argument must be a simple identifer"
+                                            "`values()` first argument must be a simple identifier"
                                         );
                                     }
                                 } else if args.is_empty() {
@@ -332,7 +332,7 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R
 // JUSTIFICATION: before session exists, only config
 #[allow(rustc::bad_opt_access)]
 pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
-    tracing::trace!("run_compiler");
+    trace!("run_compiler");
     util::run_in_thread_pool_with_globals(
         config.opts.edition,
         config.opts.unstable_opts.threads,
index 258e38c3bdb9e09197fc006865defaa66903149a..1a8d619fafb6212483b49ab045f40f6e07af901f 100644 (file)
@@ -8,6 +8,9 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
+#[macro_use]
+extern crate tracing;
+
 mod callbacks;
 mod errors;
 pub mod interface;
index 66c6a229b89e4564b4e9130f3191eded516adcf7..f8b40949e2ed993ab2b2c2917d251bd94c824ba7 100644 (file)
@@ -38,7 +38,6 @@
 use rustc_span::FileName;
 use rustc_trait_selection::traits;
 use rustc_typeck as typeck;
-use tracing::{info, warn};
 
 use std::any::Any;
 use std::cell::RefCell;
@@ -165,7 +164,7 @@ pub fn create_resolver(
     krate: &ast::Crate,
     crate_name: &str,
 ) -> BoxedResolver {
-    tracing::trace!("create_resolver");
+    trace!("create_resolver");
     BoxedResolver::new(sess, move |sess, resolver_arenas| {
         Resolver::new(sess, krate, crate_name, metadata_loader, resolver_arenas)
     })
@@ -279,7 +278,7 @@ pub fn configure_and_expand(
     crate_name: &str,
     resolver: &mut Resolver<'_>,
 ) -> Result<ast::Crate> {
-    tracing::trace!("configure_and_expand");
+    trace!("configure_and_expand");
     pre_expansion_lint(sess, lint_store, resolver.registered_tools(), &krate, crate_name);
     rustc_builtin_macros::register_builtin_macros(resolver);
 
index 65fa8d7495a4bf16cc76277a97bf5593e3e29fd9..6c725a01b53151ccfb9325f876442a22920facbf 100644 (file)
@@ -166,7 +166,7 @@ pub fn crate_name(&self) -> Result<&Query<String>> {
     pub fn expansion(
         &self,
     ) -> Result<&Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> {
-        tracing::trace!("expansion");
+        trace!("expansion");
         self.expansion.compute(|| {
             let crate_name = self.crate_name()?.peek().clone();
             let (krate, lint_store) = self.register_plugins()?.take();
index e74978485a21c818e780d1d3a923593a73de7b8c..f7e70d355cf86e6ff3ad519311676536ed3a6e24 100644 (file)
@@ -1,3 +1,4 @@
+use info;
 use libloading::Library;
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
@@ -31,7 +32,6 @@
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::OnceLock;
 use std::thread;
-use tracing::info;
 
 /// Function pointer type that constructs a new CodegenBackend.
 pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
index 868555a72b0d1284f94982c90cc8dcd600a9f486..4e9c209a58400aa176d13cf336cf7c01e1cc4a86 100644 (file)
@@ -59,7 +59,6 @@
 use crate::nonstandard_style::{method_context, MethodLateContext};
 
 use std::fmt::Write;
-use tracing::{debug, trace};
 
 // hardwired lints from librustc_middle
 pub use rustc_session::lint::builtin::*;
@@ -3173,3 +3172,81 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
         }
     }
 }
+
+declare_lint! {
+    /// The `special_module_name` lint detects module
+    /// declarations for files that have a special meaning.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// mod lib;
+    ///
+    /// fn main() {
+    ///     lib::run();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Cargo recognizes `lib.rs` and `main.rs` as the root of a
+    /// library or binary crate, so declaring them as modules
+    /// will lead to miscompilation of the crate unless configured
+    /// explicitly.
+    ///
+    /// To access a library from a binary target within the same crate,
+    /// use `your_crate_name::` as the path path instead of `lib::`:
+    ///
+    /// ```rust,compile_fail
+    /// // bar/src/lib.rs
+    /// fn run() {
+    ///     // ...
+    /// }
+    ///
+    /// // bar/src/main.rs
+    /// fn main() {
+    ///     bar::run();
+    /// }
+    /// ```
+    ///
+    /// Binary targets cannot be used as libraries and so declaring
+    /// one as a module is not allowed.
+    pub SPECIAL_MODULE_NAME,
+    Warn,
+    "module declarations for files with a special meaning",
+}
+
+declare_lint_pass!(SpecialModuleName => [SPECIAL_MODULE_NAME]);
+
+impl EarlyLintPass for SpecialModuleName {
+    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
+        for item in &krate.items {
+            if let ast::ItemKind::Mod(
+                _,
+                ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _),
+            ) = item.kind
+            {
+                if item.attrs.iter().any(|a| a.has_name(sym::path)) {
+                    continue;
+                }
+
+                match item.ident.name.as_str() {
+                    "lib" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, |lint| {
+                        lint.build("found module declaration for lib.rs")
+                            .note("lib.rs is the root of this crate's library target")
+                            .help("to refer to it from other targets, use the library's name as the path")
+                            .emit()
+                    }),
+                    "main" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, |lint| {
+                        lint.build("found module declaration for main.rs")
+                            .note("a binary crate cannot be used as library")
+                            .emit()
+                    }),
+                    _ => continue
+                }
+            }
+        }
+    }
+}
index 002bba4759be820ace8e656cd52a36f9fe52edca..e3b6c0159870072522a93ce2b2a1477e8dd12198 100644 (file)
@@ -45,7 +45,6 @@
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{BytePos, Span};
 use rustc_target::abi;
-use tracing::debug;
 
 use std::cell::Cell;
 use std::iter;
@@ -417,7 +416,7 @@ pub fn check_lint_name(
                     None => {
                         // 1. The tool is currently running, so this lint really doesn't exist.
                         // FIXME: should this handle tools that never register a lint, like rustfmt?
-                        tracing::debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
+                        debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
                         let tool_prefix = format!("{}::", tool_name);
                         return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
                             self.no_lint_suggestion(&complete_name)
@@ -510,7 +509,7 @@ fn check_tool_name_for_backwards_compat(
                 CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
             }
             Some(other) => {
-                tracing::debug!("got renamed lint {:?}", other);
+                debug!("got renamed lint {:?}", other);
                 CheckLintNameResult::NoLint(None)
             }
         }
index cdb5b3c4284a8d37afc0dd2f6bb7b4f8c3006937..27d173ebde82a506d7c1f8493352b137b3fee634 100644 (file)
@@ -26,7 +26,6 @@
 use rustc_span::Span;
 
 use std::slice;
-use tracing::debug;
 
 macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({
     $cx.pass.$f(&$cx.context, $($args),*);
index c26d7824758ef06f11b40692b10ff2a2d367ea49..16b7d2cbbaea73ac489f4b4730f76c4eca98a96c 100644 (file)
@@ -12,7 +12,6 @@
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
-use tracing::debug;
 
 declare_tool_lint! {
     pub rustc::DEFAULT_HASH_TYPES,
@@ -393,8 +392,14 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
             return;
         }
 
+        let mut found_parent_with_attr = false;
         let mut found_impl = false;
-        for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+        for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+            if let Some(owner_did) = hir_id.as_owner() {
+                found_parent_with_attr = found_parent_with_attr
+                    || cx.tcx.has_attr(owner_did.to_def_id(), sym::rustc_lint_diagnostics);
+            }
+
             debug!(?parent);
             if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent &&
                 let Impl { of_trait: Some(of_trait), .. } = impl_ &&
@@ -407,7 +412,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
             }
         }
         debug!(?found_impl);
-        if !found_impl {
+        if !found_parent_with_attr && !found_impl {
             cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
                 lint.build(fluent::lint::diag_out_of_impl).emit();
             })
@@ -425,7 +430,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
             }
         }
         debug!(?found_diagnostic_message);
-        if !found_diagnostic_message {
+        if !found_parent_with_attr && !found_diagnostic_message {
             cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
                 lint.build(fluent::lint::untranslatable_diag).emit();
             })
index 5188ac633d3928b9bcf56cc63bcab58bd76ab4da..8a336844dc2fac73f02f7c0fb003ae23ac77181c 100644 (file)
@@ -29,7 +29,6 @@
 use std::any::Any;
 use std::cell::Cell;
 use std::slice;
-use tracing::debug;
 
 /// Extract the `LintStore` from the query context.
 /// This function exists because we've erased `LintStore` as `dyn Any` in the context.
index 89409b58f88b90af3d5762e537c33ac986f7c131..f1d8ef2e47d31cb74d2f37a3056d1af66766f1e8 100644 (file)
@@ -21,7 +21,6 @@
 use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
-use tracing::debug;
 
 use crate::errors::{
     MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
index c3065e4a2d9383c13d7bb147f2540c5b6d3efef4..3478be1ed5d92c4c5bc4e1c9830b777e3ae65200 100644 (file)
@@ -42,6 +42,8 @@
 extern crate rustc_middle;
 #[macro_use]
 extern crate rustc_session;
+#[macro_use]
+extern crate tracing;
 
 mod array_into_iter;
 pub mod builtin;
@@ -131,6 +133,7 @@ macro_rules! early_lint_passes {
                 UnusedBraces: UnusedBraces,
                 UnusedImportBraces: UnusedImportBraces,
                 UnsafeCode: UnsafeCode,
+                SpecialModuleName: SpecialModuleName,
                 AnonymousParameters: AnonymousParameters,
                 EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
                 NonCamelCaseTypes: NonCamelCaseTypes,
index 484e541afc5879c477a6fefc674ba50bd642e08a..0316651998129037219b6bc7c9e2ac9d4e6adaf2 100644 (file)
@@ -19,7 +19,6 @@
 use std::cmp;
 use std::iter;
 use std::ops::ControlFlow;
-use tracing::debug;
 
 declare_lint! {
     /// The `unused_comparisons` lint detects comparisons made useless by
index 58b2f0a44161c273b62d6079aebfdd70acc55e00..1f4e5b480917d556c3245beaef3e9f82d831bd8d 100644 (file)
@@ -268,7 +268,7 @@ fn check_must_use_ty<'tcx>(
                 },
                 ty::Closure(..) => {
                     cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
-                        // FIXME(davidtwco): this isn't properly translatable becauses of the
+                        // FIXME(davidtwco): this isn't properly translatable because of the
                         // pre/post strings
                         lint.build(fluent::lint::unused_closure)
                             .set_arg("count", plural_len)
@@ -281,7 +281,7 @@ fn check_must_use_ty<'tcx>(
                 }
                 ty::Generator(..) => {
                     cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
-                        // FIXME(davidtwco): this isn't properly translatable becauses of the
+                        // FIXME(davidtwco): this isn't properly translatable because of the
                         // pre/post strings
                         lint.build(fluent::lint::unused_generator)
                             .set_arg("count", plural_len)
@@ -310,7 +310,7 @@ fn check_must_use_def(
         ) -> bool {
             if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) {
                 cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
-                    // FIXME(davidtwco): this isn't properly translatable becauses of the pre/post
+                    // FIXME(davidtwco): this isn't properly translatable because of the pre/post
                     // strings
                     let mut err = lint.build(fluent::lint::unused_def);
                     err.set_arg("pre", descr_pre_path);
index 2dca6acdd6d6fb562331476c6bec93a59df7f847..845563338eaa992c91b1f2178783800da41c0b5e 100644 (file)
     /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
     Warn,
-    "tranparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
+    "transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
     @future_incompatible = FutureIncompatibleInfo {
         reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>",
     };
index a4ccfcace1923e6bc81cc18519649c75c407eb59..2a4fe48a8aca28de4c5875ace0fb3f0f06051133 100644 (file)
@@ -239,7 +239,7 @@ fn generate_structure_code_for_attr(
             }
         }
 
-        Ok(tokens.drain(..).collect())
+        Ok(tokens.into_iter().collect())
     }
 
     fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
index 8b40e295bd8a7aa33c4e5a52034d61adb2065cbc..c1b82abc1e064a8da1b8dd62e3610232f7722393 100644 (file)
@@ -12,7 +12,7 @@
 use std::collections::HashMap;
 use std::fmt;
 use std::str::FromStr;
-use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
+use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path};
 use synstructure::{BindingInfo, Structure, VariantInfo};
 
 /// Which kind of suggestion is being created?
@@ -28,41 +28,8 @@ enum SubdiagnosticSuggestionKind {
     Verbose,
 }
 
-impl FromStr for SubdiagnosticSuggestionKind {
-    type Err = ();
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "" => Ok(SubdiagnosticSuggestionKind::Normal),
-            "_short" => Ok(SubdiagnosticSuggestionKind::Short),
-            "_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden),
-            "_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose),
-            _ => Err(()),
-        }
-    }
-}
-
-impl SubdiagnosticSuggestionKind {
-    pub fn to_suggestion_style(&self) -> TokenStream {
-        match self {
-            SubdiagnosticSuggestionKind::Normal => {
-                quote! { rustc_errors::SuggestionStyle::ShowCode }
-            }
-            SubdiagnosticSuggestionKind::Short => {
-                quote! { rustc_errors::SuggestionStyle::HideCodeInline }
-            }
-            SubdiagnosticSuggestionKind::Hidden => {
-                quote! { rustc_errors::SuggestionStyle::HideCodeAlways }
-            }
-            SubdiagnosticSuggestionKind::Verbose => {
-                quote! { rustc_errors::SuggestionStyle::ShowAlways }
-            }
-        }
-    }
-}
-
 /// Which kind of subdiagnostic is being created from a variant?
-#[derive(Clone)]
+#[derive(Clone, Copy)]
 enum SubdiagnosticKind {
     /// `#[label(...)]`
     Label,
@@ -73,9 +40,31 @@ enum SubdiagnosticKind {
     /// `#[warning(...)]`
     Warn,
     /// `#[suggestion{,_short,_hidden,_verbose}]`
-    Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream },
-    /// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
-    MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind },
+    Suggestion(SubdiagnosticSuggestionKind),
+}
+
+impl FromStr for SubdiagnosticKind {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "label" => Ok(SubdiagnosticKind::Label),
+            "note" => Ok(SubdiagnosticKind::Note),
+            "help" => Ok(SubdiagnosticKind::Help),
+            "warning" => Ok(SubdiagnosticKind::Warn),
+            "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
+            "suggestion_short" => {
+                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
+            }
+            "suggestion_hidden" => {
+                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden))
+            }
+            "suggestion_verbose" => {
+                Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose))
+            }
+            _ => Err(()),
+        }
+    }
 }
 
 impl quote::IdentFragment for SubdiagnosticKind {
@@ -85,9 +74,17 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             SubdiagnosticKind::Note => write!(f, "note"),
             SubdiagnosticKind::Help => write!(f, "help"),
             SubdiagnosticKind::Warn => write!(f, "warn"),
-            SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"),
-            SubdiagnosticKind::MultipartSuggestion { .. } => {
-                write!(f, "multipart_suggestion_with_style")
+            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
+                write!(f, "suggestion")
+            }
+            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => {
+                write!(f, "suggestion_short")
+            }
+            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => {
+                write!(f, "suggestion_hidden")
+            }
+            SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => {
+                write!(f, "suggestion_verbose")
             }
         }
     }
@@ -151,9 +148,11 @@ pub(crate) fn into_tokens(self) -> TokenStream {
                     variant,
                     span,
                     fields: fields_map,
+                    kinds: Vec::new(),
+                    slugs: Vec::new(),
+                    code: None,
                     span_field: None,
                     applicability: None,
-                    has_suggestion_parts: false,
                 };
                 builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
             });
@@ -194,15 +193,21 @@ struct SessionSubdiagnosticDeriveBuilder<'a> {
     /// derive builder.
     fields: HashMap<String, TokenStream>,
 
+    /// Subdiagnostic kind of the type/variant.
+    kinds: Vec<(SubdiagnosticKind, proc_macro::Span)>,
+
+    /// Slugs of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
+    /// `#[kind(slug)]` attribute on the type or variant.
+    slugs: Vec<(Path, proc_macro::Span)>,
+    /// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
+    /// attribute on the type or variant.
+    code: Option<(TokenStream, proc_macro::Span)>,
+
     /// Identifier for the binding to the `#[primary_span]` field.
     span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
     /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
     /// `rustc_errors::Applicability::*` variant directly.
     applicability: Option<(TokenStream, proc_macro::Span)>,
-
-    /// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
-    /// during finalization if still `false`.
-    has_suggestion_parts: bool,
 }
 
 impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
@@ -212,133 +217,124 @@ fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
 }
 
 impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
-    fn identify_kind(
-        &mut self,
-    ) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
-        let mut kind_slug = None;
-
-        for attr in self.variant.ast().attrs {
+    fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> {
+        for (i, attr) in self.variant.ast().attrs.iter().enumerate() {
             let span = attr.span().unwrap();
 
             let name = attr.path.segments.last().unwrap().ident.to_string();
             let name = name.as_str();
 
             let meta = attr.parse_meta()?;
-            let Meta::List(MetaList { ref nested, .. }) = meta else {
-                throw_invalid_attr!(attr, &meta);
-            };
-
-            let mut kind = match name {
-                "label" => SubdiagnosticKind::Label,
-                "note" => SubdiagnosticKind::Note,
-                "help" => SubdiagnosticKind::Help,
-                "warning" => SubdiagnosticKind::Warn,
-                _ => {
-                    if let Some(suggestion_kind) =
-                        name.strip_prefix("suggestion").and_then(|s| s.parse().ok())
-                    {
-                        SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() }
-                    } else if let Some(suggestion_kind) =
-                        name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok())
-                    {
-                        SubdiagnosticKind::MultipartSuggestion { suggestion_kind }
-                    } else {
-                        throw_invalid_attr!(attr, &meta);
+            let kind = match meta {
+                Meta::List(MetaList { ref nested, .. }) => {
+                    let mut nested_iter = nested.into_iter();
+                    if let Some(nested_attr) = nested_iter.next() {
+                        match nested_attr {
+                            NestedMeta::Meta(Meta::Path(path)) => {
+                                self.slugs.push((path.clone(), span));
+                            }
+                            NestedMeta::Meta(meta @ Meta::NameValue(_))
+                                if matches!(
+                                    meta.path().segments.last().unwrap().ident.to_string().as_str(),
+                                    "code" | "applicability"
+                                ) =>
+                            {
+                                // don't error for valid follow-up attributes
+                            }
+                            nested_attr => {
+                                throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                                    diag.help(
+                                        "first argument of the attribute should be the diagnostic \
+                                         slug",
+                                    )
+                                })
+                            }
+                        };
                     }
-                }
-            };
 
-            let mut slug = None;
-            let mut code = None;
-
-            let mut nested_iter = nested.into_iter();
-            if let Some(nested_attr) = nested_iter.next() {
-                match nested_attr {
-                    NestedMeta::Meta(Meta::Path(path)) => {
-                        slug.set_once((path.clone(), span));
-                    }
-                    NestedMeta::Meta(meta @ Meta::NameValue(_))
-                        if matches!(
-                            meta.path().segments.last().unwrap().ident.to_string().as_str(),
-                            "code" | "applicability"
-                        ) =>
-                    {
-                        // Don't error for valid follow-up attributes.
-                    }
-                    nested_attr => {
-                        throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                            diag.help(
-                                "first argument of the attribute should be the diagnostic \
-                                 slug",
-                            )
-                        })
+                    for nested_attr in nested_iter {
+                        let meta = match nested_attr {
+                            NestedMeta::Meta(ref meta) => meta,
+                            _ => throw_invalid_nested_attr!(attr, &nested_attr),
+                        };
+
+                        let span = meta.span().unwrap();
+                        let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+                        let nested_name = nested_name.as_str();
+
+                        match meta {
+                            Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                                match nested_name {
+                                    "code" => {
+                                        let formatted_str = self.build_format(&s.value(), s.span());
+                                        self.code.set_once((formatted_str, span));
+                                    }
+                                    "applicability" => {
+                                        let value = match Applicability::from_str(&s.value()) {
+                                            Ok(v) => v,
+                                            Err(()) => {
+                                                span_err(span, "invalid applicability").emit();
+                                                Applicability::Unspecified
+                                            }
+                                        };
+                                        self.applicability.set_once((quote! { #value }, span));
+                                    }
+                                    _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                                        diag.help(
+                                            "only `code` and `applicability` are valid nested \
+                                             attributes",
+                                        )
+                                    }),
+                                }
+                            }
+                            _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+                                if matches!(meta, Meta::Path(_)) {
+                                    diag.help(
+                                        "a diagnostic slug must be the first argument to the \
+                                         attribute",
+                                    )
+                                } else {
+                                    diag
+                                }
+                            }),
+                        }
                     }
-                };
-            }
 
-            for nested_attr in nested_iter {
-                let meta = match nested_attr {
-                    NestedMeta::Meta(ref meta) => meta,
-                    _ => throw_invalid_nested_attr!(attr, &nested_attr),
-                };
+                    let Ok(kind) = SubdiagnosticKind::from_str(name) else {
+                        throw_invalid_attr!(attr, &meta)
+                    };
 
-                let span = meta.span().unwrap();
-                let nested_name = meta.path().segments.last().unwrap().ident.to_string();
-                let nested_name = nested_name.as_str();
+                    kind
+                }
+                _ => throw_invalid_attr!(attr, &meta),
+            };
 
-                let value = match meta {
-                    Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value,
-                    Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                        diag.help("a diagnostic slug must be the first argument to the attribute")
-                    }),
-                    _ => throw_invalid_nested_attr!(attr, &nested_attr),
-                };
+            if matches!(
+                kind,
+                SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
+            ) && self.code.is_some()
+            {
+                throw_span_err!(
+                    span,
+                    &format!("`code` is not a valid nested attribute of a `{}` attribute", name)
+                );
+            }
 
-                match nested_name {
-                    "code" => {
-                        if matches!(kind, SubdiagnosticKind::Suggestion { .. }) {
-                            let formatted_str = self.build_format(&value.value(), value.span());
-                            code.set_once((formatted_str, span));
-                        } else {
-                            span_err(
-                                span,
-                                &format!(
-                                    "`code` is not a valid nested attribute of a `{}` attribute",
-                                    name
-                                ),
-                            )
-                            .emit();
-                        }
-                    }
-                    "applicability" => {
-                        if matches!(
-                            kind,
-                            SubdiagnosticKind::Suggestion { .. }
-                                | SubdiagnosticKind::MultipartSuggestion { .. }
-                        ) {
-                            let value =
-                                Applicability::from_str(&value.value()).unwrap_or_else(|()| {
-                                    span_err(span, "invalid applicability").emit();
-                                    Applicability::Unspecified
-                                });
-                            self.applicability.set_once((quote! { #value }, span));
-                        } else {
-                            span_err(
-                                span,
-                                &format!(
-                                    "`applicability` is not a valid nested attribute of a `{}` attribute",
-                                    name
-                                )
-                            ).emit();
-                        }
-                    }
-                    _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                        diag.help("only `code` and `applicability` are valid nested attributes")
-                    }),
-                }
+            if matches!(
+                kind,
+                SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
+            ) && self.applicability.is_some()
+            {
+                throw_span_err!(
+                    span,
+                    &format!(
+                        "`applicability` is not a valid nested attribute of a `{}` attribute",
+                        name
+                    )
+                );
             }
 
-            let Some((slug, _)) = slug else {
+            if self.slugs.len() != i + 1 {
                 throw_span_err!(
                     span,
                     &format!(
@@ -346,338 +342,146 @@ fn identify_kind(
                         name
                     )
                 );
-            };
-
-            match kind {
-                SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => {
-                    let Some((code, _)) = code else {
-                        throw_span_err!(span, "suggestion without `code = \"...\"`");
-                    };
-                    *code_field = code;
-                }
-                SubdiagnosticKind::Label
-                | SubdiagnosticKind::Note
-                | SubdiagnosticKind::Help
-                | SubdiagnosticKind::Warn
-                | SubdiagnosticKind::MultipartSuggestion { .. } => {}
             }
 
-            kind_slug.set_once(((kind, slug), span))
+            self.kinds.push((kind, span));
         }
 
-        Ok(kind_slug.map(|(kind_slug, _)| kind_slug))
-    }
-
-    /// Generates the code for a field with no attributes.
-    fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream {
-        let ast = binding.ast();
-        assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
-
-        let diag = &self.diag;
-        let ident = ast.ident.as_ref().unwrap();
-        quote! {
-            #diag.set_arg(
-                stringify!(#ident),
-                #binding
-            );
-        }
+        Ok(())
     }
 
-    /// Generates the necessary code for all attributes on a field.
-    fn generate_field_attr_code(
+    fn generate_field_code(
         &mut self,
         binding: &BindingInfo<'_>,
-        kind: &SubdiagnosticKind,
-    ) -> TokenStream {
+        have_suggestion: bool,
+    ) -> Result<TokenStream, DiagnosticDeriveError> {
         let ast = binding.ast();
-        assert!(ast.attrs.len() > 0, "field without attributes generating attr code");
 
-        // Abstract over `Vec<T>` and `Option<T>` fields using `FieldInnerTy`, which will
-        // apply the generated code on each element in the `Vec` or `Option`.
         let inner_ty = FieldInnerTy::from_type(&ast.ty);
-        ast.attrs
-            .iter()
-            .map(|attr| {
-                let info = FieldInfo {
-                    binding,
-                    ty: inner_ty.inner_type().unwrap_or(&ast.ty),
-                    span: &ast.span(),
-                };
-
-                let generated = self
-                    .generate_field_code_inner(kind, attr, info)
-                    .unwrap_or_else(|v| v.to_compile_error());
-
-                inner_ty.with(binding, generated)
-            })
-            .collect()
-    }
-
-    fn generate_field_code_inner(
-        &mut self,
-        kind: &SubdiagnosticKind,
-        attr: &Attribute,
-        info: FieldInfo<'_>,
-    ) -> Result<TokenStream, DiagnosticDeriveError> {
-        let meta = attr.parse_meta()?;
-        match meta {
-            Meta::Path(path) => self.generate_field_code_inner_path(kind, attr, info, path),
-            Meta::List(list @ MetaList { .. }) => {
-                self.generate_field_code_inner_list(kind, attr, info, list)
-            }
-            _ => throw_invalid_attr!(attr, &meta),
-        }
-    }
-
-    /// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`).
-    fn generate_field_code_inner_path(
-        &mut self,
-        kind: &SubdiagnosticKind,
-        attr: &Attribute,
-        info: FieldInfo<'_>,
-        path: Path,
-    ) -> Result<TokenStream, DiagnosticDeriveError> {
-        let span = attr.span().unwrap();
-        let ident = &path.segments.last().unwrap().ident;
-        let name = ident.to_string();
-        let name = name.as_str();
-
-        match name {
-            "skip_arg" => Ok(quote! {}),
-            "primary_span" => {
-                if matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
-                    throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
-                        diag.help(
-                            "multipart suggestions use one or more `#[suggestion_part]`s rather \
-                            than one `#[primary_span]`",
-                        )
-                    })
-                }
-
-                report_error_if_not_applied_to_span(attr, &info)?;
+        let info = FieldInfo {
+            binding: binding,
+            ty: inner_ty.inner_type().unwrap_or(&ast.ty),
+            span: &ast.span(),
+        };
 
-                let binding = info.binding.binding.clone();
-                self.span_field.set_once((binding, span));
+        for attr in &ast.attrs {
+            let name = attr.path.segments.last().unwrap().ident.to_string();
+            let name = name.as_str();
+            let span = attr.span().unwrap();
 
-                Ok(quote! {})
-            }
-            "suggestion_part" => {
-                self.has_suggestion_parts = true;
-
-                match kind {
-                    SubdiagnosticKind::MultipartSuggestion { .. } => {
-                        span_err(
-                            span,
-                            "`#[suggestion_part(...)]` attribute without `code = \"...\"`",
-                        )
-                        .emit();
-                        Ok(quote! {})
+            let meta = attr.parse_meta()?;
+            match meta {
+                Meta::Path(_) => match name {
+                    "primary_span" => {
+                        report_error_if_not_applied_to_span(attr, &info)?;
+                        self.span_field.set_once((binding.binding.clone(), span));
+                        return Ok(quote! {});
                     }
-                    SubdiagnosticKind::Label
-                    | SubdiagnosticKind::Note
-                    | SubdiagnosticKind::Help
-                    | SubdiagnosticKind::Warn
-                    | SubdiagnosticKind::Suggestion { .. } => {
-                        throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
-                            diag.help(
-                                "`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead",
-                            )
-                        });
+                    "applicability" if have_suggestion => {
+                        report_error_if_not_applied_to_applicability(attr, &info)?;
+                        let binding = binding.binding.clone();
+                        self.applicability.set_once((quote! { #binding }, span));
+                        return Ok(quote! {});
                     }
-                }
-            }
-            "applicability" => {
-                if let SubdiagnosticKind::Suggestion { .. }
-                | SubdiagnosticKind::MultipartSuggestion { .. } = kind
-                {
-                    report_error_if_not_applied_to_applicability(attr, &info)?;
-
-                    let binding = info.binding.binding.clone();
-                    self.applicability.set_once((quote! { #binding }, span));
-                } else {
-                    span_err(span, "`#[applicability]` is only valid on suggestions").emit();
-                }
-
-                Ok(quote! {})
-            }
-            _ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
-                let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
-                    "suggestion_part"
-                } else {
-                    "primary_span"
-                };
-                diag.help(format!(
-                    "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
-                ))
-            }),
-        }
-    }
-
-    /// Generates the code for a `[Meta::List]`-like attribute on a field (e.g.
-    /// `#[suggestion_part(code = "...")]`).
-    fn generate_field_code_inner_list(
-        &mut self,
-        kind: &SubdiagnosticKind,
-        attr: &Attribute,
-        info: FieldInfo<'_>,
-        list: MetaList,
-    ) -> Result<TokenStream, DiagnosticDeriveError> {
-        let span = attr.span().unwrap();
-        let ident = &list.path.segments.last().unwrap().ident;
-        let name = ident.to_string();
-        let name = name.as_str();
-
-        match name {
-            "suggestion_part" => {
-                if !matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
-                    throw_invalid_attr!(attr, &Meta::List(list), |diag| {
+                    "applicability" => {
+                        span_err(span, "`#[applicability]` is only valid on suggestions").emit();
+                        return Ok(quote! {});
+                    }
+                    "skip_arg" => {
+                        return Ok(quote! {});
+                    }
+                    _ => throw_invalid_attr!(attr, &meta, |diag| {
                         diag.help(
-                            "`#[suggestion_part(...)]` is only valid in multipart suggestions",
+                            "only `primary_span`, `applicability` and `skip_arg` are valid field \
+                             attributes",
                         )
-                    })
-                }
-
-                self.has_suggestion_parts = true;
-
-                report_error_if_not_applied_to_span(attr, &info)?;
-
-                let mut code = None;
-                for nested_attr in list.nested.iter() {
-                    let NestedMeta::Meta(ref meta) = nested_attr else {
-                        throw_invalid_nested_attr!(attr, &nested_attr);
-                    };
-
-                    let span = meta.span().unwrap();
-                    let nested_name = meta.path().segments.last().unwrap().ident.to_string();
-                    let nested_name = nested_name.as_str();
-
-                    let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else {
-                        throw_invalid_nested_attr!(attr, &nested_attr);
-                    };
+                    }),
+                },
+                _ => throw_invalid_attr!(attr, &meta),
+            }
+        }
 
-                    match nested_name {
-                        "code" => {
-                            let formatted_str = self.build_format(&value.value(), value.span());
-                            code.set_once((formatted_str, span));
-                        }
-                        _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                            diag.help("`code` is the only valid nested attribute")
-                        }),
-                    }
-                }
+        let ident = ast.ident.as_ref().unwrap();
 
-                let Some((code, _)) = code else {
-                    span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
-                        .emit();
-                    return Ok(quote! {});
-                };
-                let binding = info.binding;
+        let diag = &self.diag;
+        let generated = quote! {
+            #diag.set_arg(
+                stringify!(#ident),
+                #binding
+            );
+        };
 
-                Ok(quote! { suggestions.push((#binding, #code)); })
-            }
-            _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
-                let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
-                    "suggestion_part"
-                } else {
-                    "primary_span"
-                };
-                diag.help(format!(
-                    "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
-                ))
-            }),
-        }
+        Ok(inner_ty.with(binding, generated))
     }
 
-    pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
-        let Some((kind, slug)) = self.identify_kind()? else {
+    fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
+        self.identify_kind()?;
+        if self.kinds.is_empty() {
             throw_span_err!(
                 self.variant.ast().ident.span().unwrap(),
                 "subdiagnostic kind not specified"
             );
         };
+        let have_suggestion =
+            self.kinds.iter().any(|(k, _)| matches!(k, SubdiagnosticKind::Suggestion(_)));
+        let mut args = TokenStream::new();
+        for binding in self.variant.bindings() {
+            let arg = self
+                .generate_field_code(binding, have_suggestion)
+                .unwrap_or_else(|v| v.to_compile_error());
+            args.extend(arg);
+        }
+        let mut tokens = TokenStream::new();
+        for ((kind, _), (slug, _)) in self.kinds.iter().zip(&self.slugs) {
+            let code = match self.code.as_ref() {
+                Some((code, _)) => Some(quote! { #code }),
+                None if have_suggestion => {
+                    span_err(self.span, "suggestion without `code = \"...\"`").emit();
+                    Some(quote! { /* macro error */ "..." })
+                }
+                None => None,
+            };
 
-        let init = match &kind {
-            SubdiagnosticKind::Label
-            | SubdiagnosticKind::Note
-            | SubdiagnosticKind::Help
-            | SubdiagnosticKind::Warn
-            | SubdiagnosticKind::Suggestion { .. } => quote! {},
-            SubdiagnosticKind::MultipartSuggestion { .. } => {
-                quote! { let mut suggestions = Vec::new(); }
-            }
-        };
-
-        let attr_args: TokenStream = self
-            .variant
-            .bindings()
-            .iter()
-            .filter(|binding| !binding.ast().attrs.is_empty())
-            .map(|binding| self.generate_field_attr_code(binding, &kind))
-            .collect();
-
-        let span_field = self.span_field.as_ref().map(|(span, _)| span);
-        let applicability = self.applicability.take().map_or_else(
-            || quote! { rustc_errors::Applicability::Unspecified },
-            |(applicability, _)| applicability,
-        );
+            let span_field = self.span_field.as_ref().map(|(span, _)| span);
+            let applicability = match self.applicability.clone() {
+                Some((applicability, _)) => Some(applicability),
+                None if have_suggestion => {
+                    span_err(self.span, "suggestion without `applicability`").emit();
+                    Some(quote! { rustc_errors::Applicability::Unspecified })
+                }
+                None => None,
+            };
 
-        let diag = &self.diag;
-        let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
-        let message = quote! { rustc_errors::fluent::#slug };
-        let call = match kind {
-            SubdiagnosticKind::Suggestion { suggestion_kind, code } => {
+            let diag = &self.diag;
+            let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
+            let message = quote! { rustc_errors::fluent::#slug };
+            let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
                 if let Some(span) = span_field {
-                    let style = suggestion_kind.to_suggestion_style();
-
-                    quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
+                    quote! { #diag.#name(#span, #message, #code, #applicability); }
                 } else {
                     span_err(self.span, "suggestion without `#[primary_span]` field").emit();
                     quote! { unreachable!(); }
                 }
-            }
-            SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => {
-                if !self.has_suggestion_parts {
-                    span_err(
-                        self.span,
-                        "multipart suggestion without any `#[suggestion_part(...)]` fields",
-                    )
-                    .emit();
-                }
-
-                let style = suggestion_kind.to_suggestion_style();
-
-                quote! { #diag.#name(#message, suggestions, #applicability, #style); }
-            }
-            SubdiagnosticKind::Label => {
+            } else if matches!(kind, SubdiagnosticKind::Label) {
                 if let Some(span) = span_field {
                     quote! { #diag.#name(#span, #message); }
                 } else {
                     span_err(self.span, "label without `#[primary_span]` field").emit();
                     quote! { unreachable!(); }
                 }
-            }
-            _ => {
+            } else {
                 if let Some(span) = span_field {
                     quote! { #diag.#name(#span, #message); }
                 } else {
                     quote! { #diag.#name(#message); }
                 }
-            }
-        };
+            };
+            tokens.extend(quote! {
+                #call
+                #args
+            });
+        }
 
-        let plain_args: TokenStream = self
-            .variant
-            .bindings()
-            .iter()
-            .filter(|binding| binding.ast().attrs.is_empty())
-            .map(|binding| self.generate_field_set_arg(binding))
-            .collect();
-
-        Ok(quote! {
-            #init
-            #attr_args
-            #call
-            #plain_args
-        })
+        Ok(tokens)
     }
 }
index 708d0b1fd8a301818b8b667233975772cfffe0ea..6a5716600b3b34379b3257fcf93121b347a3ead0 100644 (file)
@@ -29,7 +29,6 @@
 use std::ops::Fn;
 use std::path::Path;
 use std::{cmp, env};
-use tracing::{debug, info};
 
 #[derive(Clone)]
 pub struct CStore {
@@ -263,7 +262,7 @@ pub fn into_cstore(self) -> CStore {
     fn existing_match(&self, name: Symbol, hash: Option<Svh>, kind: PathKind) -> Option<CrateNum> {
         for (cnum, data) in self.cstore.iter_crate_data() {
             if data.name() != name {
-                tracing::trace!("{} did not match {}", data.name(), name);
+                trace!("{} did not match {}", data.name(), name);
                 continue;
             }
 
index b765c34f8e364d2d4ec15846bd5d4daa5718a9a9..1a25e987d3a62c5369e772bf5592938838b137a7 100644 (file)
@@ -158,11 +158,11 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
         let name = tcx.crate_name(cnum);
         let src = tcx.used_crate_source(cnum);
         if src.dylib.is_some() {
-            tracing::info!("adding dylib: {}", name);
+            info!("adding dylib: {}", name);
             add_library(tcx, cnum, RequireDynamic, &mut formats);
             let deps = tcx.dylib_dependency_formats(cnum);
             for &(depnum, style) in deps.iter() {
-                tracing::info!("adding {:?}: {}", style, tcx.crate_name(depnum));
+                info!("adding {:?}: {}", style, tcx.crate_name(depnum));
                 add_library(tcx, depnum, style, &mut formats);
             }
         }
@@ -190,7 +190,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
             && tcx.dep_kind(cnum) == CrateDepKind::Explicit
         {
             assert!(src.rlib.is_some() || src.rmeta.is_some());
-            tracing::info!("adding staticlib: {}", tcx.crate_name(cnum));
+            info!("adding staticlib: {}", tcx.crate_name(cnum));
             add_library(tcx, cnum, RequireStatic, &mut formats);
             ret[cnum.as_usize() - 1] = Linkage::Static;
         }
index 6440f3e390cf1fde772fe34a0b50f79eef8faf20..337d3cca2aed7fb3acab33145400fca40561ba3a 100644 (file)
@@ -26,6 +26,9 @@
 #[macro_use]
 extern crate rustc_data_structures;
 
+#[macro_use]
+extern crate tracing;
+
 pub use rmeta::{provide, provide_extern};
 
 mod dependency_format;
index 2c1c84b0be26a874d7d9e6657f6a05f105381460..5b7d0c8581ab214217dc49f520d3044fafbfcbfa 100644 (file)
 use std::io::{Read, Result as IoResult, Write};
 use std::path::{Path, PathBuf};
 use std::{cmp, fmt, fs};
-use tracing::{debug, info};
 
 #[derive(Clone)]
 pub(crate) struct CrateLocator<'a> {
index d0e0aa91480c917eb56c5243795c9c5ef5c3c1f3..6f026678170b7af312fbde3ba26f97d40ebf3982 100644 (file)
@@ -4,7 +4,6 @@
 use crate::rmeta::*;
 
 use rustc_ast as ast;
-use rustc_ast::ptr::P;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::svh::Svh;
@@ -33,7 +32,7 @@
 use rustc_session::Session;
 use rustc_span::hygiene::{ExpnIndex, MacroKind};
 use rustc_span::source_map::{respan, Spanned};
-use rustc_span::symbol::{sym, Ident, Symbol};
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{self, BytePos, ExpnId, Pos, Span, SyntaxContext, DUMMY_SP};
 
 use proc_macro::bridge::client::ProcMacro;
@@ -42,7 +41,6 @@
 use std::mem;
 use std::num::NonZeroUsize;
 use std::path::Path;
-use tracing::debug;
 
 pub(super) use cstore_impl::provide;
 pub use cstore_impl::provide_extern;
@@ -786,26 +784,11 @@ fn item_ident(self, item_index: DefIndex, sess: &Session) -> Ident {
         self.opt_item_ident(item_index, sess).expect("no encoded ident for item")
     }
 
-    fn maybe_kind(self, item_id: DefIndex) -> Option<EntryKind> {
-        self.root.tables.kind.get(self, item_id).map(|k| k.decode(self))
-    }
-
     #[inline]
     pub(super) fn map_encoded_cnum_to_current(self, cnum: CrateNum) -> CrateNum {
         if cnum == LOCAL_CRATE { self.cnum } else { self.cnum_map[cnum] }
     }
 
-    fn kind(self, item_id: DefIndex) -> EntryKind {
-        self.maybe_kind(item_id).unwrap_or_else(|| {
-            bug!(
-                "CrateMetadata::kind({:?}): id not found, in crate {:?} with number {}",
-                item_id,
-                self.root.name,
-                self.cnum,
-            )
-        })
-    }
-
     fn def_kind(self, item_id: DefIndex) -> DefKind {
         self.root.tables.opt_def_kind.get(self, item_id).unwrap_or_else(|| {
             bug!(
@@ -857,21 +840,16 @@ fn load_proc_macro(self, id: DefIndex, sess: &Session) -> SyntaxExtension {
         )
     }
 
-    fn get_variant(self, kind: &EntryKind, index: DefIndex, parent_did: DefId) -> ty::VariantDef {
-        let data = match kind {
-            EntryKind::Variant(data) | EntryKind::Struct(data) | EntryKind::Union(data) => {
-                data.decode(self)
-            }
-            _ => bug!(),
-        };
-
+    fn get_variant(self, kind: &DefKind, index: DefIndex, parent_did: DefId) -> ty::VariantDef {
         let adt_kind = match kind {
-            EntryKind::Variant(_) => ty::AdtKind::Enum,
-            EntryKind::Struct(..) => ty::AdtKind::Struct,
-            EntryKind::Union(..) => ty::AdtKind::Union,
+            DefKind::Variant => ty::AdtKind::Enum,
+            DefKind::Struct => ty::AdtKind::Struct,
+            DefKind::Union => ty::AdtKind::Union,
             _ => bug!(),
         };
 
+        let data = self.root.tables.variant_data.get(self, index).unwrap().decode(self);
+
         let variant_did =
             if adt_kind == ty::AdtKind::Enum { Some(self.local_def_id(index)) } else { None };
         let ctor_did = data.ctor.map(|index| self.local_def_id(index));
@@ -902,13 +880,13 @@ fn get_variant(self, kind: &EntryKind, index: DefIndex, parent_did: DefId) -> ty
     }
 
     fn get_adt_def(self, item_id: DefIndex, tcx: TyCtxt<'tcx>) -> ty::AdtDef<'tcx> {
-        let kind = self.kind(item_id);
+        let kind = self.def_kind(item_id);
         let did = self.local_def_id(item_id);
 
         let adt_kind = match kind {
-            EntryKind::Enum => ty::AdtKind::Enum,
-            EntryKind::Struct(_) => ty::AdtKind::Struct,
-            EntryKind::Union(_) => ty::AdtKind::Union,
+            DefKind::Enum => ty::AdtKind::Enum,
+            DefKind::Struct => ty::AdtKind::Struct,
+            DefKind::Union => ty::AdtKind::Union,
             _ => bug!("get_adt_def called on a non-ADT {:?}", did),
         };
         let repr = self.root.tables.repr_options.get(self, item_id).unwrap().decode(self);
@@ -920,7 +898,7 @@ fn get_adt_def(self, item_id: DefIndex, tcx: TyCtxt<'tcx>) -> ty::AdtDef<'tcx> {
                 .get(self, item_id)
                 .unwrap_or_else(LazyArray::empty)
                 .decode(self)
-                .map(|index| self.get_variant(&self.kind(index), index, did))
+                .map(|index| self.get_variant(&self.def_kind(index), index, did))
                 .collect()
         } else {
             std::iter::once(self.get_variant(&kind, item_id, did)).collect()
@@ -1030,10 +1008,9 @@ fn for_each_module_child(
                 let vis = self.get_visibility(child_index);
                 let span = self.get_span(child_index, sess);
                 let macro_rules = match kind {
-                    DefKind::Macro(..) => match self.kind(child_index) {
-                        EntryKind::MacroDef(_, macro_rules) => macro_rules,
-                        _ => unreachable!(),
-                    },
+                    DefKind::Macro(..) => {
+                        self.root.tables.macro_rules.get(self, child_index).is_some()
+                    }
                     _ => false,
                 };
 
@@ -1087,14 +1064,10 @@ fn for_each_module_child(
             }
         }
 
-        match self.kind(id) {
-            EntryKind::Mod(exports) => {
-                for exp in exports.decode((self, sess)) {
-                    callback(exp);
-                }
+        if let Some(exports) = self.root.tables.module_reexports.get(self, id) {
+            for exp in exports.decode((self, sess)) {
+                callback(exp);
             }
-            EntryKind::Enum | EntryKind::Trait => {}
-            _ => bug!("`for_each_module_child` is called on a non-module: {:?}", self.def_kind(id)),
         }
     }
 
@@ -1107,19 +1080,21 @@ fn is_item_mir_available(self, id: DefIndex) -> bool {
     }
 
     fn module_expansion(self, id: DefIndex, sess: &Session) -> ExpnId {
-        match self.kind(id) {
-            EntryKind::Mod(_) | EntryKind::Enum | EntryKind::Trait => {
-                self.get_expn_that_defined(id, sess)
-            }
+        match self.def_kind(id) {
+            DefKind::Mod | DefKind::Enum | DefKind::Trait => self.get_expn_that_defined(id, sess),
             _ => panic!("Expected module, found {:?}", self.local_def_id(id)),
         }
     }
 
-    fn get_fn_has_self_parameter(self, id: DefIndex) -> bool {
-        match self.kind(id) {
-            EntryKind::AssocFn { has_self, .. } => has_self,
-            _ => false,
-        }
+    fn get_fn_has_self_parameter(self, id: DefIndex, sess: &'a Session) -> bool {
+        self.root
+            .tables
+            .fn_arg_names
+            .get(self, id)
+            .unwrap_or_else(LazyArray::empty)
+            .decode((self, sess))
+            .nth(0)
+            .map_or(false, |ident| ident.name == kw::SelfLower)
     }
 
     fn get_associated_item_def_ids(
@@ -1136,15 +1111,17 @@ fn get_associated_item_def_ids(
             .map(move |child_index| self.local_def_id(child_index))
     }
 
-    fn get_associated_item(self, id: DefIndex) -> ty::AssocItem {
+    fn get_associated_item(self, id: DefIndex, sess: &'a Session) -> ty::AssocItem {
         let name = self.item_name(id);
 
-        let (kind, container, has_self) = match self.kind(id) {
-            EntryKind::AssocConst(container) => (ty::AssocKind::Const, container, false),
-            EntryKind::AssocFn { container, has_self } => (ty::AssocKind::Fn, container, has_self),
-            EntryKind::AssocType(container) => (ty::AssocKind::Type, container, false),
-            _ => bug!("cannot get associated-item of `{:?}`", id),
+        let kind = match self.def_kind(id) {
+            DefKind::AssocConst => ty::AssocKind::Const,
+            DefKind::AssocFn => ty::AssocKind::Fn,
+            DefKind::AssocTy => ty::AssocKind::Type,
+            _ => bug!("cannot get associated-item of `{:?}`", self.def_key(id)),
         };
+        let has_self = self.get_fn_has_self_parameter(id, sess);
+        let container = self.root.tables.assoc_container.get(self, id).unwrap();
 
         ty::AssocItem {
             name,
@@ -1157,9 +1134,9 @@ fn get_associated_item(self, id: DefIndex) -> ty::AssocItem {
     }
 
     fn get_ctor_def_id_and_kind(self, node_id: DefIndex) -> Option<(DefId, CtorKind)> {
-        match self.kind(node_id) {
-            EntryKind::Struct(data) | EntryKind::Variant(data) => {
-                let vdata = data.decode(self);
+        match self.def_kind(node_id) {
+            DefKind::Struct | DefKind::Variant => {
+                let vdata = self.root.tables.variant_data.get(self, node_id).unwrap().decode(self);
                 vdata.ctor.map(|index| (self.local_def_id(index), vdata.ctor_kind))
             }
             _ => None,
@@ -1347,18 +1324,22 @@ fn exported_symbols(
     }
 
     fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef {
-        match self.kind(id) {
-            EntryKind::MacroDef(mac_args, macro_rules) => {
-                ast::MacroDef { body: P(mac_args.decode((self, sess))), macro_rules }
+        match self.def_kind(id) {
+            DefKind::Macro(_) => {
+                let macro_rules = self.root.tables.macro_rules.get(self, id).is_some();
+                let body =
+                    self.root.tables.macro_definition.get(self, id).unwrap().decode((self, sess));
+                ast::MacroDef { macro_rules, body: ast::ptr::P(body) }
             }
             _ => bug!(),
         }
     }
 
     fn is_foreign_item(self, id: DefIndex) -> bool {
-        match self.kind(id) {
-            EntryKind::ForeignStatic | EntryKind::ForeignFn => true,
-            _ => false,
+        if let Some(parent) = self.def_key(id).parent {
+            matches!(self.def_kind(parent), DefKind::ForeignMod)
+        } else {
+            false
         }
     }
 
index 9d201a0c799928174f80efd1f93135013ed01733..6b447ebd99910ae21f348d34728e24b00095168f 100644 (file)
@@ -233,7 +233,7 @@ fn into_args(self) -> (DefId, SimplifiedType) {
     associated_item_def_ids => {
         tcx.arena.alloc_from_iter(cdata.get_associated_item_def_ids(def_id.index, tcx.sess))
     }
-    associated_item => { cdata.get_associated_item(def_id.index) }
+    associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) }
     inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
     is_foreign_item => { cdata.is_foreign_item(def_id.index) }
     item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) }
@@ -535,8 +535,8 @@ pub fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro {
         )
     }
 
-    pub fn fn_has_self_parameter_untracked(&self, def: DefId) -> bool {
-        self.get_crate_data(def.krate).get_fn_has_self_parameter(def.index)
+    pub fn fn_has_self_parameter_untracked(&self, def: DefId, sess: &Session) -> bool {
+        self.get_crate_data(def.krate).get_fn_has_self_parameter(def.index, sess)
     }
 
     pub fn crate_source_untracked(&self, cnum: CrateNum) -> Lrc<CrateSource> {
index 3482d9f04514e561b08c458639af28b7dde76292..4be4d4b7872aeeaa7d745d034cf2daf247ac3e4e 100644 (file)
@@ -16,7 +16,6 @@
 use rustc_hir::definitions::DefPathData;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::lang_items;
-use rustc_hir::{AnonConst, GenericParamKind};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::{
@@ -44,7 +43,6 @@
 use std::iter;
 use std::num::NonZeroUsize;
 use std::path::{Path, PathBuf};
-use tracing::{debug, trace};
 
 pub(super) struct EncodeContext<'a, 'tcx> {
     opaque: opaque::FileEncoder,
@@ -1020,6 +1018,89 @@ fn should_encode_generics(def_kind: DefKind) -> bool {
     }
 }
 
+fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> bool {
+    match def_kind {
+        DefKind::Struct
+        | DefKind::Union
+        | DefKind::Enum
+        | DefKind::Variant
+        | DefKind::Ctor(..)
+        | DefKind::Field
+        | DefKind::Fn
+        | DefKind::Const
+        | DefKind::Static(..)
+        | DefKind::TyAlias
+        | DefKind::OpaqueTy
+        | DefKind::ForeignTy
+        | DefKind::Impl
+        | DefKind::AssocFn
+        | DefKind::AssocConst
+        | DefKind::Closure
+        | DefKind::Generator
+        | DefKind::ConstParam
+        | DefKind::AnonConst
+        | DefKind::InlineConst => true,
+
+        DefKind::AssocTy => {
+            let assoc_item = tcx.associated_item(def_id);
+            match assoc_item.container {
+                ty::AssocItemContainer::ImplContainer => true,
+                ty::AssocItemContainer::TraitContainer => assoc_item.defaultness(tcx).has_value(),
+            }
+        }
+        DefKind::TyParam => {
+            let hir::Node::GenericParam(param) = tcx.hir().get_by_def_id(def_id) else { bug!() };
+            let hir::GenericParamKind::Type { default, .. } = param.kind else { bug!() };
+            default.is_some()
+        }
+
+        DefKind::Trait
+        | DefKind::TraitAlias
+        | DefKind::Mod
+        | DefKind::ForeignMod
+        | DefKind::Macro(..)
+        | DefKind::Use
+        | DefKind::LifetimeParam
+        | DefKind::GlobalAsm
+        | DefKind::ExternCrate => false,
+    }
+}
+
+fn should_encode_const(def_kind: DefKind) -> bool {
+    match def_kind {
+        DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => true,
+
+        DefKind::Struct
+        | DefKind::Union
+        | DefKind::Enum
+        | DefKind::Variant
+        | DefKind::Ctor(..)
+        | DefKind::Field
+        | DefKind::Fn
+        | DefKind::Static(..)
+        | DefKind::TyAlias
+        | DefKind::OpaqueTy
+        | DefKind::ForeignTy
+        | DefKind::Impl
+        | DefKind::AssocFn
+        | DefKind::Closure
+        | DefKind::Generator
+        | DefKind::ConstParam
+        | DefKind::InlineConst
+        | DefKind::AssocTy
+        | DefKind::TyParam
+        | DefKind::Trait
+        | DefKind::TraitAlias
+        | DefKind::Mod
+        | DefKind::ForeignMod
+        | DefKind::Macro(..)
+        | DefKind::Use
+        | DefKind::LifetimeParam
+        | DefKind::GlobalAsm
+        | DefKind::ExternCrate => false,
+    }
+}
+
 impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     fn encode_attrs(&mut self, def_id: LocalDefId) {
         let mut attrs = self
@@ -1045,7 +1126,8 @@ fn encode_def_ids(&mut self) {
             let def_kind = tcx.opt_def_kind(local_id);
             let Some(def_kind) = def_kind else { continue };
             self.tables.opt_def_kind.set(def_id.index, def_kind);
-            record!(self.tables.def_span[def_id] <- tcx.def_span(def_id));
+            let def_span = tcx.def_span(local_id);
+            record!(self.tables.def_span[def_id] <- def_span);
             self.encode_attrs(local_id);
             record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id));
             if let Some(ident_span) = tcx.def_ident_span(def_id) {
@@ -1076,6 +1158,9 @@ fn encode_def_ids(&mut self) {
                     record_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
                 }
             }
+            if should_encode_type(tcx, local_id, def_kind) {
+                record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id));
+            }
             if let DefKind::TyParam | DefKind::ConstParam = def_kind {
                 if let Some(default) = self.tcx.object_lifetime_default(def_id) {
                     record!(self.tables.object_lifetime_default[def_id] <- default);
@@ -1097,11 +1182,6 @@ fn encode_def_ids(&mut self) {
         }
     }
 
-    fn encode_item_type(&mut self, def_id: DefId) {
-        debug!("EncodeContext::encode_item_type({:?})", def_id);
-        record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id));
-    }
-
     fn encode_enum_variant_info(&mut self, def: ty::AdtDef<'tcx>, index: VariantIdx) {
         let tcx = self.tcx;
         let variant = &def.variant(index);
@@ -1115,13 +1195,12 @@ fn encode_enum_variant_info(&mut self, def: ty::AdtDef<'tcx>, index: VariantIdx)
             is_non_exhaustive: variant.is_field_list_non_exhaustive(),
         };
 
-        record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data)));
+        record!(self.tables.variant_data[def_id] <- data);
         self.tables.constness.set(def_id.index, hir::Constness::Const);
         record_array!(self.tables.children[def_id] <- variant.fields.iter().map(|f| {
             assert!(f.did.is_local());
             f.did.index
         }));
-        self.encode_item_type(def_id);
         if variant.ctor_kind == CtorKind::Fn {
             // FIXME(eddyb) encode signature only in `encode_enum_variant_ctor`.
             if let Some(ctor_def_id) = variant.ctor_def_id {
@@ -1144,9 +1223,8 @@ fn encode_enum_variant_ctor(&mut self, def: ty::AdtDef<'tcx>, index: VariantIdx)
             is_non_exhaustive: variant.is_field_list_non_exhaustive(),
         };
 
-        record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data)));
+        record!(self.tables.variant_data[def_id] <- data);
         self.tables.constness.set(def_id.index, hir::Constness::Const);
-        self.encode_item_type(def_id);
         if variant.ctor_kind == CtorKind::Fn {
             record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
         }
@@ -1163,15 +1241,12 @@ fn encode_info_for_mod(&mut self, local_def_id: LocalDefId, md: &hir::Mod<'_>) {
         // code uses it). However, we skip encoding anything relating to child
         // items - we encode information about proc-macros later on.
         let reexports = if !self.is_proc_macro {
-            match tcx.module_reexports(local_def_id) {
-                Some(exports) => self.lazy_array(exports),
-                _ => LazyArray::empty(),
-            }
+            tcx.module_reexports(local_def_id).unwrap_or(&[])
         } else {
-            LazyArray::empty()
+            &[]
         };
 
-        record!(self.tables.kind[def_id] <- EntryKind::Mod(reexports));
+        record_array!(self.tables.module_reexports[def_id] <- reexports);
         if self.is_proc_macro {
             // Encode this here because we don't do it in encode_def_ids.
             record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id));
@@ -1199,22 +1274,6 @@ fn encode_info_for_mod(&mut self, local_def_id: LocalDefId, md: &hir::Mod<'_>) {
         }
     }
 
-    fn encode_field(
-        &mut self,
-        adt_def: ty::AdtDef<'tcx>,
-        variant_index: VariantIdx,
-        field_index: usize,
-    ) {
-        let variant = &adt_def.variant(variant_index);
-        let field = &variant.fields[field_index];
-
-        let def_id = field.did;
-        debug!("EncodeContext::encode_field({:?})", def_id);
-
-        record!(self.tables.kind[def_id] <- EntryKind::Field);
-        self.encode_item_type(def_id);
-    }
-
     fn encode_struct_ctor(&mut self, adt_def: ty::AdtDef<'tcx>, def_id: DefId) {
         debug!("EncodeContext::encode_struct_ctor({:?})", def_id);
         let tcx = self.tcx;
@@ -1228,9 +1287,8 @@ fn encode_struct_ctor(&mut self, adt_def: ty::AdtDef<'tcx>, def_id: DefId) {
         };
 
         record!(self.tables.repr_options[def_id] <- adt_def.repr());
+        record!(self.tables.variant_data[def_id] <- data);
         self.tables.constness.set(def_id.index, hir::Constness::Const);
-        record!(self.tables.kind[def_id] <- EntryKind::Struct(self.lazy(data)));
-        self.encode_item_type(def_id);
         if variant.ctor_kind == CtorKind::Fn {
             record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
         }
@@ -1251,18 +1309,10 @@ fn encode_info_for_trait_item(&mut self, def_id: DefId) {
         let ast_item = tcx.hir().expect_trait_item(def_id.expect_local());
         self.tables.impl_defaultness.set(def_id.index, ast_item.defaultness);
         let trait_item = tcx.associated_item(def_id);
+        self.tables.assoc_container.set(def_id.index, trait_item.container);
 
         match trait_item.kind {
-            ty::AssocKind::Const => {
-                let rendered = rustc_hir_pretty::to_string(
-                    &(&self.tcx.hir() as &dyn intravisit::Map<'_>),
-                    |s| s.print_trait_item(ast_item),
-                );
-
-                record!(self.tables.kind[def_id] <- EntryKind::AssocConst(ty::AssocItemContainer::TraitContainer));
-                record!(self.tables.mir_const_qualif[def_id] <- mir::ConstQualifs::default());
-                record!(self.tables.rendered_const[def_id] <- rendered);
-            }
+            ty::AssocKind::Const => {}
             ty::AssocKind::Fn => {
                 let hir::TraitItemKind::Fn(m_sig, m) = &ast_item.kind else { bug!() };
                 match *m {
@@ -1275,24 +1325,9 @@ fn encode_info_for_trait_item(&mut self, def_id: DefId) {
                 };
                 self.tables.asyncness.set(def_id.index, m_sig.header.asyncness);
                 self.tables.constness.set(def_id.index, hir::Constness::NotConst);
-                record!(self.tables.kind[def_id] <- EntryKind::AssocFn {
-                    container: ty::AssocItemContainer::TraitContainer,
-                    has_self: trait_item.fn_has_self_parameter,
-                });
             }
             ty::AssocKind::Type => {
                 self.encode_explicit_item_bounds(def_id);
-                record!(self.tables.kind[def_id] <- EntryKind::AssocType(ty::AssocItemContainer::TraitContainer));
-            }
-        }
-        match trait_item.kind {
-            ty::AssocKind::Const | ty::AssocKind::Fn => {
-                self.encode_item_type(def_id);
-            }
-            ty::AssocKind::Type => {
-                if ast_item.defaultness.has_value() {
-                    self.encode_item_type(def_id);
-                }
             }
         }
         if trait_item.kind == ty::AssocKind::Fn {
@@ -1307,20 +1342,9 @@ fn encode_info_for_impl_item(&mut self, def_id: DefId) {
         let ast_item = self.tcx.hir().expect_impl_item(def_id.expect_local());
         self.tables.impl_defaultness.set(def_id.index, ast_item.defaultness);
         let impl_item = self.tcx.associated_item(def_id);
+        self.tables.assoc_container.set(def_id.index, impl_item.container);
 
         match impl_item.kind {
-            ty::AssocKind::Const => {
-                if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind {
-                    let qualifs = self.tcx.at(ast_item.span).mir_const_qualif(def_id);
-                    let const_data = self.encode_rendered_const_for_body(body_id);
-
-                    record!(self.tables.kind[def_id] <- EntryKind::AssocConst(ty::AssocItemContainer::ImplContainer));
-                    record!(self.tables.mir_const_qualif[def_id] <- qualifs);
-                    record!(self.tables.rendered_const[def_id] <- const_data);
-                } else {
-                    bug!()
-                }
-            }
             ty::AssocKind::Fn => {
                 let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind else { bug!() };
                 self.tables.asyncness.set(def_id.index, sig.header.asyncness);
@@ -1332,16 +1356,9 @@ fn encode_info_for_impl_item(&mut self, def_id: DefId) {
                     hir::Constness::NotConst
                 };
                 self.tables.constness.set(def_id.index, constness);
-                record!(self.tables.kind[def_id] <- EntryKind::AssocFn {
-                    container: ty::AssocItemContainer::ImplContainer,
-                    has_self: impl_item.fn_has_self_parameter,
-                });
-            }
-            ty::AssocKind::Type => {
-                record!(self.tables.kind[def_id] <- EntryKind::AssocType(ty::AssocItemContainer::ImplContainer));
             }
+            ty::AssocKind::Const | ty::AssocKind::Type => {}
         }
-        self.encode_item_type(def_id);
         if let Some(trait_item_def_id) = impl_item.trait_item_def_id {
             self.tables.trait_item_def_id.set(def_id.index, trait_item_def_id.into());
         }
@@ -1358,12 +1375,13 @@ fn encode_mir(&mut self) {
             return;
         }
 
-        let keys_and_jobs = self
-            .tcx
+        let tcx = self.tcx;
+
+        let keys_and_jobs = tcx
             .mir_keys(())
             .iter()
             .filter_map(|&def_id| {
-                let (encode_const, encode_opt) = should_encode_mir(self.tcx, def_id);
+                let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
                 if encode_const || encode_opt {
                     Some((def_id, encode_const, encode_opt))
                 } else {
@@ -1376,22 +1394,32 @@ fn encode_mir(&mut self) {
 
             debug!("EntryBuilder::encode_mir({:?})", def_id);
             if encode_opt {
-                record!(self.tables.optimized_mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
+                record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id));
             }
             if encode_const {
-                record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- self.tcx.mir_for_ctfe(def_id));
+                record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id));
 
                 // FIXME(generic_const_exprs): this feels wrong to have in `encode_mir`
-                let abstract_const = self.tcx.thir_abstract_const(def_id);
+                let abstract_const = tcx.thir_abstract_const(def_id);
                 if let Ok(Some(abstract_const)) = abstract_const {
                     record!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const);
                 }
+
+                if should_encode_const(tcx.def_kind(def_id)) {
+                    let qualifs = tcx.mir_const_qualif(def_id);
+                    record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs);
+                    let body_id = tcx.hir().maybe_body_owned_by(def_id);
+                    if let Some(body_id) = body_id {
+                        let const_data = self.encode_rendered_const_for_body(body_id);
+                        record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data);
+                    }
+                }
             }
-            record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
+            record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id));
 
             let instance =
                 ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id()));
-            let unused = self.tcx.unused_generic_params(instance);
+            let unused = tcx.unused_generic_params(instance);
             if !unused.is_empty() {
                 record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
             }
@@ -1454,38 +1482,27 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) {
 
         debug!("EncodeContext::encode_info_for_item({:?})", def_id);
 
-        let entry_kind = match item.kind {
-            hir::ItemKind::Static(..) => EntryKind::Static,
-            hir::ItemKind::Const(_, body_id) => {
-                let qualifs = self.tcx.at(item.span).mir_const_qualif(def_id);
-                let const_data = self.encode_rendered_const_for_body(body_id);
-                record!(self.tables.mir_const_qualif[def_id] <- qualifs);
-                record!(self.tables.rendered_const[def_id] <- const_data);
-                EntryKind::Const
-            }
+        match item.kind {
             hir::ItemKind::Fn(ref sig, .., body) => {
                 self.tables.asyncness.set(def_id.index, sig.header.asyncness);
                 record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
                 self.tables.constness.set(def_id.index, sig.header.constness);
-                EntryKind::Fn
             }
             hir::ItemKind::Macro(ref macro_def, _) => {
-                EntryKind::MacroDef(self.lazy(&*macro_def.body), macro_def.macro_rules)
+                if macro_def.macro_rules {
+                    self.tables.macro_rules.set(def_id.index, ());
+                }
+                record!(self.tables.macro_definition[def_id] <- &*macro_def.body);
             }
             hir::ItemKind::Mod(ref m) => {
                 return self.encode_info_for_mod(item.def_id, m);
             }
-            hir::ItemKind::ForeignMod { .. } => EntryKind::ForeignMod,
-            hir::ItemKind::GlobalAsm(..) => EntryKind::GlobalAsm,
-            hir::ItemKind::TyAlias(..) => EntryKind::Type,
             hir::ItemKind::OpaqueTy(..) => {
                 self.encode_explicit_item_bounds(def_id);
-                EntryKind::OpaqueTy
             }
             hir::ItemKind::Enum(..) => {
                 let adt_def = self.tcx.adt_def(def_id);
                 record!(self.tables.repr_options[def_id] <- adt_def.repr());
-                EntryKind::Enum
             }
             hir::ItemKind::Struct(ref struct_def, _) => {
                 let adt_def = self.tcx.adt_def(def_id);
@@ -1500,24 +1517,24 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) {
                     .map(|ctor_hir_id| self.tcx.hir().local_def_id(ctor_hir_id).local_def_index);
 
                 let variant = adt_def.non_enum_variant();
-                EntryKind::Struct(self.lazy(VariantData {
+                record!(self.tables.variant_data[def_id] <- VariantData {
                     ctor_kind: variant.ctor_kind,
                     discr: variant.discr,
                     ctor,
                     is_non_exhaustive: variant.is_field_list_non_exhaustive(),
-                }))
+                });
             }
             hir::ItemKind::Union(..) => {
                 let adt_def = self.tcx.adt_def(def_id);
                 record!(self.tables.repr_options[def_id] <- adt_def.repr());
 
                 let variant = adt_def.non_enum_variant();
-                EntryKind::Union(self.lazy(VariantData {
+                record!(self.tables.variant_data[def_id] <- VariantData {
                     ctor_kind: variant.ctor_kind,
                     discr: variant.discr,
                     ctor: None,
                     is_non_exhaustive: variant.is_field_list_non_exhaustive(),
-                }))
+                });
             }
             hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
                 self.tables.impl_defaultness.set(def_id.index, *defaultness);
@@ -1543,26 +1560,24 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) {
 
                 let polarity = self.tcx.impl_polarity(def_id);
                 self.tables.impl_polarity.set(def_id.index, polarity);
-
-                EntryKind::Impl
             }
             hir::ItemKind::Trait(..) => {
                 let trait_def = self.tcx.trait_def(def_id);
                 record!(self.tables.trait_def[def_id] <- trait_def);
-
-                EntryKind::Trait
             }
             hir::ItemKind::TraitAlias(..) => {
                 let trait_def = self.tcx.trait_def(def_id);
                 record!(self.tables.trait_def[def_id] <- trait_def);
-
-                EntryKind::TraitAlias
             }
             hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => {
                 bug!("cannot encode info for item {:?}", item)
             }
+            hir::ItemKind::Static(..)
+            | hir::ItemKind::Const(..)
+            | hir::ItemKind::ForeignMod { .. }
+            | hir::ItemKind::GlobalAsm(..)
+            | hir::ItemKind::TyAlias(..) => {}
         };
-        record!(self.tables.kind[def_id] <- entry_kind);
         // FIXME(eddyb) there should be a nicer way to do this.
         match item.kind {
             hir::ItemKind::Enum(..) => record_array!(self.tables.children[def_id] <-
@@ -1590,18 +1605,6 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) {
             }
             _ => {}
         }
-        match item.kind {
-            hir::ItemKind::Static(..)
-            | hir::ItemKind::Const(..)
-            | hir::ItemKind::Fn(..)
-            | hir::ItemKind::TyAlias(..)
-            | hir::ItemKind::OpaqueTy(..)
-            | hir::ItemKind::Enum(..)
-            | hir::ItemKind::Struct(..)
-            | hir::ItemKind::Union(..)
-            | hir::ItemKind::Impl { .. } => self.encode_item_type(def_id),
-            _ => {}
-        }
         if let hir::ItemKind::Fn(..) = item.kind {
             record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
             if tcx.is_intrinsic(def_id) {
@@ -1613,12 +1616,43 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) {
                 record!(self.tables.impl_trait_ref[def_id] <- trait_ref);
             }
         }
-    }
+        // In some cases, along with the item itself, we also
+        // encode some sub-items. Usually we want some info from the item
+        // so it's easier to do that here then to wait until we would encounter
+        // normally in the visitor walk.
+        match item.kind {
+            hir::ItemKind::Enum(..) => {
+                let def = self.tcx.adt_def(item.def_id.to_def_id());
+                for (i, variant) in def.variants().iter_enumerated() {
+                    self.encode_enum_variant_info(def, i);
 
-    fn encode_info_for_generic_param(&mut self, def_id: DefId, kind: EntryKind, encode_type: bool) {
-        record!(self.tables.kind[def_id] <- kind);
-        if encode_type {
-            self.encode_item_type(def_id);
+                    if let Some(_ctor_def_id) = variant.ctor_def_id {
+                        self.encode_enum_variant_ctor(def, i);
+                    }
+                }
+            }
+            hir::ItemKind::Struct(ref struct_def, _) => {
+                let def = self.tcx.adt_def(item.def_id.to_def_id());
+                // If the struct has a constructor, encode it.
+                if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
+                    let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
+                    self.encode_struct_ctor(def, ctor_def_id.to_def_id());
+                }
+            }
+            hir::ItemKind::Impl { .. } => {
+                for &trait_item_def_id in
+                    self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
+                {
+                    self.encode_info_for_impl_item(trait_item_def_id);
+                }
+            }
+            hir::ItemKind::Trait(..) => {
+                for &item_def_id in self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
+                {
+                    self.encode_info_for_trait_item(item_def_id);
+                }
+            }
+            _ => {}
         }
     }
 
@@ -1633,34 +1667,16 @@ fn encode_info_for_closure(&mut self, hir_id: hir::HirId) {
             ty::Generator(..) => {
                 let data = self.tcx.generator_kind(def_id).unwrap();
                 let generator_diagnostic_data = typeck_result.get_generator_diagnostic_data();
-                record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::Generator);
                 record!(self.tables.generator_kind[def_id.to_def_id()] <- data);
                 record!(self.tables.generator_diagnostic_data[def_id.to_def_id()]  <- generator_diagnostic_data);
             }
 
-            ty::Closure(..) => {
-                record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::Closure);
+            ty::Closure(_, substs) => {
+                record!(self.tables.fn_sig[def_id.to_def_id()] <- substs.as_closure().sig());
             }
 
             _ => bug!("closure that is neither generator nor closure"),
         }
-        self.encode_item_type(def_id.to_def_id());
-        if let ty::Closure(def_id, substs) = *ty.kind() {
-            record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig());
-        }
-    }
-
-    fn encode_info_for_anon_const(&mut self, id: hir::HirId) {
-        let def_id = self.tcx.hir().local_def_id(id);
-        debug!("EncodeContext::encode_info_for_anon_const({:?})", def_id);
-        let body_id = self.tcx.hir().body_owned_by(def_id);
-        let const_data = self.encode_rendered_const_for_body(body_id);
-        let qualifs = self.tcx.mir_const_qualif(def_id);
-
-        record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::AnonConst);
-        record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs);
-        record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data);
-        self.encode_item_type(def_id.to_def_id());
     }
 
     fn encode_native_libraries(&mut self) -> LazyArray<NativeLib> {
@@ -1758,7 +1774,7 @@ fn encode_proc_macros(&mut self) -> Option<ProcMacroData> {
 
                 let def_id = id.to_def_id();
                 self.tables.opt_def_kind.set(def_id.index, DefKind::Macro(macro_kind));
-                record!(self.tables.kind[def_id] <- EntryKind::ProcMacro(macro_kind));
+                self.tables.proc_macro.set(def_id.index, macro_kind);
                 self.encode_attrs(id);
                 record!(self.tables.def_keys[def_id] <- def_key);
                 record!(self.tables.def_ident_span[def_id] <- span);
@@ -1996,18 +2012,11 @@ fn encode_info_for_foreign_item(&mut self, def_id: DefId, nitem: &hir::ForeignIt
                     hir::Constness::NotConst
                 };
                 self.tables.constness.set(def_id.index, constness);
-                record!(self.tables.kind[def_id] <- EntryKind::ForeignFn);
-            }
-            hir::ForeignItemKind::Static(..) => {
-                record!(self.tables.kind[def_id] <- EntryKind::ForeignStatic);
-            }
-            hir::ForeignItemKind::Type => {
-                record!(self.tables.kind[def_id] <- EntryKind::ForeignType);
+                record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
             }
+            hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => {}
         }
-        self.encode_item_type(def_id);
         if let hir::ForeignItemKind::Fn(..) = nitem.kind {
-            record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
             if tcx.is_intrinsic(def_id) {
                 self.tables.is_intrinsic.set(def_id.index, ());
             }
@@ -2026,17 +2035,12 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
         intravisit::walk_expr(self, ex);
         self.encode_info_for_expr(ex);
     }
-    fn visit_anon_const(&mut self, c: &'tcx AnonConst) {
-        intravisit::walk_anon_const(self, c);
-        self.encode_info_for_anon_const(c.hir_id);
-    }
     fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
         intravisit::walk_item(self, item);
         match item.kind {
             hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => {} // ignore these
             _ => self.encode_info_for_item(item.def_id.to_def_id(), item),
         }
-        self.encode_addl_info_for_item(item);
     }
     fn visit_foreign_item(&mut self, ni: &'tcx hir::ForeignItem<'tcx>) {
         intravisit::walk_foreign_item(self, ni);
@@ -2049,29 +2053,13 @@ fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
 }
 
 impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
-    fn encode_fields(&mut self, adt_def: ty::AdtDef<'tcx>) {
-        for (variant_index, variant) in adt_def.variants().iter_enumerated() {
-            for (field_index, _field) in variant.fields.iter().enumerate() {
-                self.encode_field(adt_def, variant_index, field_index);
-            }
-        }
-    }
-
     fn encode_info_for_generics(&mut self, generics: &hir::Generics<'tcx>) {
         for param in generics.params {
             let def_id = self.tcx.hir().local_def_id(param.hir_id);
             match param.kind {
-                GenericParamKind::Lifetime { .. } => continue,
-                GenericParamKind::Type { default, .. } => {
-                    self.encode_info_for_generic_param(
-                        def_id.to_def_id(),
-                        EntryKind::TypeParam,
-                        default.is_some(),
-                    );
-                }
-                GenericParamKind::Const { ref default, .. } => {
+                hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => {}
+                hir::GenericParamKind::Const { ref default, .. } => {
                     let def_id = def_id.to_def_id();
-                    self.encode_info_for_generic_param(def_id, EntryKind::ConstParam, true);
                     if default.is_some() {
                         record!(self.tables.const_param_default[def_id] <- self.tcx.const_param_default(def_id))
                     }
@@ -2085,68 +2073,6 @@ fn encode_info_for_expr(&mut self, expr: &hir::Expr<'_>) {
             self.encode_info_for_closure(expr.hir_id);
         }
     }
-
-    /// In some cases, along with the item itself, we also
-    /// encode some sub-items. Usually we want some info from the item
-    /// so it's easier to do that here then to wait until we would encounter
-    /// normally in the visitor walk.
-    fn encode_addl_info_for_item(&mut self, item: &hir::Item<'_>) {
-        match item.kind {
-            hir::ItemKind::Static(..)
-            | hir::ItemKind::Const(..)
-            | hir::ItemKind::Fn(..)
-            | hir::ItemKind::Macro(..)
-            | hir::ItemKind::Mod(..)
-            | hir::ItemKind::ForeignMod { .. }
-            | hir::ItemKind::GlobalAsm(..)
-            | hir::ItemKind::ExternCrate(..)
-            | hir::ItemKind::Use(..)
-            | hir::ItemKind::TyAlias(..)
-            | hir::ItemKind::OpaqueTy(..)
-            | hir::ItemKind::TraitAlias(..) => {
-                // no sub-item recording needed in these cases
-            }
-            hir::ItemKind::Enum(..) => {
-                let def = self.tcx.adt_def(item.def_id.to_def_id());
-                self.encode_fields(def);
-
-                for (i, variant) in def.variants().iter_enumerated() {
-                    self.encode_enum_variant_info(def, i);
-
-                    if let Some(_ctor_def_id) = variant.ctor_def_id {
-                        self.encode_enum_variant_ctor(def, i);
-                    }
-                }
-            }
-            hir::ItemKind::Struct(ref struct_def, _) => {
-                let def = self.tcx.adt_def(item.def_id.to_def_id());
-                self.encode_fields(def);
-
-                // If the struct has a constructor, encode it.
-                if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
-                    let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
-                    self.encode_struct_ctor(def, ctor_def_id.to_def_id());
-                }
-            }
-            hir::ItemKind::Union(..) => {
-                let def = self.tcx.adt_def(item.def_id.to_def_id());
-                self.encode_fields(def);
-            }
-            hir::ItemKind::Impl { .. } => {
-                for &trait_item_def_id in
-                    self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
-                {
-                    self.encode_info_for_impl_item(trait_item_def_id);
-                }
-            }
-            hir::ItemKind::Trait(..) => {
-                for &item_def_id in self.tcx.associated_item_def_ids(item.def_id.to_def_id()).iter()
-                {
-                    self.encode_info_for_trait_item(item_def_id);
-                }
-            }
-        }
-    }
 }
 
 /// Used to prefetch queries which will be needed later by metadata encoding.
index aeffc85b507556648ed956d50937c91435949a48..6f849a58580e679550216f06ff690d75e11a8a8f 100644 (file)
@@ -334,7 +334,6 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
 }
 
 define_tables! {
-    kind: Table<DefIndex, LazyValue<EntryKind>>,
     attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
     children: Table<DefIndex, LazyArray<DefIndex>>,
 
@@ -393,39 +392,13 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
     proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
     generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
     may_have_doc_links: Table<DefIndex, ()>,
-}
-
-#[derive(Copy, Clone, MetadataEncodable, MetadataDecodable)]
-enum EntryKind {
-    AnonConst,
-    Const,
-    Static,
-    ForeignStatic,
-    ForeignMod,
-    ForeignType,
-    GlobalAsm,
-    Type,
-    TypeParam,
-    ConstParam,
-    OpaqueTy,
-    Enum,
-    Field,
-    Variant(LazyValue<VariantData>),
-    Struct(LazyValue<VariantData>),
-    Union(LazyValue<VariantData>),
-    Fn,
-    ForeignFn,
-    Mod(LazyArray<ModChild>),
-    MacroDef(LazyValue<ast::MacArgs>, /*macro_rules*/ bool),
-    ProcMacro(MacroKind),
-    Closure,
-    Generator,
-    Trait,
-    Impl,
-    AssocFn { container: ty::AssocItemContainer, has_self: bool },
-    AssocType(ty::AssocItemContainer),
-    AssocConst(ty::AssocItemContainer),
-    TraitAlias,
+    variant_data: Table<DefIndex, LazyValue<VariantData>>,
+    assoc_container: Table<DefIndex, ty::AssocItemContainer>,
+    // Slot is full when macro is macro_rules.
+    macro_rules: Table<DefIndex, ()>,
+    macro_definition: Table<DefIndex, LazyValue<ast::MacArgs>>,
+    proc_macro: Table<DefIndex, MacroKind>,
+    module_reexports: Table<DefIndex, LazyArray<ModChild>>,
 }
 
 #[derive(TyEncodable, TyDecodable)]
@@ -459,7 +432,6 @@ pub fn provide(providers: &mut Providers) {
 
 trivially_parameterized_over_tcx! {
     VariantData,
-    EntryKind,
     RawDefId,
     TraitImpls,
     IncoherentImpls,
index 21841ae2532a7b913f1da7b901c1417ae091cc84..d5f151f0ed8e50a4116de32d27be14044bf589eb 100644 (file)
@@ -10,7 +10,6 @@
 use std::convert::TryInto;
 use std::marker::PhantomData;
 use std::num::NonZeroUsize;
-use tracing::debug;
 
 /// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
 /// Used mainly for Lazy positions and lengths.
@@ -51,7 +50,7 @@ impl FixedSizeEncoding for Option<$ty> {
                 }
                 match b[0] - 1 {
                     $(${index()} => Some($($pat)*),)*
-                    _ => panic!("Unexpected ImplPolarity code: {:?}", b[0]),
+                    _ => panic!("Unexpected {} code: {:?}", stringify!($ty), b[0]),
                 }
             }
 
@@ -141,6 +140,21 @@ impl FixedSizeEncoding for Option<$ty> {
     }
 }
 
+fixed_size_enum! {
+    ty::AssocItemContainer {
+        ( TraitContainer )
+        ( ImplContainer  )
+    }
+}
+
+fixed_size_enum! {
+    MacroKind {
+        ( Attr   )
+        ( Bang   )
+        ( Derive )
+    }
+}
+
 // We directly encode `DefPathHash` because a `LazyValue` would incur a 25% cost.
 impl FixedSizeEncoding for Option<DefPathHash> {
     type ByteArray = [u8; 16];
index 008d2c7091c1ea56ef61e1e580b3db46c2520621..cca17a4eccd3afc52243554f13277b9006f8fd44 100644 (file)
@@ -7,34 +7,35 @@ edition = "2021"
 doctest = false
 
 [dependencies]
-rustc_arena = { path = "../rustc_arena" }
 bitflags = "1.2.1"
+chalk-ir = "0.80.0"
 either = "1.5.0"
 gsgdt = "0.1.2"
-tracing = "0.1"
-rustc-rayon = { version = "0.4.0", optional = true }
-rustc-rayon-core = { version = "0.4.0", optional = true }
 polonius-engine = "0.13.0"
+rand = "0.8.4"
+rand_xoshiro = "0.6.0"
 rustc_apfloat = { path = "../rustc_apfloat" }
+rustc_arena = { path = "../rustc_arena" }
+rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
-rustc_feature = { path = "../rustc_feature" }
-rustc_hir = { path = "../rustc_hir" }
-rustc_target = { path = "../rustc_target" }
-rustc_macros = { path = "../rustc_macros" }
 rustc_data_structures = { path = "../rustc_data_structures" }
-rustc_query_system = { path = "../rustc_query_system" }
 rustc_errors = { path = "../rustc_errors" }
+rustc_feature = { path = "../rustc_feature" }
 rustc_graphviz = { path = "../rustc_graphviz" }
+rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
+rustc_macros = { path = "../rustc_macros" }
+rustc_query_system = { path = "../rustc_query_system" }
+rustc-rayon-core = { version = "0.4.0", optional = true }
+rustc-rayon = { version = "0.4.0", optional = true }
 rustc_serialize = { path = "../rustc_serialize" }
-rustc_ast = { path = "../rustc_ast" }
-rustc_span = { path = "../rustc_span" }
-chalk-ir = "0.80.0"
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 rustc_session = { path = "../rustc_session" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
 rustc_type_ir = { path = "../rustc_type_ir" }
-rand = "0.8.4"
-rand_xoshiro = "0.6.0"
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
+tracing = "0.1"
 
 [features]
 rustc_use_parallel_compiler = ["rustc-rayon", "rustc-rayon-core"]
index 78080fcd581f6e0cdca918dd399939a61945d8ca..752cbdeae6b25df2eb76ad53c4db05c3edbfb534 100644 (file)
@@ -86,7 +86,7 @@ pub fn as_mut_preserves_cfg(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockDa
     ///
     /// You will only ever need this if you have also called [`BasicBlocks::as_mut_preserves_cfg`].
     /// All other methods that allow you to mutate the basic blocks also call this method
-    /// themselves, thereby avoiding any risk of accidentaly cache invalidation.
+    /// themselves, thereby avoiding any risk of accidentally cache invalidation.
     pub fn invalidate_cfg_cache(&mut self) {
         self.predecessor_cache.invalidate();
         self.switch_source_cache.invalidate();
index 7784449d605e7f476758a708dd0bb353f958f8d1..f3676604bb0e64b34b6d6d6e38eea52be4768683 100644 (file)
@@ -1457,7 +1457,7 @@ pub struct PlaceRef<'tcx> {
 // Once we stop implementing `Ord` for `DefId`,
 // this impl will be unnecessary. Until then, we'll
 // leave this impl in place to prevent re-adding a
-// dependnecy on the `Ord` impl for `DefId`
+// dependency on the `Ord` impl for `DefId`
 impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
 
 impl<'tcx> Place<'tcx> {
@@ -2267,7 +2267,7 @@ pub fn from_anon_const(
         Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id), param_env)
     }
 
-    #[instrument(skip(tcx), level = "debug")]
+    #[instrument(skip(tcx), level = "debug", ret)]
     pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
         let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
         let body_id = match tcx.hir().get(hir_id) {
@@ -2305,21 +2305,18 @@ pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
         let substs =
             ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty })
                 .substs;
-        let uneval_const = tcx.mk_const(ty::ConstS {
+        debug_assert!(!substs.has_free_regions());
+        Self::Ty(tcx.mk_const(ty::ConstS {
             kind: ty::ConstKind::Unevaluated(ty::Unevaluated {
                 def: ty::WithOptConstParam::unknown(def_id).to_global(),
                 substs,
                 promoted: None,
             }),
             ty,
-        });
-        debug!(?uneval_const);
-        debug_assert!(!uneval_const.has_free_regions());
-
-        Self::Ty(uneval_const)
+        }))
     }
 
-    #[instrument(skip(tcx), level = "debug")]
+    #[instrument(skip(tcx), level = "debug", ret)]
     fn from_opt_const_arg_anon_const(
         tcx: TyCtxt<'tcx>,
         def: ty::WithOptConstParam<LocalDefId>,
@@ -2402,24 +2399,21 @@ fn from_opt_const_arg_anon_const(
 
         match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
             Ok(val) => {
-                debug!("evaluated const value: {:?}", val);
+                debug!("evaluated const value");
                 Self::Val(val, ty)
             }
             Err(_) => {
                 debug!("error encountered during evaluation");
                 // Error was handled in `const_eval_resolve`. Here we just create a
                 // new unevaluated const and error hard later in codegen
-                let ty_const = tcx.mk_const(ty::ConstS {
+                Self::Ty(tcx.mk_const(ty::ConstS {
                     kind: ty::ConstKind::Unevaluated(ty::Unevaluated {
                         def: def.to_global(),
                         substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()),
                         promoted: None,
                     }),
                     ty,
-                });
-                debug!(?ty_const);
-
-                Self::Ty(ty_const)
+                }))
             }
         }
     }
index 3426f5f43f0bad4878b8b88d778a526eefd13b57..d7b9d59eced51eee813171db11a11635a881378e 100644 (file)
@@ -332,7 +332,7 @@ pub enum StatementKind<'tcx> {
     /// First, all three operands are evaluated. `src` and `dest` must each be a reference, pointer,
     /// or `Box` pointing to the same type `T`. `count` must evaluate to a `usize`. Then, `src` and
     /// `dest` are dereferenced, and `count * size_of::<T>()` bytes beginning with the first byte of
-    /// the `src` place are copied to the continguous range of bytes beginning with the first byte
+    /// the `src` place are copied to the contiguous range of bytes beginning with the first byte
     /// of `dest`.
     ///
     /// **Needs clarification**: In what order are operands computed and dereferenced? It should
@@ -378,7 +378,7 @@ pub enum FakeReadCause {
     /// Some(closure_def_id).
     /// Otherwise, the value of the optional LocalDefId will be None.
     //
-    // We can use LocaDefId here since fake read statements are removed
+    // We can use LocalDefId here since fake read statements are removed
     // before codegen in the `CleanupNonCodegenStatements` pass.
     ForMatchedPlace(Option<LocalDefId>),
 
index ddca9820da1aec8cacbd60356284744fa2c7fc03..abaef0354ade5b319f5ca9560aa19eced27113b8 100644 (file)
     /// Used by rustdoc.
     query rendered_const(def_id: DefId) -> String {
         storage(ArenaCacheSelector<'tcx>)
-        desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) }
+        desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) }
         cache_on_disk_if { def_id.is_local() }
         separate_provide_extern
     }
index 2465f8e2533e80caa36077c63815cf6b45a3a6f3..0a2819feecf0bb80a4b78f9e80d1571cc33993cf 100644 (file)
@@ -115,7 +115,7 @@ pub fn is_from_trait(&self) -> bool {
         matches!(self, Node::Trait(..))
     }
 
-    /// Trys to find the associated item that implements `trait_item_def_id`
+    /// Tries to find the associated item that implements `trait_item_def_id`
     /// defined in this node.
     ///
     /// If this returns `None`, the item can potentially still be found in
index f8792edc017b2d8d3a5da6affae1e10302511dce..2eb5cffa6bc44360a25dc9cb7f578ac7addc31ff 100644 (file)
@@ -65,8 +65,6 @@ pub fn from_opt_const_arg_anon_const(
         tcx: TyCtxt<'tcx>,
         def: ty::WithOptConstParam<LocalDefId>,
     ) -> Self {
-        debug!("Const::from_anon_const(def={:?})", def);
-
         let body_id = match tcx.hir().get_by_def_id(def.did) {
             hir::Node::AnonConst(ac) => ac.body,
             _ => span_bug!(
index 0f34aa10a1257f03f53f349faf70e45e15ecd1e8..052bb1263c98f8d022e4577b4c26d275f1bd4afa 100644 (file)
@@ -1498,17 +1498,17 @@ pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + 'tcx {
         // Create a dependency to the crate to be sure we re-execute this when the amount of
         // definitions change.
         self.ensure().hir_crate(());
-        // Leak a read lock once we start iterating on definitions, to prevent adding new onces
+        // Leak a read lock once we start iterating on definitions, to prevent adding new ones
         // while iterating.  If some query needs to add definitions, it should be `ensure`d above.
         let definitions = self.definitions.leak();
         definitions.iter_local_def_id()
     }
 
     pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable {
-        // Create a dependency to the crate to be sure we reexcute this when the amount of
+        // Create a dependency to the crate to be sure we re-execute this when the amount of
         // definitions change.
         self.ensure().hir_crate(());
-        // Leak a read lock once we start iterating on definitions, to prevent adding new onces
+        // Leak a read lock once we start iterating on definitions, to prevent adding new ones
         // while iterating.  If some query needs to add definitions, it should be `ensure`d above.
         let definitions = self.definitions.leak();
         definitions.def_path_table()
@@ -1517,10 +1517,10 @@ pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable {
     pub fn def_path_hash_to_def_index_map(
         self,
     ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap {
-        // Create a dependency to the crate to be sure we reexcute this when the amount of
+        // Create a dependency to the crate to be sure we re-execute this when the amount of
         // definitions change.
         self.ensure().hir_crate(());
-        // Leak a read lock once we start iterating on definitions, to prevent adding new onces
+        // Leak a read lock once we start iterating on definitions, to prevent adding new ones
         // while iterating.  If some query needs to add definitions, it should be `ensure`d above.
         let definitions = self.definitions.leak();
         definitions.def_path_hash_to_def_index_map()
@@ -1829,9 +1829,9 @@ pub mod tls {
     use crate::dep_graph::TaskDepsRef;
     use crate::ty::query;
     use rustc_data_structures::sync::{self, Lock};
-    use rustc_data_structures::thin_vec::ThinVec;
     use rustc_errors::Diagnostic;
     use std::mem;
+    use thin_vec::ThinVec;
 
     #[cfg(not(parallel_compiler))]
     use std::cell::Cell;
index 5e96e278b9cad5f76008b2878a8bdfbe9c89b9af..cb46a9dba579fd6d40a75e0b835cba021d2a7661 100644 (file)
@@ -353,7 +353,7 @@ fn fold_binder<T: TypeFoldable<'tcx>>(
         t
     }
 
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self), level = "debug", ret)]
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
             ty::ReLateBound(debruijn, _) if debruijn < self.current_index => {
index 42b5d5a6efd7ec5435b6d141aef29c5858bf2f20..a1d980af921afeb05590a6ae722dc0436146dbb5 100644 (file)
@@ -266,7 +266,7 @@ pub fn own_substs_no_defaults(
         // Filter the default arguments.
         //
         // This currently uses structural equality instead
-        // of semantic equivalance. While not ideal, that's
+        // of semantic equivalence. While not ideal, that's
         // good enough for now as this should only be used
         // for diagnostics anyways.
         own_params.end -= self
index 980bb8e861551fc4832fe76d32c9bc6678542dfb..26b60e4f33988b4885dac0fc0d7cfb7a23fc549f 100644 (file)
@@ -756,7 +756,7 @@ fn layout_of_uncached(&self, ty: Ty<'tcx>) -> Result<Layout<'tcx>, LayoutError<'
                 // * the element type and length of the single array field, if
                 // the first field is of array type, or
                 //
-                // * the homogenous field type and the number of fields.
+                // * the homogeneous field type and the number of fields.
                 let (e_ty, e_len, is_array) = if let ty::Array(e_ty, _) = f0_ty.kind() {
                     // First ADT field is an array:
 
index 9d8a811659433f6b36b4b2043f714a228d24545d..cac8560ce1c320229dfe789a27695b039160ab26 100644 (file)
@@ -188,13 +188,11 @@ struct NormalizeAfterErasingRegionsFolder<'tcx> {
 }
 
 impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
-    #[instrument(skip(self), level = "debug")]
     fn normalize_generic_arg_after_erasing_regions(
         &self,
         arg: ty::GenericArg<'tcx>,
     ) -> ty::GenericArg<'tcx> {
         let arg = self.param_env.and(arg);
-        debug!(?arg);
 
         self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| bug!(
                 "Failed to normalize {:?}, maybe try to call `try_normalize_erasing_regions` instead",
index 19b8f27bc95caae910d164128766cbefb533a254..ca24c0d1ce386fc5801be18526395b68e3589677 100644 (file)
@@ -55,6 +55,7 @@ impl $crate::ty::ParameterizedOverTcx for $ty {
     crate::middle::exported_symbols::SymbolExportInfo,
     crate::middle::resolve_lifetime::ObjectLifetimeDefault,
     crate::mir::ConstQualifs,
+    ty::AssocItemContainer,
     ty::Generics,
     ty::ImplPolarity,
     ty::ReprOptions,
index 541dace5cc2bb5f31964d42799e792ebdab6af2d..ac79949fca5cc40d7ed5391e8e96557ff459e36d 100644 (file)
@@ -256,7 +256,6 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait
 }
 
 // Query provider for `incoherent_impls`.
-#[instrument(level = "debug", skip(tcx))]
 pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] {
     let mut impls = Vec::new();
 
index 591bb7831b5b6375ab98b17f28ecfb389dc26e5a..a3837512bce2652c1155ca2672e06bcb1cd07bdb 100644 (file)
@@ -627,7 +627,7 @@ pub fn static_ptr_ty(self, def_id: DefId) -> Ty<'tcx> {
     }
 
     /// Expands the given impl trait type, stopping if the type is recursive.
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self), level = "debug", ret)]
     pub fn try_expand_impl_trait_type(
         self,
         def_id: DefId,
@@ -644,7 +644,6 @@ pub fn try_expand_impl_trait_type(
         };
 
         let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap();
-        trace!(?expanded_type);
         if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
     }
 
index 5365067209af98042864cd0a8410a0a20170e722..5e042c3acfce25897621b4b4215df93554a266ed 100644 (file)
@@ -84,7 +84,7 @@ fn has_escaping_bound_vars(&self) -> bool {
         self.has_vars_bound_at_or_above(ty::INNERMOST)
     }
 
-    #[instrument(level = "trace")]
+    #[instrument(level = "trace", ret)]
     fn has_type_flags(&self, flags: TypeFlags) -> bool {
         self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags)
     }
@@ -560,7 +560,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
     type BreakTy = FoundFlags;
 
     #[inline]
-    #[instrument(skip(self), level = "trace")]
+    #[instrument(skip(self), level = "trace", ret)]
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = t.flags();
         trace!(t.flags=?t.flags());
@@ -572,7 +572,7 @@ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
     }
 
     #[inline]
-    #[instrument(skip(self), level = "trace")]
+    #[instrument(skip(self), level = "trace", ret)]
     fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = r.type_flags();
         trace!(r.flags=?flags);
@@ -584,7 +584,7 @@ fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
     }
 
     #[inline]
-    #[instrument(level = "trace")]
+    #[instrument(level = "trace", ret)]
     fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = FlagComputation::for_const(c);
         trace!(r.flags=?flags);
@@ -596,7 +596,7 @@ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
     }
 
     #[inline]
-    #[instrument(level = "trace")]
+    #[instrument(level = "trace", ret)]
     fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = FlagComputation::for_unevaluated_const(uv);
         trace!(r.flags=?flags);
@@ -608,7 +608,7 @@ fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::
     }
 
     #[inline]
-    #[instrument(level = "trace")]
+    #[instrument(level = "trace", ret)]
     fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
         debug!(
             "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}",
index 080dab0303166ea9747f5fce80ab106f5e43d134..0e5cd6199ac9fb69ec7d3ecd7e23c42c3c483669 100644 (file)
@@ -155,7 +155,7 @@ pub(crate) fn then_else_break(
     ///
     /// * From each pre-binding block to the next pre-binding block.
     /// * From each otherwise block to the next pre-binding block.
-    #[tracing::instrument(level = "debug", skip(self, arms))]
+    #[instrument(level = "debug", skip(self, arms))]
     pub(crate) fn match_expr(
         &mut self,
         destination: Place<'tcx>,
@@ -702,7 +702,7 @@ pub(crate) fn storage_live_binding(
         let local_id = self.var_local_id(var, for_guard);
         let source_info = self.source_info(span);
         self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
-        // Altough there is almost always scope for given variable in corner cases
+        // Although there is almost always scope for given variable in corner cases
         // like #92893 we might get variable with no scope.
         if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) && schedule_drop{
             self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
index 0c2b117453fe9ed5785a444d4ee0deff4186f34b..9ed1c064d2b7f3ea651eb303c9e7824dbbdba8ec 100644 (file)
@@ -268,7 +268,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
                 // the overall method call for better diagnostics. args[0]
                 // is guaranteed to exist, since a method call always has a receiver.
                 let old_adjustment_span = self.adjustment_span.replace((args[0].hir_id, expr_span));
-                tracing::info!("Using method span: {:?}", expr.span);
+                info!("Using method span: {:?}", expr.span);
                 let args = self.mirror_exprs(args);
                 self.adjustment_span = old_adjustment_span;
                 ExprKind::Call {
index f7351a4caa9545250d59dcbf5c21a146724fcc27..b84a84976c7d9371339158086d26d4c3d472a92b 100644 (file)
@@ -77,7 +77,7 @@ fn new(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) -> Cx<'tcx> {
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     pub(crate) fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> {
         let p = match self.tcx.hir().get(p.hir_id) {
             Node::Pat(p) => p,
index f2045ac19cac42920293ecc3b71d865cf5cb29a9..210d77c66e7002ff4928a9f26e45af4fee5117c8 100644 (file)
@@ -19,7 +19,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     /// Converts an evaluated constant to a pattern (if possible).
     /// This means aggregate values (like structs and enums) are converted
     /// to a pattern that matches the value (as if you'd compared via structural equality).
-    #[instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn const_to_pat(
         &self,
         cv: mir::ConstantKind<'tcx>,
@@ -27,13 +27,10 @@ pub(super) fn const_to_pat(
         span: Span,
         mir_structural_match_violation: bool,
     ) -> Pat<'tcx> {
-        let pat = self.tcx.infer_ctxt().enter(|infcx| {
+        self.tcx.infer_ctxt().enter(|infcx| {
             let mut convert = ConstToPat::new(self, id, span, infcx);
             convert.to_pat(cv, mir_structural_match_violation)
-        });
-
-        debug!(?pat);
-        pat
+        })
     }
 }
 
index 443626d14b9f67c636bed05f1a7483d9366ce79d..319183eb9b33fc7e0a81f15cedd6f87cd641ad88 100644 (file)
@@ -791,7 +791,7 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>(
 /// `is_under_guard` is used to inform if the pattern has a guard. If it
 /// has one it must not be inserted into the matrix. This shouldn't be
 /// relied on for soundness.
-#[instrument(level = "debug", skip(cx, matrix, hir_id))]
+#[instrument(level = "debug", skip(cx, matrix, hir_id), ret)]
 fn is_useful<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     matrix: &Matrix<'p, 'tcx>,
@@ -917,7 +917,6 @@ fn is_useful<'p, 'tcx>(
         v.head().set_reachable();
     }
 
-    debug!(?ret);
     ret
 }
 
index 170616d4b42ce76e62a6f12021907822b351e32c..6ec5e9e113d1b4b85f6134c36fc1e4e75c89e529 100644 (file)
@@ -419,7 +419,6 @@ fn collect_items_rec<'tcx>(
         // We've been here already, no need to search again.
         return;
     }
-    debug!("BEGIN collect_items_rec({})", starting_point.node);
 
     let mut neighbors = MonoItems { compute_inlining: true, tcx, items: Vec::new() };
     let recursion_depth_reset;
@@ -545,8 +544,6 @@ fn collect_items_rec<'tcx>(
     if let Some((def_id, depth)) = recursion_depth_reset {
         recursion_depths.insert(def_id, depth);
     }
-
-    debug!("END collect_items_rec({})", starting_point.node);
 }
 
 /// Format instance name that is already known to be too long for rustc.
@@ -1148,23 +1145,18 @@ fn find_vtable_types_for_unsizing<'tcx>(
     }
 }
 
-#[instrument(skip(tcx), level = "debug")]
+#[instrument(skip(tcx), level = "debug", ret)]
 fn create_fn_mono_item<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: Instance<'tcx>,
     source: Span,
 ) -> Spanned<MonoItem<'tcx>> {
-    debug!("create_fn_mono_item(instance={})", instance);
-
     let def_id = instance.def_id();
     if tcx.sess.opts.unstable_opts.profile_closures && def_id.is_local() && tcx.is_closure(def_id) {
         crate::util::dump_closure_profile(tcx, instance);
     }
 
-    let respanned = respan(source, MonoItem::Fn(instance.polymorphize(tcx)));
-    debug!(?respanned);
-
-    respanned
+    respan(source, MonoItem::Fn(instance.polymorphize(tcx)))
 }
 
 /// Creates a `MonoItem` for each method that is referenced by the vtable for
@@ -1309,7 +1301,7 @@ fn is_root(&self, def_id: LocalDefId) -> bool {
     #[instrument(skip(self), level = "debug")]
     fn push_if_root(&mut self, def_id: LocalDefId) {
         if self.is_root(def_id) {
-            debug!("RootCollector::push_if_root: found root def_id={:?}", def_id);
+            debug!("found root");
 
             let instance = Instance::mono(self.tcx, def_id.to_def_id());
             self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP));
index 6e4ab2a35c3375e0c43b274ae285bf7fbd22f28d..af4b35db3bace8bbaefb9b7808e01bd05aaf75ed 100644 (file)
@@ -33,7 +33,6 @@ pub fn provide(providers: &mut Providers) {
 ///
 /// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all
 /// parameters are used).
-#[instrument(level = "debug", skip(tcx))]
 fn unused_generic_params<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: ty::InstanceDef<'tcx>,
index 847e64dc2a2f1c3469187c30c1dba8569b50e933..6a4d2df1ead158b8612f7b4ad0c14cf8f26e5c4b 100644 (file)
@@ -13,7 +13,7 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In
         .append(true)
         .open(&format!("closure_profile_{}.csv", std::process::id()))
     else {
-        eprintln!("Cound't open file for writing closure profile");
+        eprintln!("Couldn't open file for writing closure profile");
         return;
     };
 
index 848e142e59ce9b4dcc47b41a37fb4d46b073d330..63819a2f98df57b17a49a426500e48111befcfa4 100644 (file)
@@ -14,8 +14,6 @@
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{edition::Edition, BytePos, Pos, Span};
 
-use tracing::debug;
-
 mod tokentrees;
 mod unescape_error_reporting;
 mod unicode_chars;
index 273827864f1a405295e2f7f0499d605c82f290f0..77c4fadab45eacc9f61d5fd4cb4d361cedc62666 100644 (file)
@@ -20,13 +20,9 @@ pub(crate) fn emit_unescape_error(
     range: Range<usize>,
     error: EscapeError,
 ) {
-    tracing::debug!(
+    debug!(
         "emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}",
-        lit,
-        span_with_quotes,
-        mode,
-        range,
-        error
+        lit, span_with_quotes, mode, range, error
     );
     let last_char = || {
         let c = lit[range.clone()].chars().rev().next().unwrap();
index 72ab96b5ca67032aa9e4c83509e42a5b4ced6613..77a6bde1c164e43ac43c73d6a36e50929da3a671 100644 (file)
@@ -7,8 +7,6 @@
 use rustc_span::{sym, BytePos, Span};
 use std::convert::TryInto;
 
-use tracing::debug;
-
 // Public for rustfmt usage
 #[derive(Debug)]
 pub enum InnerAttrPolicy<'a> {
index b564f4ad92cff89cf28a5dcf03cadd4d33ce4c2d..f353ee6df9bc173f77a5d1e58117f9664fa145dd 100644 (file)
@@ -355,7 +355,7 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
             && matches!(self.capture_state.capturing, Capturing::Yes)
             && has_cfg_or_cfg_attr(final_attrs)
         {
-            let attr_data = AttributesData { attrs: final_attrs.to_vec().into(), tokens };
+            let attr_data = AttributesData { attrs: final_attrs.iter().cloned().collect(), tokens };
 
             // Replace the entire AST node that we just parsed, including attributes,
             // with a `FlatToken::AttrTarget`. If this AST node is inside an item
index f0ea1dfe297f2fb5c99da39460a21b693bc9c980..dd806e2130e9b062f62a58603524534328ab3298 100644 (file)
@@ -29,7 +29,6 @@
 use std::mem::take;
 
 use crate::parser;
-use tracing::{debug, trace};
 
 const TURBOFISH_SUGGESTION_STR: &str =
     "use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments";
index c85416095144dbe171532cbfccd61127a01df84e..d4828a201207b25182e927e01a480a7435e9418b 100644 (file)
@@ -944,13 +944,18 @@ pub(super) fn parse_dot_or_call_expr_with(
         // Stitch the list of outer attributes onto the return value.
         // A little bit ugly, but the best way given the current code
         // structure
-        self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| {
-            expr.map(|mut expr| {
-                attrs.extend(expr.attrs);
-                expr.attrs = attrs;
-                expr
+        let res = self.parse_dot_or_call_expr_with_(e0, lo);
+        if attrs.is_empty() {
+            res
+        } else {
+            res.map(|expr| {
+                expr.map(|mut expr| {
+                    attrs.extend(expr.attrs);
+                    expr.attrs = attrs;
+                    expr
+                })
             })
-        })
+        }
     }
 
     fn parse_dot_or_call_expr_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
@@ -1578,7 +1583,7 @@ fn visit_expr_post(&mut self, ex: &'ast Expr) {
                     Applicability::MachineApplicable,
                 );
 
-                // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`.
+                // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`.
                 let stmt = self.mk_stmt(span, StmtKind::Expr(expr));
                 let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span);
                 self.mk_expr(span, ExprKind::Block(blk, label))
@@ -2578,7 +2583,7 @@ fn parse_arm_body_missing_braces(
     }
 
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
-        // Used to check the `let_chains` and `if_let_guard` features mostly by scaning
+        // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
         // `&&` tokens.
         fn check_let_expr(expr: &Expr) -> (bool, bool) {
             match expr.kind {
index b743162a7e4d0809a234454ccea601335bb9cc2a..5b75d1d5f221daa69dbaf0d34fc1a0462ec756ba 100644 (file)
@@ -22,7 +22,6 @@
 
 use std::convert::TryFrom;
 use std::mem;
-use tracing::debug;
 
 impl<'a> Parser<'a> {
     /// Parses a source module as a crate. This is the main entry point for the parser.
index f6516d3bd4543604155cf9bde1010d54c01b65cb..5c8f374255c7fc91635b88391161d743150ecced 100644 (file)
@@ -37,7 +37,6 @@
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::{Span, DUMMY_SP};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use tracing::debug;
 
 use std::ops::Range;
 use std::{cmp, mem, slice};
@@ -281,7 +280,7 @@ fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
                         if delim != Delimiter::Invisible {
                             return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone);
                         }
-                        // No open delimeter to return; continue on to the next iteration.
+                        // No open delimiter to return; continue on to the next iteration.
                     }
                 };
             } else if let Some(frame) = self.stack.pop() {
index fc7fb866f110f5dd91e85fa1f0a0d79ea8a47cef..fdc1af27f82e4206e3e7d95eec13f6ffb59a4a92 100644 (file)
@@ -13,7 +13,6 @@
 use rustc_span::symbol::{kw, sym, Ident};
 
 use std::mem;
-use tracing::debug;
 
 /// Specifies how to parse a path.
 #[derive(Copy, Clone, PartialEq)]
index 399d00b403a6ba784eb5a88791f8ccbf33c25aa1..fb1d724d9d83b5a30a39cff45d5de51b6758af66 100644 (file)
@@ -49,7 +49,7 @@ fn new() -> Node {
 ///
 /// For example, `ast::Visitor` has `visit_ident`, but `Ident`s are always
 /// stored inline within other AST nodes, so we don't implement `visit_ident`
-/// here. In constrast, we do implement `visit_expr` because `ast::Expr` is
+/// here. In contrast, we do implement `visit_expr` because `ast::Expr` is
 /// always stored as `P<ast::Expr>`, and every such expression should be
 /// measured separately.
 ///
index 5b79dd3d3ef25d3a14e0760bcb2587773e14dacd..2a6889af7c2c92aab88860e449ae341e8e07917f 100644 (file)
@@ -1573,7 +1573,7 @@ fn check_unused_vars_in_pat(
         }
     }
 
-    #[tracing::instrument(skip(self), level = "INFO")]
+    #[instrument(skip(self), level = "INFO")]
     fn report_unused(
         &self,
         hir_ids_and_spans: Vec<(HirId, Span, Span)>,
index 5d562f18a815814785602fff35961933b7b32d0a..a9271761358c9c7f1316c10f5553ab703b5f4f03 100644 (file)
@@ -8,6 +8,9 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
+#[macro_use]
+extern crate tracing;
+
 mod errors;
 
 use rustc_ast::MacroDef;
@@ -1784,7 +1787,7 @@ fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display)
     fn leaks_private_dep(&self, item_id: DefId) -> bool {
         let ret = self.required_visibility.is_public() && self.tcx.is_private_dep(item_id.krate);
 
-        tracing::debug!("leaks_private_dep(item_id={:?})={}", item_id, ret);
+        debug!("leaks_private_dep(item_id={:?})={}", item_id, ret);
         ret
     }
 }
index c37ae4f32536bd3bc8a41d0f8dadac8b21b01522..e7f12caaf33e523d03598df61ada434300698815 100644 (file)
@@ -8,7 +8,6 @@ doctest = false
 
 [dependencies]
 measureme = "10.0.0"
-rustc-rayon-core = { version = "0.4.0", optional = true }
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
@@ -17,10 +16,12 @@ rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_query_system = { path = "../rustc_query_system" }
+rustc-rayon-core = { version = "0.4.0", optional = true }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
+thin-vec = "0.2.8"
 tracing = "0.1"
 
 [features]
index eab627c5d4c27edde273635a053f246ccf31be51..eabb316614747365eb4f1373bd2383aabca814f1 100644 (file)
@@ -4,6 +4,9 @@
 
 use crate::keys::Key;
 use crate::{on_disk_cache, Queries};
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::Lock;
+use rustc_errors::{Diagnostic, Handler};
 use rustc_middle::dep_graph::{self, DepKind, DepNodeIndex, SerializedDepNodeIndex};
 use rustc_middle::ty::tls::{self, ImplicitCtxt};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_query_system::query::{
     QueryContext, QueryJobId, QueryMap, QuerySideEffects, QueryStackFrame,
 };
-
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::Lock;
-use rustc_data_structures::thin_vec::ThinVec;
-use rustc_errors::{Diagnostic, Handler};
-
 use std::any::Any;
 use std::num::NonZeroU64;
+use thin_vec::ThinVec;
 
 #[derive(Copy, Clone)]
 pub struct QueryCtxt<'tcx> {
@@ -557,7 +555,7 @@ fn try_mark_green(&'tcx self, tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode)
 
             $($(#[$attr])*
             #[inline(always)]
-            #[tracing::instrument(level = "trace", skip(self, tcx))]
+            #[tracing::instrument(level = "trace", skip(self, tcx), ret)]
             fn $name(
                 &'tcx self,
                 tcx: TyCtxt<'tcx>,
index b7787aeb8f7655a60b4a114adc36c5536982886b..bafc6b0a082334826d40a6c0aa9980b05b91c3e7 100644 (file)
@@ -7,9 +7,8 @@ edition = "2021"
 doctest = false
 
 [dependencies]
+parking_lot = "0.11"
 rustc_arena = { path = "../rustc_arena" }
-tracing = "0.1"
-rustc-rayon-core = { version = "0.4.0", optional = true }
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
@@ -17,12 +16,14 @@ rustc_feature = { path = "../rustc_feature" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
+rustc-rayon-core = { version = "0.4.0", optional = true }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-parking_lot = "0.11"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
+tracing = "0.1"
 
 [features]
 rustc_use_parallel_compiler = ["rustc-rayon-core"]
diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs
new file mode 100644 (file)
index 0000000..5f992ec
--- /dev/null
@@ -0,0 +1,73 @@
+use rustc_errors::AddSubdiagnostic;
+use rustc_span::Span;
+
+pub struct CycleStack {
+    pub span: Span,
+    pub desc: String,
+}
+
+impl AddSubdiagnostic for CycleStack {
+    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+        diag.span_note(self.span, &format!("...which requires {}...", self.desc));
+    }
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum StackCount {
+    #[note(query_system::cycle_stack_single)]
+    Single,
+    #[note(query_system::cycle_stack_multiple)]
+    Multiple,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum Alias {
+    #[note(query_system::cycle_recursive_ty_alias)]
+    #[help(query_system::cycle_recursive_ty_alias_help1)]
+    #[help(query_system::cycle_recursive_ty_alias_help2)]
+    Ty,
+    #[note(query_system::cycle_recursive_trait_alias)]
+    Trait,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(query_system::cycle_usage)]
+pub struct CycleUsage {
+    #[primary_span]
+    pub span: Span,
+    pub usage: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(query_system::cycle, code = "E0391")]
+pub struct Cycle {
+    #[primary_span]
+    pub span: Span,
+    pub stack_bottom: String,
+    #[subdiagnostic]
+    pub cycle_stack: Vec<CycleStack>,
+    #[subdiagnostic]
+    pub stack_count: StackCount,
+    #[subdiagnostic]
+    pub alias: Option<Alias>,
+    #[subdiagnostic]
+    pub cycle_usage: Option<CycleUsage>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(query_system::reentrant)]
+pub struct Reentrant;
+
+#[derive(SessionDiagnostic)]
+#[diag(query_system::increment_compilation)]
+#[help]
+#[note(query_system::increment_compilation_note1)]
+#[note(query_system::increment_compilation_note2)]
+pub struct IncrementCompilation {
+    pub run_cmd: String,
+    pub dep_node: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(query_system::query_overflow)]
+pub struct QueryOverflow;
index 68284dcaa0be8213ef2f9add4b1b416a98e2297e..7067bc5f09cfc07229179036d4042896b836611b 100644 (file)
@@ -5,6 +5,8 @@
 #![feature(min_specialization)]
 #![feature(extern_types)]
 #![allow(rustc::potential_query_instability)]
+// #![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate tracing;
@@ -15,5 +17,6 @@
 
 pub mod cache;
 pub mod dep_graph;
+mod error;
 pub mod ich;
 pub mod query;
index 6d2aff38172facc9f4a0157dae89a56460442abb..ddb5cd0634407c5d09e9341b2018789ef415ca6c 100644 (file)
@@ -1,12 +1,11 @@
+use crate::error::CycleStack;
 use crate::query::plumbing::CycleError;
 use crate::query::{QueryContext, QueryStackFrame};
-use rustc_hir::def::DefKind;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{
-    struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level,
-};
-use rustc_session::Session;
+use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level};
+use rustc_hir::def::DefKind;
+use rustc_session::{Session, SessionDiagnostic};
 use rustc_span::Span;
 
 use std::hash::Hash;
@@ -536,46 +535,44 @@ pub(crate) fn report_cycle<'a>(
     assert!(!stack.is_empty());
 
     let span = stack[0].query.default_span(stack[1 % stack.len()].span);
-    let mut err =
-        struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description);
+
+    let mut cycle_stack = Vec::new();
+
+    use crate::error::StackCount;
+    let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
 
     for i in 1..stack.len() {
         let query = &stack[i].query;
         let span = query.default_span(stack[(i + 1) % stack.len()].span);
-        err.span_note(span, &format!("...which requires {}...", query.description));
-    }
-
-    if stack.len() == 1 {
-        err.note(&format!("...which immediately requires {} again", stack[0].query.description));
-    } else {
-        err.note(&format!(
-            "...which again requires {}, completing the cycle",
-            stack[0].query.description
-        ));
-    }
-
-    if stack.iter().all(|entry| {
-        entry
-            .query
-            .def_kind
-            .map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias | DefKind::TraitAlias))
-    }) {
-        if stack.iter().all(|entry| {
-            entry.query.def_kind.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias))
-        }) {
-            err.note("type aliases cannot be recursive");
-            err.help("consider using a struct, enum, or union instead to break the cycle");
-            err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information");
-        } else {
-            err.note("trait aliases cannot be recursive");
-        }
+        cycle_stack.push(CycleStack { span, desc: query.description.to_owned() });
     }
 
+    let mut cycle_usage = None;
     if let Some((span, query)) = usage {
-        err.span_note(query.default_span(span), &format!("cycle used when {}", query.description));
+        cycle_usage = Some(crate::error::CycleUsage {
+            span: query.default_span(span),
+            usage: query.description,
+        });
     }
 
-    err
+    let alias = if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TyAlias)) {
+        Some(crate::error::Alias::Ty)
+    } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) {
+        Some(crate::error::Alias::Trait)
+    } else {
+        None
+    };
+
+    let cycle_diag = crate::error::Cycle {
+        span,
+        cycle_stack,
+        stack_bottom: stack[0].query.description.to_owned(),
+        alias,
+        cycle_usage: cycle_usage,
+        stack_count,
+    };
+
+    cycle_diag.into_diagnostic(&sess.parse_sess)
 }
 
 pub fn print_query_stack<CTX: QueryContext>(
index a1f2b081d43465f2515b88aead23859b8e53fd73..0b07bb64b6ae0b47e212e5451a23c0e78a3ab7dc 100644 (file)
 pub use self::config::{QueryConfig, QueryDescription, QueryVTable};
 
 use crate::dep_graph::{DepContext, DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
-
 use rustc_data_structures::sync::Lock;
-use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::Diagnostic;
 use rustc_hir::def::DefKind;
 use rustc_span::Span;
+use thin_vec::ThinVec;
 
 /// Description of a frame in the query stack.
 ///
@@ -125,6 +124,6 @@ fn start_query<R>(
     ) -> R;
 
     fn depth_limit_error(&self) {
-        self.dep_context().sess().fatal("queries overflow the depth limit!");
+        self.dep_context().sess().emit_fatal(crate::error::QueryOverflow);
     }
 }
index 6ae9147ff774fe1dfffb920e425cc08eddb1a125..7bbc22e8293a566ab9e2103ef9137bb5c07b359d 100644 (file)
@@ -14,7 +14,6 @@
 #[cfg(parallel_compiler)]
 use rustc_data_structures::sharded::Sharded;
 use rustc_data_structures::sync::Lock;
-use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError};
 use rustc_session::Session;
 use rustc_span::{Span, DUMMY_SP};
@@ -24,6 +23,7 @@
 use std::hash::Hash;
 use std::mem;
 use std::ptr;
+use thin_vec::ThinVec;
 
 pub struct QueryState<K> {
     #[cfg(parallel_compiler)]
@@ -618,16 +618,12 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D
     let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
 
     if old_in_panic {
-        sess.struct_err(
-            "internal compiler error: re-entrant incremental verify failure, suppressing message",
-        )
-        .emit();
+        sess.emit_err(crate::error::Reentrant);
     } else {
-        sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node))
-                .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd))
-                .note("Please follow the instructions below to create a bug report with the provided information")
-                .note("See <https://github.com/rust-lang/rust/issues/84970> for more information")
-                .emit();
+        sess.emit_err(crate::error::IncrementCompilation {
+            run_cmd,
+            dep_node: format!("{:?}", dep_node),
+        });
         panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result);
     }
 
index 3fba923d9fdf425c443300fb15eed97ba6ffdfd8..882a92c0ebb62d31257ba7d092d2104081dd22eb 100644 (file)
@@ -39,7 +39,7 @@ pub fn compute_access_levels<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
             visit::walk_crate(&mut visitor, krate);
         }
 
-        tracing::info!("resolve::access_levels: {:#?}", r.access_levels);
+        info!("resolve::access_levels: {:#?}", r.access_levels);
     }
 
     fn reset(&mut self) {
index 8f3b6009bd6ef8a5f31fa6387c5badaceb443faf..51e8c24b9c25a51066088753bf4ec5b00a261798 100644 (file)
@@ -36,7 +36,6 @@
 
 use std::cell::Cell;
 use std::ptr;
-use tracing::debug;
 
 type Res = def::Res<NodeId>;
 
@@ -1030,7 +1029,7 @@ fn build_reduced_graph_for_external_crate_res(&mut self, child: ModChild) {
                 self.insert_field_names(def_id, field_names);
             }
             Res::Def(DefKind::AssocFn, def_id) => {
-                if cstore.fn_has_self_parameter_untracked(def_id) {
+                if cstore.fn_has_self_parameter_untracked(def_id, self.r.session) {
                     self.r.has_self.insert(def_id);
                 }
             }
index 66641fb2cb248dc15446f722b0bb88f6f530a536..5955d8df16ee1edfa34f6edf2af8fca6c3cfc69c 100644 (file)
@@ -8,7 +8,6 @@
 use rustc_span::hygiene::LocalExpnId;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
-use tracing::debug;
 
 pub(crate) fn collect_definitions(
     resolver: &mut Resolver<'_>,
index 2d15b1b0a1b94fbb23740e9248407b6732a2d570..4fd6fe4e36c69093babe14a7b3b2e8c214fa83d4 100644 (file)
@@ -25,7 +25,6 @@
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Span};
-use tracing::debug;
 
 use crate::imports::{Import, ImportKind, ImportResolver};
 use crate::late::{PatternSource, Rib};
index 2afba94d793af192258f03affa50a5c65e63093c..b84a610833ddf6a8faa398d6e070d0ef93b25136 100644 (file)
@@ -273,7 +273,7 @@ fn hygienic_lexical_parent(
     ///
     /// Invariant: This must only be called during main resolution, not during
     /// import resolution.
-    #[tracing::instrument(level = "debug", skip(self, ribs))]
+    #[instrument(level = "debug", skip(self, ribs))]
     pub(crate) fn resolve_ident_in_lexical_scope(
         &mut self,
         mut ident: Ident,
@@ -367,7 +367,7 @@ pub(crate) fn resolve_ident_in_lexical_scope(
     /// expansion and import resolution (perhaps they can be merged in the future).
     /// The function is used for resolving initial segments of macro paths (e.g., `foo` in
     /// `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition.
-    #[tracing::instrument(level = "debug", skip(self, scope_set))]
+    #[instrument(level = "debug", skip(self, scope_set))]
     pub(crate) fn early_resolve_ident_in_lexical_scope(
         &mut self,
         orig_ident: Ident,
@@ -708,7 +708,7 @@ struct Flags: u8 {
         Err(Determinacy::determined(determinacy == Determinacy::Determined || force))
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     pub(crate) fn maybe_resolve_ident_in_module(
         &mut self,
         module: ModuleOrUniformRoot<'a>,
@@ -720,7 +720,7 @@ pub(crate) fn maybe_resolve_ident_in_module(
             .map_err(|(determinacy, _)| determinacy)
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     pub(crate) fn resolve_ident_in_module(
         &mut self,
         module: ModuleOrUniformRoot<'a>,
@@ -734,7 +734,7 @@ pub(crate) fn resolve_ident_in_module(
             .map_err(|(determinacy, _)| determinacy)
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn resolve_ident_in_module_ext(
         &mut self,
         module: ModuleOrUniformRoot<'a>,
@@ -772,7 +772,7 @@ fn resolve_ident_in_module_ext(
         )
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn resolve_ident_in_module_unadjusted(
         &mut self,
         module: ModuleOrUniformRoot<'a>,
@@ -796,7 +796,7 @@ fn resolve_ident_in_module_unadjusted(
 
     /// Attempts to resolve `ident` in namespaces `ns` of `module`.
     /// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete.
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn resolve_ident_in_module_unadjusted_ext(
         &mut self,
         module: ModuleOrUniformRoot<'a>,
@@ -1059,7 +1059,7 @@ fn resolve_ident_in_module_unadjusted_ext(
     }
 
     /// Validate a local resolution (from ribs).
-    #[tracing::instrument(level = "debug", skip(self, all_ribs))]
+    #[instrument(level = "debug", skip(self, all_ribs))]
     fn validate_res_from_ribs(
         &mut self,
         rib_index: usize,
@@ -1294,7 +1294,7 @@ fn validate_res_from_ribs(
         res
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     pub(crate) fn maybe_resolve_path(
         &mut self,
         path: &[Segment],
@@ -1304,7 +1304,7 @@ pub(crate) fn maybe_resolve_path(
         self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None)
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     pub(crate) fn resolve_path(
         &mut self,
         path: &[Segment],
index c2491c6ebdec08892d03f255f2f991b4484b902a..619ce0462203ad84966576aab2aec8e2fcfb0c24 100644 (file)
@@ -23,8 +23,6 @@
 use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::Span;
 
-use tracing::*;
-
 use std::cell::Cell;
 use std::{mem, ptr};
 
index 693ec86616ee431e3ff70e2bbeeef37228ba70f2..dbe4d691f04cdf1a465dc3d1598498294f27b784 100644 (file)
@@ -32,7 +32,6 @@
 use rustc_span::source_map::{respan, Spanned};
 use std::collections::{hash_map::Entry, BTreeSet};
 use std::mem::{replace, take};
-use tracing::debug;
 
 mod diagnostics;
 pub(crate) mod lifetimes;
@@ -1390,7 +1389,7 @@ fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper:
         })
     }
 
-    #[tracing::instrument(level = "debug", skip(self, work))]
+    #[instrument(level = "debug", skip(self, work))]
     fn with_lifetime_rib<T>(
         &mut self,
         kind: LifetimeRibKind,
@@ -1404,7 +1403,7 @@ fn with_lifetime_rib<T>(
         ret
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) {
         let ident = lifetime.ident;
 
@@ -1508,7 +1507,7 @@ fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::Lifeti
         self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named);
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
         debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
 
@@ -1573,7 +1572,7 @@ fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
         self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
         let id = self.r.next_node_id();
         let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
@@ -1586,7 +1585,7 @@ fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
         self.resolve_anonymous_lifetime(&lt, true);
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn create_fresh_lifetime(&mut self, id: NodeId, ident: Ident, binder: NodeId) -> LifetimeRes {
         debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
         debug!(?ident.span);
@@ -1604,7 +1603,7 @@ fn create_fresh_lifetime(&mut self, id: NodeId, ident: Ident, binder: NodeId) ->
         res
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn resolve_elided_lifetimes_in_path(
         &mut self,
         path_id: NodeId,
@@ -1804,7 +1803,7 @@ fn resolve_elided_lifetimes_in_path(
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn record_lifetime_res(
         &mut self,
         id: NodeId,
@@ -1827,7 +1826,7 @@ fn record_lifetime_res(
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) {
         if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
             panic!(
@@ -1838,7 +1837,7 @@ fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) {
     }
 
     /// Perform resolution of a function signature, accounting for lifetime elision.
-    #[tracing::instrument(level = "debug", skip(self, inputs))]
+    #[instrument(level = "debug", skip(self, inputs))]
     fn resolve_fn_signature(
         &mut self,
         fn_id: NodeId,
@@ -3268,11 +3267,9 @@ fn smart_resolve_path_fragment(
         source: PathSource<'ast>,
         finalize: Finalize,
     ) -> PartialRes {
-        tracing::debug!(
+        debug!(
             "smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})",
-            qself,
-            path,
-            finalize,
+            qself, path, finalize,
         );
         let ns = source.namespace();
 
index d41edea6a256f423fe03f88ee9ad6d160090fe5c..99d13acbae1b9aa5fd9afecc36f18949557bbc8b 100644 (file)
@@ -33,8 +33,6 @@
 use std::iter;
 use std::ops::Deref;
 
-use tracing::debug;
-
 type Res = def::Res<ast::NodeId>;
 
 /// A field or associated item from self type suggested in case of resolution failure.
@@ -1792,7 +1790,7 @@ fn suggest_using_enum_variant(
                 }
             };
 
-            let mut suggestable_variants = variants
+            let suggestable_variants = variants
                 .iter()
                 .filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
                 .map(|(variant, _, kind)| (path_names_to_string(variant), kind))
@@ -1802,8 +1800,9 @@ fn suggest_using_enum_variant(
                     CtorKind::Fictive => format!("({} {{}})", variant),
                 })
                 .collect::<Vec<_>>();
+            let no_suggestable_variant = suggestable_variants.is_empty();
 
-            if !suggestable_variants.is_empty() {
+            if !no_suggestable_variant {
                 let msg = if suggestable_variants.len() == 1 {
                     "you might have meant to use the following enum variant"
                 } else {
@@ -1813,7 +1812,7 @@ fn suggest_using_enum_variant(
                 err.span_suggestions(
                     span,
                     msg,
-                    suggestable_variants.drain(..),
+                    suggestable_variants.into_iter(),
                     Applicability::MaybeIncorrect,
                 );
             }
@@ -1830,15 +1829,15 @@ fn suggest_using_enum_variant(
                 .collect::<Vec<_>>();
 
             if !suggestable_variants_with_placeholders.is_empty() {
-                let msg = match (
-                    suggestable_variants.is_empty(),
-                    suggestable_variants_with_placeholders.len(),
-                ) {
-                    (true, 1) => "the following enum variant is available",
-                    (true, _) => "the following enum variants are available",
-                    (false, 1) => "alternatively, the following enum variant is available",
-                    (false, _) => "alternatively, the following enum variants are also available",
-                };
+                let msg =
+                    match (no_suggestable_variant, suggestable_variants_with_placeholders.len()) {
+                        (true, 1) => "the following enum variant is available",
+                        (true, _) => "the following enum variants are available",
+                        (false, 1) => "alternatively, the following enum variant is available",
+                        (false, _) => {
+                            "alternatively, the following enum variants are also available"
+                        }
+                    };
 
                 err.span_suggestions(
                     span,
index 6ea976a59006470517ddf93e68df4014d8605ffd..c16eab222f625d8495bcf99e62d8fed42aef951e 100644 (file)
@@ -278,7 +278,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
 /// lifetimes into a single binder.) This requires us to resolve the
 /// *trait definition* of `Sub`; basically just enough lifetime information
 /// to look at the supertraits.
-#[tracing::instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx))]
 fn resolve_lifetimes_trait_definition(
     tcx: TyCtxt<'_>,
     local_def_id: LocalDefId,
@@ -289,7 +289,7 @@ fn resolve_lifetimes_trait_definition(
 /// Computes the `ResolveLifetimes` map that contains data for an entire `Item`.
 /// You should not read the result of this query directly, but rather use
 /// `named_region_map`, `is_late_bound_map`, etc.
-#[tracing::instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx))]
 fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes {
     convert_named_region_map(do_resolve(tcx, local_def_id, false))
 }
@@ -647,7 +647,7 @@ fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
         match ty.kind {
             hir::TyKind::BareFn(ref c) => {
@@ -930,7 +930,7 @@ fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
         match lifetime_ref.name {
             hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static),
@@ -1212,7 +1212,7 @@ fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F)
             scope: &wrap_scope,
             trait_definition_only: self.trait_definition_only,
         };
-        let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
+        let span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
         {
             let _enter = span.enter();
             f(&mut this);
@@ -1287,7 +1287,7 @@ fn visit_early_late<F>(
         self.with(scope, walk);
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn resolve_lifetime_ref(
         &mut self,
         region_def_id: LocalDefId,
@@ -1368,7 +1368,7 @@ fn resolve_lifetime_ref(
             return;
         }
 
-        // We may fail to resolve higher-ranked lifetimes that are mentionned by APIT.
+        // We may fail to resolve higher-ranked lifetimes that are mentioned by APIT.
         // AST-based resolution does not care for impl-trait desugaring, which are the
         // responibility of lowering.  This may create a mismatch between the resolution
         // AST found (`region_def_id`) which points to HRTB, and what HIR allows.
@@ -1409,7 +1409,7 @@ fn resolve_lifetime_ref(
         );
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn visit_segment_args(
         &mut self,
         res: Res,
@@ -1659,7 +1659,7 @@ fn supertrait_hrtb_lifetimes(
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn visit_fn_like_elision(
         &mut self,
         inputs: &'tcx [hir::Ty<'tcx>],
@@ -1707,7 +1707,7 @@ fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime)
         self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self))]
     fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
         debug!(
             node = ?self.tcx.hir().node_to_string(lifetime_ref.hir_id),
index 66090c96d1ee2f63b92264c3d56028bc1a605d0a..4e8f3a2cae879f11c03a2fd23c8cc7bd44230718 100644 (file)
@@ -58,7 +58,6 @@
 use std::cell::{Cell, RefCell};
 use std::collections::BTreeSet;
 use std::{cmp, fmt, ptr};
-use tracing::debug;
 
 use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
 use imports::{Import, ImportKind, ImportResolver, NameResolution};
@@ -1991,7 +1990,7 @@ pub fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
                         _ => panic!("invalid arg index"),
                     }
                 }
-                // Cache the lookup to avoid parsing attributes for an iterm multiple times.
+                // Cache the lookup to avoid parsing attributes for an item multiple times.
                 self.legacy_const_generic_args.insert(def_id, Some(ret.clone()));
                 return Some(ret);
             }
index 0c428aa6cc05c16b82c4a7f6a9e8a6539277bb82..dafa10e9e0026ff4ad4c32fc8f511a1bf73b8f2e 100644 (file)
@@ -441,7 +441,7 @@ fn cfg_accessible(
                 }
                 PathResult::Indeterminate => indeterminate = true,
                 // We can only be sure that a path doesn't exist after having tested all the
-                // posibilities, only at that time we can return false.
+                // possibilities, only at that time we can return false.
                 PathResult::Failed { .. } => {}
                 PathResult::Module(_) => panic!("unexpected path resolution"),
             }
index e2e0e1f5b300ad345ef4bf9837bab4eb49c8b29a..ac6c3663b63762d134e8bb21a6567724a08dd022 100644 (file)
@@ -44,8 +44,6 @@
     RefKind, Relation, RelationKind, SpanData,
 };
 
-use tracing::{debug, error};
-
 #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5213
 macro_rules! down_cast_data {
     ($id:ident, $kind:ident, $sp:expr) => {
index 619e083d89ad398b1991ecbf49e808e37409321a..16af53385104646aab85c1876aaf6e4f07b3c1ee 100644 (file)
@@ -7,6 +7,9 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
+#[macro_use]
+extern crate tracing;
+
 mod dump_visitor;
 mod dumper;
 #[macro_use]
@@ -49,8 +52,6 @@
     RefKind, Relation, RelationKind, SpanData,
 };
 
-use tracing::{debug, error, info};
-
 pub struct SaveContext<'tcx> {
     tcx: TyCtxt<'tcx>,
     maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
index dbc5c15195c86128511be6425f123523f69398e1..3b0b3144f2cd7d924fb0e4c3019b4c6654b8f0e0 100644 (file)
@@ -6,6 +6,7 @@ edition = "2021"
 [dependencies]
 indexmap = "1.9.1"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.8"
 
 [dev-dependencies]
 rustc_macros = { path = "../rustc_macros" }
index 5e53f0b104dfc6f398bb7f5670758a3f7cd3adc4..8f8c504117cc1c795f0326c561690c65557eb049 100644 (file)
@@ -1,13 +1,12 @@
 //! Implementations of serialization for structures found in liballoc
 
-use std::hash::{BuildHasher, Hash};
-
 use crate::{Decodable, Decoder, Encodable, Encoder};
+use smallvec::{Array, SmallVec};
 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque};
+use std::hash::{BuildHasher, Hash};
 use std::rc::Rc;
 use std::sync::Arc;
-
-use smallvec::{Array, SmallVec};
+use thin_vec::ThinVec;
 
 impl<S: Encoder, A: Array<Item: Encodable<S>>> Encodable<S> for SmallVec<A> {
     fn encode(&self, s: &mut S) {
@@ -23,6 +22,19 @@ fn decode(d: &mut D) -> SmallVec<A> {
     }
 }
 
+impl<S: Encoder, T: Encodable<S>> Encodable<S> for ThinVec<T> {
+    fn encode(&self, s: &mut S) {
+        self.as_slice().encode(s);
+    }
+}
+
+impl<D: Decoder, T: Decodable<D>> Decodable<D> for ThinVec<T> {
+    fn decode(d: &mut D) -> ThinVec<T> {
+        let len = d.read_usize();
+        (0..len).map(|_| Decodable::decode(d)).collect()
+    }
+}
+
 impl<S: Encoder, T: Encodable<S>> Encodable<S> for LinkedList<T> {
     fn encode(&self, s: &mut S) {
         s.emit_usize(self.len());
index 2a4a772f61085dfe13dfba1a48d4944cd39e0f56..2336d99363fd3d8ed639b61d96d9891dcb293439 100644 (file)
@@ -10,7 +10,6 @@
 use std::borrow::Cow;
 use std::fmt::{self};
 use std::sync::{Arc, Mutex};
-use tracing::debug;
 
 #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
 pub enum CguReuse {
index 162fc9aa0a6ea622e3202fb47a2fe2f967777ebe..7c50fe2d823bd443a8ad5adb9664c94c6ff8f662 100644 (file)
@@ -2530,7 +2530,7 @@ fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Optio
             ),
         ),
     };
-    tracing::debug!("got unpretty option: {first:?}");
+    debug!("got unpretty option: {first:?}");
     Some(first)
 }
 
index c973e3140cef97b596545cdfb3d536fa6bd6eef1..e8edb38f5038ea1f4c55d949a5dc3558050efbbe 100644 (file)
@@ -7,7 +7,6 @@
 
 use crate::search_paths::{PathKind, SearchPath};
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
-use tracing::debug;
 
 #[derive(Copy, Clone)]
 pub enum FileMatch {
index 429475c573c8e8d01202e46d7ed7a1c8957ffd97..02d5d33c8d5ba24727b27fec54a3a42bbd511fe4 100644 (file)
@@ -14,6 +14,9 @@
 extern crate rustc_macros;
 pub mod errors;
 
+#[macro_use]
+extern crate tracing;
+
 pub mod cgu_reuse_tracker;
 pub mod utils;
 pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass};
index e169d3c7cfb7c2553d583f7686c30a9e73383798..e8ddb4ed17a3e6da5af4e922736fa11dbe4c029f 100644 (file)
@@ -41,7 +41,6 @@
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use std::fmt;
 use std::hash::Hash;
-use tracing::*;
 
 /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks".
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
index 860af7fe93a077959f1377c70d642af5e8709a9e..34e2e92bdfce1efa6fc203f15245ac5d76104a04 100644 (file)
@@ -76,8 +76,6 @@
 use sha1::Sha1;
 use sha2::Sha256;
 
-use tracing::debug;
-
 #[cfg(test)]
 mod tests;
 
index a32cabab4c407281a0190e9dba473a863086b8bf..4d94c92d3f2b182ce8521d059210527bf784d62d 100644 (file)
@@ -23,7 +23,6 @@
 
 use std::fs;
 use std::io;
-use tracing::debug;
 
 #[cfg(test)]
 mod tests;
@@ -1060,13 +1059,13 @@ pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) {
 
         return remap_path_prefix(&self.mapping, path);
 
-        #[instrument(level = "debug", skip(mapping))]
+        #[instrument(level = "debug", skip(mapping), ret)]
         fn remap_path_prefix(mapping: &[(PathBuf, PathBuf)], path: PathBuf) -> (PathBuf, bool) {
             // NOTE: We are iterating over the mapping entries from last to first
             //       because entries specified later on the command line should
             //       take precedence.
             for &(ref from, ref to) in mapping.iter().rev() {
-                debug!("Trying to apply {:?} => {:?}", from, to);
+                debug!("Trying to apply {from:?} => {to:?}");
 
                 if let Ok(rest) = path.strip_prefix(from) {
                     let remapped = if rest.as_os_str().is_empty() {
@@ -1080,15 +1079,15 @@ fn remap_path_prefix(mapping: &[(PathBuf, PathBuf)], path: PathBuf) -> (PathBuf,
                     } else {
                         to.join(rest)
                     };
-                    debug!("Match - remapped {:?} => {:?}", path, remapped);
+                    debug!("Match - remapped");
 
                     return (remapped, true);
                 } else {
-                    debug!("No match - prefix {:?} does not match {:?}", from, path);
+                    debug!("No match - prefix {from:?} does not match");
                 }
             }
 
-            debug!("Path {:?} was not remapped", path);
+            debug!("not remapped");
             (path, false)
         }
     }
index 9241fd82c745fde4a857c5598faaba111c2edd34..46c5fe78ffbf65697085e5bcd7874ed70e50595a 100644 (file)
@@ -6,8 +6,6 @@
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitable};
 use rustc_middle::util::common::record_time;
 
-use tracing::debug;
-
 use std::fmt::{self, Write};
 use std::mem::{self, discriminant};
 
index 0c6489acb34835085ceb32cd02f2223c5d05f8cf..62f44a48032efd93301b704bf538bdea53813c8f 100644 (file)
@@ -97,6 +97,9 @@
 #[macro_use]
 extern crate rustc_middle;
 
+#[macro_use]
+extern crate tracing;
+
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::ty::{self, Instance, TyCtxt};
 use rustc_session::config::SymbolManglingVersion;
 
-use tracing::debug;
-
 mod legacy;
 mod v0;
 
index 1b7161fbb85c5365aa42aae59836b80b2edba6b7..b301ce68a1ce10ba20b877f904059a91c85299dd 100644 (file)
@@ -18,7 +18,6 @@ pub fn target() -> Target {
             panic_strategy: PanicStrategy::Abort,
             position_independent_executables: true,
             dynamic_linking: true,
-            executables: true,
             relro_level: RelroLevel::Off,
             ..Default::default()
         },
index 2bf83a8782a1273a56eeeb04c4b52548bea9260f..9f3e0bd5ef0e3ac8b74484d418e249f44740abca 100644 (file)
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     let mut base = super::linux_base::opts();
     base.os = "android".into();
     base.default_dwarf_version = 2;
-    base.position_independent_executables = true;
     base.has_thread_local = false;
     // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
     // for context. (At that time, there was no `-C force-unwind-tables`, so the only solution
index 9bbee88a894cad82a8ab012a26242ba1dfc47b23..2c72bf88a41e8b03b881d8d97e822a7658b2c00e 100644 (file)
@@ -1,40 +1,37 @@
 use std::{borrow::Cow, env};
 
-use crate::spec::{cvs, DebuginfoKind, FramePointer, SplitDebuginfo, TargetOptions};
+use crate::spec::{cvs, DebuginfoKind, FramePointer, SplitDebuginfo, StaticCow, TargetOptions};
 use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor};
 
 fn pre_link_args(os: &'static str, arch: &'static str, abi: &'static str) -> LinkArgs {
-    let mut args = LinkArgs::new();
-
-    let platform_name = match abi {
-        "sim" => format!("{}-simulator", os),
-        "macabi" => "mac-catalyst".to_string(),
-        _ => os.to_string(),
+    let platform_name: StaticCow<str> = match abi {
+        "sim" => format!("{}-simulator", os).into(),
+        "macabi" => "mac-catalyst".into(),
+        _ => os.into(),
     };
 
-    let platform_version = match os.as_ref() {
+    let platform_version: StaticCow<str> = match os.as_ref() {
         "ios" => ios_lld_platform_version(),
         "tvos" => tvos_lld_platform_version(),
         "watchos" => watchos_lld_platform_version(),
         "macos" => macos_lld_platform_version(arch),
         _ => unreachable!(),
-    };
-
-    if abi != "macabi" {
-        args.insert(LinkerFlavor::Gcc, vec!["-arch".into(), arch.into()]);
     }
+    .into();
 
-    args.insert(
+    let mut args = TargetOptions::link_args(
         LinkerFlavor::Lld(LldFlavor::Ld64),
-        vec![
-            "-arch".into(),
-            arch.into(),
-            "-platform_version".into(),
-            platform_name.into(),
-            platform_version.clone().into(),
-            platform_version.into(),
-        ],
+        &["-arch", arch, "-platform_version"],
     );
+    // Manually add owned args unsupported by link arg building helpers.
+    args.entry(LinkerFlavor::Lld(LldFlavor::Ld64)).or_default().extend([
+        platform_name,
+        platform_version.clone(),
+        platform_version,
+    ]);
+    if abi != "macabi" {
+        super::add_link_args(&mut args, LinkerFlavor::Gcc, &["-arch", arch]);
+    }
 
     args
 }
@@ -127,7 +124,7 @@ pub fn macos_llvm_target(arch: &str) -> String {
     format!("{}-apple-macosx{}.{}.0", arch, major, minor)
 }
 
-pub fn macos_link_env_remove() -> Vec<Cow<'static, str>> {
+pub fn macos_link_env_remove() -> Vec<StaticCow<str>> {
     let mut env_remove = Vec::with_capacity(2);
     // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
     // may occur when we're linking a custom build script while targeting iOS for example.
index 1d441e558ddcc257b9c88b16844b24aaf3ec5e97..8cca33cc43b358e1a55cf56f23ce8c2bf5c31f51 100644 (file)
@@ -1,4 +1,4 @@
-use crate::spec::{LinkerFlavor, Target, TargetOptions};
+use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions};
 
 /// A base target for AVR devices using the GNU toolchain.
 ///
@@ -21,6 +21,7 @@ pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target {
             late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]),
             max_atomic_width: Some(0),
             atomic_cas: false,
+            relocation_model: RelocModel::Static,
             ..TargetOptions::default()
         },
     }
index cc2c78c69fe15f321728e662743d8e00984ef50d..2a24e4459c553c65ceb35feb37d8afb5fc8ba597 100644 (file)
@@ -10,7 +10,6 @@ pub fn target() -> Target {
     base.crt_static_default = false;
     base.has_rpath = true;
     base.linker_is_gnu = false;
-    base.dynamic_linking = true;
 
     base.c_enum_min_bits = 8;
 
index a08756861e5b265167baa9ad8cbc3c1b8d8baba4..cab4dd333d43dbc5edb34d0f634616d7af6597bb 100644 (file)
@@ -1,4 +1,4 @@
-use crate::spec::{cvs, LinkerFlavor, PanicStrategy, TargetOptions};
+use crate::spec::{cvs, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions};
 
 pub fn opts() -> TargetOptions {
     TargetOptions {
@@ -9,6 +9,7 @@ pub fn opts() -> TargetOptions {
         linker: Some("l4-bender".into()),
         linker_is_gnu: false,
         families: cvs!["unix"],
+        relocation_model: RelocModel::Static,
         ..Default::default()
     }
 }
index aa73625ff810cc6a100823be9817de4cf2cc826e..2459b0280cd66df3e2f5ca94a8193f4b9364f7f1 100644 (file)
@@ -837,15 +837,15 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 }
 
 macro_rules! supported_targets {
-    ( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
+    ( $(($triple:literal, $module:ident ),)+ ) => {
         $(mod $module;)+
 
         /// List of supported targets
-        pub const TARGETS: &[&str] = &[$($($triple),+),+];
+        pub const TARGETS: &[&str] = &[$($triple),+];
 
         fn load_builtin(target: &str) -> Option<Target> {
             let mut t = match target {
-                $( $($triple)|+ => $module::target(), )+
+                $( $triple => $module::target(), )+
                 _ => return None,
             };
             t.is_builtin = true;
@@ -861,7 +861,7 @@ mod tests {
             $(
                 #[test] // `#[test]`
                 fn $module() {
-                    tests_impl::test_target(super::$module::target());
+                    tests_impl::test_target(super::$module::target(), $triple);
                 }
             )+
         }
@@ -1528,7 +1528,7 @@ fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'stati
     match flavor {
         LinkerFlavor::Ld => insert(LinkerFlavor::Lld(LldFlavor::Ld)),
         LinkerFlavor::Msvc => insert(LinkerFlavor::Lld(LldFlavor::Link)),
-        LinkerFlavor::Lld(LldFlavor::Wasm) => {}
+        LinkerFlavor::Lld(LldFlavor::Ld64) | LinkerFlavor::Lld(LldFlavor::Wasm) => {}
         LinkerFlavor::Lld(lld_flavor) => {
             panic!("add_link_args: use non-LLD flavor for {:?}", lld_flavor)
         }
@@ -2311,7 +2311,7 @@ pub fn expect_builtin(target_triple: &TargetTriple) -> Target {
                 load_builtin(target_triple).expect("built-in target")
             }
             TargetTriple::TargetJson { .. } => {
-                panic!("built-in targets doens't support target-paths")
+                panic!("built-in targets doesn't support target-paths")
             }
         }
     }
index 516b2de37eaa7cc77afb068592b08fd5065f705f..75ac66c276d57c72b454b721c95d252d2280ac1f 100644 (file)
@@ -1,5 +1,5 @@
 use crate::abi::Endian;
-use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions};
+use crate::spec::{LinkerFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::freebsd_base::opts();
@@ -15,7 +15,6 @@ pub fn target() -> Target {
         options: TargetOptions {
             endian: Endian::Big,
             features: "+secure-plt".into(),
-            relocation_model: RelocModel::Pic,
             mcount: "_mcount".into(),
             ..base
         },
index 03e579aee0a96f042c7188a704c10d54cfb608f4..4a53b9c173d1ff43b492e886e3313ab410c23530 100644 (file)
@@ -2,18 +2,20 @@
 use std::assert_matches::assert_matches;
 
 // Test target self-consistency and JSON encoding/decoding roundtrip.
-pub(super) fn test_target(target: Target) {
-    target.check_consistency();
+pub(super) fn test_target(target: Target, triple: &str) {
+    target.check_consistency(triple);
     assert_eq!(Target::from_json(target.to_json()).map(|(j, _)| j), Ok(target));
 }
 
 impl Target {
-    fn check_consistency(&self) {
+    fn check_consistency(&self, triple: &str) {
         assert_eq!(self.is_like_osx, self.vendor == "apple");
         assert_eq!(self.is_like_solaris, self.os == "solaris" || self.os == "illumos");
         assert_eq!(self.is_like_windows, self.os == "windows" || self.os == "uefi");
         assert_eq!(self.is_like_wasm, self.arch == "wasm32" || self.arch == "wasm64");
-        assert!(self.is_like_windows || !self.is_like_msvc);
+        if self.is_like_msvc {
+            assert!(self.is_like_windows);
+        }
 
         // Check that default linker flavor and lld flavor are compatible
         // with some other key properties.
@@ -94,8 +96,9 @@ fn check_consistency(&self) {
                             check_noncc(LinkerFlavor::Ld);
                             check_noncc(LinkerFlavor::Lld(LldFlavor::Ld));
                         }
+                        LldFlavor::Ld64 => check_noncc(LinkerFlavor::Lld(LldFlavor::Ld64)),
                         LldFlavor::Wasm => check_noncc(LinkerFlavor::Lld(LldFlavor::Wasm)),
-                        LldFlavor::Ld64 | LldFlavor::Link => {}
+                        LldFlavor::Link => {}
                     },
                     _ => {}
                 }
@@ -109,20 +112,56 @@ fn check_consistency(&self) {
             );
         }
 
-        assert!(
-            (self.pre_link_objects_self_contained.is_empty()
-                && self.post_link_objects_self_contained.is_empty())
-                || self.link_self_contained != LinkSelfContainedDefault::False
-        );
+        if self.link_self_contained == LinkSelfContainedDefault::False {
+            assert!(
+                self.pre_link_objects_self_contained.is_empty()
+                    && self.post_link_objects_self_contained.is_empty()
+            );
+        }
 
         // If your target really needs to deviate from the rules below,
         // except it and document the reasons.
         // Keep the default "unknown" vendor instead.
         assert_ne!(self.vendor, "");
+        assert_ne!(self.os, "");
         if !self.can_use_os_unknown() {
             // Keep the default "none" for bare metal targets instead.
             assert_ne!(self.os, "unknown");
         }
+
+        // Check dynamic linking stuff
+        // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries.
+        if self.os == "none" && self.arch != "bpf" {
+            assert!(!self.dynamic_linking);
+        }
+        if self.only_cdylib
+            || self.crt_static_allows_dylibs
+            || !self.late_link_args_dynamic.is_empty()
+        {
+            assert!(self.dynamic_linking);
+        }
+        // Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs
+        if self.dynamic_linking && !(self.is_like_wasm && self.os != "emscripten") {
+            assert_eq!(self.relocation_model, RelocModel::Pic);
+        }
+        // PIEs are supported but not enabled by default with linuxkernel target.
+        if self.position_independent_executables && !triple.ends_with("-linuxkernel") {
+            assert_eq!(self.relocation_model, RelocModel::Pic);
+        }
+        if self.relocation_model == RelocModel::Pic {
+            assert!(self.dynamic_linking || self.position_independent_executables);
+        }
+        if self.static_position_independent_executables {
+            assert!(self.position_independent_executables);
+        }
+        if self.position_independent_executables {
+            assert!(self.executables);
+        }
+
+        // Check crt static stuff
+        if self.crt_static_default || self.crt_static_allows_dylibs {
+            assert!(self.crt_static_respected);
+        }
     }
 
     // Add your target to the whitelist if it has `std` library
index aee8eb2e31c7ab4ab0a9483281f0a556b3a4c709..250da03cbd2b656af8fa21df4a369f6a33c82f2d 100644 (file)
@@ -9,7 +9,8 @@
 // the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
 // code runs in the same environment, no process separation is supported.
 
-use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions};
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy};
+use crate::spec::{RelocModel, StackProbeType, TargetOptions};
 
 pub fn opts() -> TargetOptions {
     let mut base = super::msvc_base::opts();
@@ -46,6 +47,7 @@ pub fn opts() -> TargetOptions {
         stack_probes: StackProbeType::Call,
         singlethread: true,
         linker: Some("rust-lld".into()),
+        relocation_model: RelocModel::Static,
         ..base
     }
 }
index bae007dc9f3bc0ad21a7bcd0c555cd8a2ea2c298..f30be25497d8a7ef9626658a00d4b7d493eb5d20 100644 (file)
@@ -3,7 +3,7 @@
 pub fn opts() -> TargetOptions {
     // We cannot use `-nodefaultlibs` because compiler-rt has to be passed
     // as a path since it's not added to linker search path by the default.
-    // There were attemts to make it behave like libgcc (so one can just use -l<name>)
+    // There were attempts to make it behave like libgcc (so one can just use -l<name>)
     // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440
     let pre_link_args =
         TargetOptions::link_args(LinkerFlavor::Gcc, &["-nolibc", "--unwindlib=none"]);
index 78189a0c0969a3dd14dd1fb4b762246b085a4861..26da7e800114a17994a709771348c6c3585da4d3 100644 (file)
@@ -4,8 +4,6 @@ pub fn target() -> Target {
     let mut base = super::l4re_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.crt_static_allows_dylibs = false;
-    base.dynamic_linking = false;
     base.panic_strategy = PanicStrategy::Abort;
 
     Target {
index 809fd642d41143fd12a2df76959972de42d52f80..b9a345127e3724d736c13be060afaae504890c03 100644 (file)
@@ -4,10 +4,8 @@
 // `target-cpu` compiler flags to opt-in more hardware-specific
 // features.
 
-use super::{
-    CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType,
-    Target, TargetOptions,
-};
+use super::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy};
+use super::{RelroLevel, StackProbeType, Target, TargetOptions};
 
 pub fn target() -> Target {
     let opts = TargetOptions {
@@ -18,7 +16,6 @@ pub fn target() -> Target {
         position_independent_executables: true,
         static_position_independent_executables: true,
         relro_level: RelroLevel::Full,
-        relocation_model: RelocModel::Pic,
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features:
index c0700748c79f9d7df78b0bf21458e92ebd509974..26f8e7d34c6eac056f1b06a4563b0e7c33497906 100644 (file)
@@ -18,7 +18,6 @@
 /// obligations *could be* resolved if we wanted to.
 ///
 /// This also expects that `trait_ref` is fully normalized.
-#[instrument(level = "debug", skip(tcx))]
 pub fn codegen_fulfill_obligation<'tcx>(
     tcx: TyCtxt<'tcx>,
     (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
@@ -74,7 +73,6 @@ pub fn codegen_fulfill_obligation<'tcx>(
         // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass
         let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
 
-        debug!("Cache miss: {trait_ref:?} => {impl_source:?}");
         Ok(&*tcx.arena.alloc(impl_source))
     })
 }
index e4af7022239b83fa2492b0754ea1688096a76854..99046bd126f937f74d557cb582c8db8d17457305 100644 (file)
@@ -2014,7 +2014,7 @@ fn maybe_report_ambiguity(
         let predicate = self.resolve_vars_if_possible(obligation.predicate);
         let span = obligation.cause.span;
 
-        debug!(?predicate, obligation.cause.code = tracing::field::debug(&obligation.cause.code()));
+        debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
 
         // Ambiguity errors are often caused as fallout from earlier errors.
         // We ignore them if this `infcx` is tainted in some cases below.
index d840677f1ca8b0d4c5e52d694a231789915a13b4..3763a98c488b766c11fc1e863a17e29f1c5c23d2 100644 (file)
@@ -135,7 +135,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
     /// `SomeTrait` or a where-clause that lets us unify `$0` with
     /// something concrete. If this fails, we'll unify `$0` with
     /// `projection_ty` again.
-    #[tracing::instrument(level = "debug", skip(self, infcx, param_env, cause))]
+    #[instrument(level = "debug", skip(self, infcx, param_env, cause))]
     fn normalize_projection_type(
         &mut self,
         infcx: &InferCtxt<'_, 'tcx>,
index 444ca6471e2e4d51696f25da4abfd4a1881e4c76..398635674abcfd19a69c7de02f88af6787e209f9 100644 (file)
@@ -231,7 +231,7 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
 /// If successful, this may result in additional obligations.
 ///
 /// See [poly_project_and_unify_type] for an explanation of the return value.
-#[tracing::instrument(level = "debug", skip(selcx))]
+#[instrument(level = "debug", skip(selcx))]
 fn project_and_unify_type<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionObligation<'tcx>,
@@ -1206,7 +1206,7 @@ fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx
 ///
 /// IMPORTANT:
 /// - `obligation` must be fully normalized
-#[tracing::instrument(level = "info", skip(selcx))]
+#[instrument(level = "info", skip(selcx))]
 fn project<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
@@ -1368,7 +1368,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
     );
 }
 
-#[tracing::instrument(
+#[instrument(
     level = "debug",
     skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates)
 )]
@@ -1419,7 +1419,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
     }
 }
 
-#[tracing::instrument(level = "debug", skip(selcx, obligation, candidate_set))]
+#[instrument(level = "debug", skip(selcx, obligation, candidate_set))]
 fn assemble_candidates_from_impls<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
index d67bd6292b434534556ea19f1896f3597294e377..e84c462ca816195e39badb321ed16c0a2135404b 100644 (file)
@@ -28,7 +28,7 @@
 use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack};
 
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
-    #[instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn candidate_from_obligation<'o>(
         &mut self,
         stack: &TraitObligationStack<'o, 'tcx>,
@@ -48,7 +48,7 @@ pub(super) fn candidate_from_obligation<'o>(
         if let Some(c) =
             self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
         {
-            debug!(candidate = ?c, "CACHE HIT");
+            debug!("CACHE HIT");
             return c;
         }
 
@@ -61,7 +61,7 @@ pub(super) fn candidate_from_obligation<'o>(
         let (candidate, dep_node) =
             self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
 
-        debug!(?candidate, "CACHE MISS");
+        debug!("CACHE MISS");
         self.insert_candidate_cache(
             stack.obligation.param_env,
             cache_fresh_trait_pred,
@@ -337,7 +337,7 @@ pub(super) fn assemble_candidates<'o>(
         Ok(candidates)
     }
 
-    #[tracing::instrument(level = "debug", skip(self, candidates))]
+    #[instrument(level = "debug", skip(self, candidates))]
     fn assemble_candidates_from_projected_tys(
         &mut self,
         obligation: &TraitObligation<'tcx>,
@@ -367,7 +367,7 @@ fn assemble_candidates_from_projected_tys(
     /// supplied to find out whether it is listed among them.
     ///
     /// Never affects the inference environment.
-    #[tracing::instrument(level = "debug", skip(self, stack, candidates))]
+    #[instrument(level = "debug", skip(self, stack, candidates))]
     fn assemble_candidates_from_caller_bounds<'o>(
         &mut self,
         stack: &TraitObligationStack<'o, 'tcx>,
@@ -880,7 +880,7 @@ fn assemble_candidates_for_unsizing(
         };
     }
 
-    #[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
+    #[instrument(level = "debug", skip(self, obligation, candidates))]
     fn assemble_candidates_for_transmutability(
         &mut self,
         obligation: &TraitObligation<'tcx>,
@@ -898,7 +898,7 @@ fn assemble_candidates_for_transmutability(
         candidates.vec.push(TransmutabilityCandidate);
     }
 
-    #[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
+    #[instrument(level = "debug", skip(self, obligation, candidates))]
     fn assemble_candidates_for_trait_alias(
         &mut self,
         obligation: &TraitObligation<'tcx>,
@@ -917,7 +917,7 @@ fn assemble_candidates_for_trait_alias(
 
     /// Assembles the trait which are built-in to the language itself:
     /// `Copy`, `Clone` and `Sized`.
-    #[tracing::instrument(level = "debug", skip(self, candidates))]
+    #[instrument(level = "debug", skip(self, candidates))]
     fn assemble_builtin_bound_candidates(
         &mut self,
         conditions: BuiltinImplConditions<'tcx>,
index 46b50dd92f1ef7a3ed17fa20b66ccaf9b7b7657b..5da8cfab0b13b77102197cf235c3503b1cef9533 100644 (file)
@@ -295,7 +295,7 @@ pub fn is_intercrate(&self) -> bool {
 
     /// Attempts to satisfy the obligation. If successful, this will affect the surrounding
     /// type environment by performing unification.
-    #[instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     pub fn select(
         &mut self,
         obligation: &TraitObligation<'tcx>,
@@ -325,10 +325,7 @@ pub fn select(
                 Err(SelectionError::Overflow(OverflowError::Canonical))
             }
             Err(e) => Err(e),
-            Ok(candidate) => {
-                debug!(?candidate, "confirmed");
-                Ok(Some(candidate))
-            }
+            Ok(candidate) => Ok(Some(candidate)),
         }
     }
 
@@ -435,6 +432,7 @@ fn evaluate_predicates_recursively<'o, I>(
         level = "debug",
         skip(self, previous_stack),
         fields(previous_stack = ?previous_stack.head())
+        ret,
     )]
     fn evaluate_predicate_recursively<'o>(
         &mut self,
@@ -450,7 +448,7 @@ fn evaluate_predicate_recursively<'o>(
             None => self.check_recursion_limit(&obligation, &obligation)?,
         }
 
-        let result = ensure_sufficient_stack(|| {
+        ensure_sufficient_stack(|| {
             let bound_predicate = obligation.predicate.kind();
             match bound_predicate.skip_binder() {
                 ty::PredicateKind::Trait(t) => {
@@ -760,14 +758,10 @@ fn evaluate_predicate_recursively<'o>(
                     bug!("TypeWellFormedFromEnv is only used for chalk")
                 }
             }
-        });
-
-        debug!("finished: {:?} from {:?}", result, obligation);
-
-        result
+        })
     }
 
-    #[instrument(skip(self, previous_stack), level = "debug")]
+    #[instrument(skip(self, previous_stack), level = "debug", ret)]
     fn evaluate_trait_predicate_recursively<'o>(
         &mut self,
         previous_stack: TraitObligationStackList<'o, 'tcx>,
@@ -798,12 +792,12 @@ fn evaluate_trait_predicate_recursively<'o>(
         // If a trait predicate is in the (local or global) evaluation cache,
         // then we know it holds without cycles.
         if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) {
-            debug!(?result, "CACHE HIT");
+            debug!("CACHE HIT");
             return Ok(result);
         }
 
         if let Some(result) = stack.cache().get_provisional(fresh_trait_pred) {
-            debug!(?result, "PROVISIONAL CACHE HIT");
+            debug!("PROVISIONAL CACHE HIT");
             stack.update_reached_depth(result.reached_depth);
             return Ok(result.result);
         }
@@ -826,11 +820,11 @@ fn evaluate_trait_predicate_recursively<'o>(
 
         let reached_depth = stack.reached_depth.get();
         if reached_depth >= stack.depth {
-            debug!(?result, "CACHE MISS");
+            debug!("CACHE MISS");
             self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result);
             stack.cache().on_completion(stack.dfn);
         } else {
-            debug!(?result, "PROVISIONAL");
+            debug!("PROVISIONAL");
             debug!(
                 "caching provisionally because {:?} \
                  is a cycle participant (at depth {}, reached depth {})",
@@ -1023,7 +1017,8 @@ fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
     #[instrument(
         level = "debug",
         skip(self, stack),
-        fields(depth = stack.obligation.recursion_depth)
+        fields(depth = stack.obligation.recursion_depth),
+        ret
     )]
     fn evaluate_candidate<'o>(
         &mut self,
@@ -1056,7 +1051,6 @@ fn evaluate_candidate<'o>(
             result = result.max(EvaluatedToOkModuloRegions);
         }
 
-        debug!(?result);
         Ok(result)
     }
 
@@ -1405,7 +1399,7 @@ fn insert_candidate_cache(
     /// a projection, look at the bounds of `T::Bar`, see if we can find a
     /// `Baz` bound. We return indexes into the list returned by
     /// `tcx.item_bounds` for any applicable bounds.
-    #[instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     fn match_projection_obligation_against_definition_bounds(
         &mut self,
         obligation: &TraitObligation<'tcx>,
@@ -1435,7 +1429,7 @@ fn match_projection_obligation_against_definition_bounds(
         // unnecessary ambiguity.
         let mut distinct_normalized_bounds = FxHashSet::default();
 
-        let matching_bounds = bounds
+        bounds
             .iter()
             .enumerate()
             .filter_map(|(idx, bound)| {
@@ -1462,10 +1456,7 @@ fn match_projection_obligation_against_definition_bounds(
                 }
                 None
             })
-            .collect();
-
-        debug!(?matching_bounds);
-        matching_bounds
+            .collect()
     }
 
     /// Equates the trait in `obligation` with trait bound. If the two traits
@@ -2153,7 +2144,7 @@ fn rematch_impl(
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     fn match_impl(
         &mut self,
         impl_def_id: DefId,
@@ -2194,17 +2185,16 @@ fn match_impl(
             .at(&cause, obligation.param_env)
             .define_opaque_types(false)
             .eq(placeholder_obligation_trait_ref, impl_trait_ref)
-            .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?;
+            .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{e}`"))?;
         nested_obligations.extend(obligations);
 
         if !self.intercrate
             && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation
         {
-            debug!("match_impl: reservation impls only apply in intercrate mode");
+            debug!("reservation impls only apply in intercrate mode");
             return Err(());
         }
 
-        debug!(?impl_substs, ?nested_obligations, "match_impl: success");
         Ok(Normalized { value: impl_substs, obligations: nested_obligations })
     }
 
@@ -2335,7 +2325,7 @@ fn generator_trait_ref_unnormalized(
     /// impl or trait. The obligations are substituted and fully
     /// normalized. This is used when confirming an impl or default
     /// impl.
-    #[tracing::instrument(level = "debug", skip(self, cause, param_env))]
+    #[instrument(level = "debug", skip(self, cause, param_env))]
     fn impl_or_trait_obligations(
         &mut self,
         cause: &ObligationCause<'tcx>,
index 4bd179d23913143dd663bd7c7899677264f5c3ba..bb6009cb22a37b2df733bb9019e164eae8d5288b 100644 (file)
@@ -841,7 +841,7 @@ pub fn object_region_bounds<'tcx>(
 ///
 /// Requires that trait definitions have been processed so that we can
 /// elaborate predicates and walk supertraits.
-#[instrument(skip(tcx, predicates), level = "debug")]
+#[instrument(skip(tcx, predicates), level = "debug", ret)]
 pub(crate) fn required_region_bounds<'tcx>(
     tcx: TyCtxt<'tcx>,
     erased_self_ty: Ty<'tcx>,
index c7c604e14e3ee4d235ffc5db02c1b7b1c82d0247..a166371fed1e01c2dc2af7ae5700b1a0829cb7e7 100644 (file)
@@ -191,7 +191,7 @@ fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::GoalData<RustInte
                 GenericArgKind::Const(..) => {
                     chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
                 }
-                GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt),
+                GenericArgKind::Lifetime(lt) => bug!("unexpected well formed predicate: {:?}", lt),
             },
 
             ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal(
index 93cab7ca533954b216e7df75b6efbedc8fae21e1..211c813b8001c8ae626bc5f783213c3c83fc2010 100644 (file)
@@ -317,7 +317,7 @@ pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> {
                             tcx,
                         )?,
                         AdtKind::Enum => {
-                            tracing::trace!(?adt_def, "treeifying enum");
+                            trace!(?adt_def, "treeifying enum");
                             let mut tree = Tree::uninhabited();
 
                             for (idx, discr) in adt_def.discriminants(tcx) {
@@ -381,7 +381,7 @@ fn from_repr_c_variant(
             let clamp =
                 |align: Align| align.clamp(min_align, max_align).bytes().try_into().unwrap();
 
-            let variant_span = tracing::trace_span!(
+            let variant_span = trace_span!(
                 "treeifying variant",
                 min_align = ?min_align,
                 max_align = ?max_align,
@@ -396,27 +396,27 @@ fn from_repr_c_variant(
 
             // The layout of the variant is prefixed by the discriminant, if any.
             if let Some(discr) = discr {
-                tracing::trace!(?discr, "treeifying discriminant");
+                trace!(?discr, "treeifying discriminant");
                 let discr_layout = alloc::Layout::from_size_align(
                     layout_summary.discriminant_size,
                     clamp(layout_summary.discriminant_align),
                 )
                 .unwrap();
-                tracing::trace!(?discr_layout, "computed discriminant layout");
+                trace!(?discr_layout, "computed discriminant layout");
                 variant_layout = variant_layout.extend(discr_layout).unwrap().0;
                 tree = tree.then(Self::from_disr(discr, tcx, layout_summary.discriminant_size));
             }
 
             // Next come fields.
-            let fields_span = tracing::trace_span!("treeifying fields").entered();
+            let fields_span = trace_span!("treeifying fields").entered();
             for field_def in variant_def.fields.iter() {
                 let field_ty = field_def.ty(tcx, substs_ref);
-                let _span = tracing::trace_span!("treeifying field", field = ?field_ty).entered();
+                let _span = trace_span!("treeifying field", field = ?field_ty).entered();
 
                 // begin with the field's visibility
                 tree = tree.then(Self::def(Def::Field(field_def)));
 
-                // compute the field's layout charactaristics
+                // compute the field's layout characteristics
                 let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align);
 
                 // next comes the field's padding
@@ -434,7 +434,7 @@ fn from_repr_c_variant(
             drop(fields_span);
 
             // finally: padding
-            let padding_span = tracing::trace_span!("adding trailing padding").entered();
+            let padding_span = trace_span!("adding trailing padding").entered();
             let padding_needed = layout_summary.total_size - variant_layout.size();
             if padding_needed > 0 {
                 tree = tree.then(Self::padding(padding_needed));
@@ -467,7 +467,7 @@ fn layout_of<'tcx>(
             layout.align().abi.bytes().try_into().unwrap(),
         )
         .unwrap();
-        tracing::trace!(?ty, ?layout, "computed layout for type");
+        trace!(?ty, ?layout, "computed layout for type");
         Ok(layout)
     }
 }
index 076d922d1b72b4157a47dd296dc15a3007252d8b..248ff1ec24164c9209c84bdd98b009de8e2679d7 100644 (file)
@@ -110,7 +110,7 @@ pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
             // Remove all `Def` nodes from `src`, without checking their visibility.
             let src = src.prune(&|def| true);
 
-            tracing::trace!(?src, "pruned src");
+            trace!(?src, "pruned src");
 
             // Remove all `Def` nodes from `dst`, additionally...
             let dst = if assume_visibility {
@@ -121,7 +121,7 @@ pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
                 dst.prune(&|def| context.is_accessible_from(def, scope))
             };
 
-            tracing::trace!(?dst, "pruned dst");
+            trace!(?dst, "pruned dst");
 
             // Convert `src` from a tree-based representation to an NFA-based representation.
             // If the conversion fails because `src` is uninhabited, conclude that the transmutation
index 9c2cf4c9a92384821dc5c356372d351982213b83..adab343ac98aaa4b225a4b2bcb790d3000f3c2ea 100644 (file)
@@ -82,7 +82,7 @@ fn is_accessible_from(&self, def: Self::Def, scope: Self::Scope) -> bool {
                 false
             };
 
-            tracing::trace!(?ret, "ret");
+            trace!(?ret, "ret");
             ret
         }
 
index bd1d568cd9a051079bae294ec74562a557c371ce..661e413fc5b8170defb0d06724fc0b62710b4573 100644 (file)
@@ -15,8 +15,6 @@
 use std::collections::BTreeMap;
 use std::ops::ControlFlow;
 
-use tracing::debug;
-
 // FIXME(#86795): `BoundVarsCollector` here should **NOT** be used
 // outside of `resolve_associated_item`. It's just to address #64494,
 // #83765, and #85848 which are creating bound types/regions that lose
index acfeefb4d12d535d9d7a03477a90049cde370ac6..9d640672cf92c8cd3bc2f7cc861c931a71b3edb7 100644 (file)
@@ -104,7 +104,6 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstrain
 }
 
 /// See `ParamEnv` struct definition for details.
-#[instrument(level = "debug", skip(tcx))]
 fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     // The param_env of an impl Trait type is its defining function's param_env
     if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) {
@@ -410,7 +409,6 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
 }
 
 /// Don't call this directly: use ``tcx.conservative_is_privately_uninhabited`` instead.
-#[instrument(level = "debug", skip(tcx))]
 pub fn conservative_is_privately_uninhabited_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env_and: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
index 5c96f1c4623a6c9010633d79951b1af3023c96c4..ef927058df4eef0781c1c7a512921844d540b2c9 100644 (file)
@@ -144,7 +144,7 @@ enum ConvertedBindingKind<'a, 'tcx> {
 /// instantiated with some generic arguments providing `'a` explicitly,
 /// we taint those arguments with `ExplicitLateBound::Yes` so that we
 /// can provide an appropriate diagnostic later.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, Debug)]
 pub enum ExplicitLateBound {
     Yes,
     No,
@@ -167,7 +167,7 @@ pub(crate) enum GenericArgPosition {
 
 /// A marker denoting that the generic arguments that were
 /// provided did not match the respective generic parameters.
-#[derive(Clone, Default)]
+#[derive(Clone, Default, Debug)]
 pub struct GenericArgCountMismatch {
     /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`).
     pub reported: Option<ErrorGuaranteed>,
@@ -177,7 +177,7 @@ pub struct GenericArgCountMismatch {
 
 /// Decorates the result of a generic argument count mismatch
 /// check with whether explicit late bounds were provided.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct GenericArgCountResult {
     pub explicit_late_bound: ExplicitLateBound,
     pub correct: Result<(), GenericArgCountMismatch>,
@@ -201,7 +201,7 @@ fn inferred_kind(
 }
 
 impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     pub fn ast_region_to_region(
         &self,
         lifetime: &hir::Lifetime,
@@ -210,7 +210,7 @@ pub fn ast_region_to_region(
         let tcx = self.tcx();
         let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id));
 
-        let r = match tcx.named_region(lifetime.hir_id) {
+        match tcx.named_region(lifetime.hir_id) {
             Some(rl::Region::Static) => tcx.lifetimes.re_static,
 
             Some(rl::Region::LateBound(debruijn, index, def_id)) => {
@@ -255,9 +255,7 @@ pub fn ast_region_to_region(
                     tcx.lifetimes.re_static
                 })
             }
-        };
-        debug!("ast_region_to_region(lifetime={:?}) yields {:?}", lifetime, r);
-        r
+        }
     }
 
     /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`,
@@ -317,7 +315,7 @@ pub fn ast_path_substs_for_ty(
     /// `[Vec<u8>, u8]` and `generic_args` are the arguments for the associated
     /// type itself: `['a]`. The returned `SubstsRef` concatenates these two
     /// lists: `[Vec<u8>, u8, 'a]`.
-    #[tracing::instrument(level = "debug", skip(self, span))]
+    #[instrument(level = "debug", skip(self, span), ret)]
     fn create_substs_for_ast_path<'a>(
         &self,
         span: Span,
@@ -537,11 +535,6 @@ fn inferred_kind(
             &mut substs_ctx,
         );
 
-        debug!(
-            "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}",
-            generics, self_ty, substs
-        );
-
         (substs, arg_count)
     }
 
@@ -716,7 +709,7 @@ fn instantiate_poly_trait_ref_inner(
     /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be
     /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly,
     /// however.
-    #[tracing::instrument(level = "debug", skip(self, span, constness, bounds, speculative))]
+    #[instrument(level = "debug", skip(self, span, constness, bounds, speculative))]
     pub(crate) fn instantiate_poly_trait_ref(
         &self,
         trait_ref: &hir::TraitRef<'_>,
@@ -808,7 +801,7 @@ fn ast_path_to_mono_trait_ref(
         ty::TraitRef::new(trait_def_id, substs)
     }
 
-    #[tracing::instrument(level = "debug", skip(self, span))]
+    #[instrument(level = "debug", skip(self, span))]
     fn create_substs_for_ast_trait_ref<'a>(
         &self,
         span: Span,
@@ -922,7 +915,7 @@ pub(crate) fn add_implicitly_sized<'hir>(
     /// **A note on binders:** there is an implied binder around
     /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref`
     /// for more details.
-    #[tracing::instrument(level = "debug", skip(self, ast_bounds, bounds))]
+    #[instrument(level = "debug", skip(self, ast_bounds, bounds))]
     pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>(
         &self,
         param_ty: Ty<'tcx>,
@@ -1028,10 +1021,7 @@ fn compute_bounds_inner(
     /// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the
     /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside*
     /// the binder (e.g., `&'a u32`) and hence may reference bound regions.
-    #[tracing::instrument(
-        level = "debug",
-        skip(self, bounds, speculative, dup_bindings, path_span)
-    )]
+    #[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))]
     fn add_predicates_for_ast_type_binding(
         &self,
         hir_ref_id: hir::HirId,
@@ -2599,7 +2589,7 @@ pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
 
     /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait
     /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors.
-    #[tracing::instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> {
         let tcx = self.tcx();
 
@@ -2703,8 +2693,6 @@ fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool
             hir::TyKind::Err => tcx.ty_error(),
         };
 
-        debug!(?result_ty);
-
         self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
         result_ty
     }
index 2d50412007d909b493571c7d774bc4c82b729dd0..25bafdfe859b882af75dee0a021595a9db8f7670 100644 (file)
@@ -12,7 +12,7 @@
 };
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self), level = "debug", ret)]
     pub fn check_match(
         &self,
         expr: &'tcx hir::Expr<'tcx>,
@@ -212,9 +212,7 @@ pub fn check_match(
         // We won't diverge unless the scrutinee or all arms diverge.
         self.diverges.set(scrut_diverges | all_arms_diverge);
 
-        let match_ty = coercion.complete(self);
-        debug!(?match_ty);
-        match_ty
+        coercion.complete(self)
     }
 
     /// When the previously checked expression (the scrutinee) diverges,
index 5cdb2acd9f3c5b24cf91b65549dafd2f5d527ee7..29a128f27b840f5653dc3796f181f8f317d3d738 100644 (file)
@@ -101,7 +101,7 @@ pub(super) fn check_fn<'a, 'tcx>(
             decl.output.span(),
             param_env,
         ));
-    // If we replaced declared_ret_ty with infer vars, then we must be infering
+    // If we replaced declared_ret_ty with infer vars, then we must be inferring
     // an opaque type, so set a flag so we can improve diagnostics.
     fcx.return_type_has_opaque = ret_ty != declared_ret_ty;
 
@@ -1543,7 +1543,7 @@ fn detect_discriminant_duplicate<'tcx>(
             None => {
                 // At this point we know this discriminant is a duplicate, and was not explicitly
                 // assigned by the user. Here we iterate backwards to fetch the HIR for the last
-                // explictly assigned discriminant, and letting the user know that this was the
+                // explicitly assigned discriminant, and letting the user know that this was the
                 // increment startpoint, and how many steps from there leading to the duplicate
                 if let Some((n, hir::Variant { span, ident, .. })) =
                     vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some())
@@ -1566,7 +1566,7 @@ fn detect_discriminant_duplicate<'tcx>(
     };
 
     // Here we loop through the discriminants, comparing each discriminant to another.
-    // When a duplicate is detected, we instatiate an error and point to both
+    // When a duplicate is detected, we instantiate an error and point to both
     // initial and duplicate value. The duplicate discriminant is then discarded by swapping
     // it with the last element and decrementing the `vec.len` (which is why we have to evaluate
     // `discrs.len()` anew every iteration, and why this could be tricky to do in a functional
index fee872155f5b2d1fa86c3ca4958a8c5d0384ce3a..bc3fec6e7d66a371dc57dfb0fd8a0039a30838fa 100644 (file)
@@ -58,7 +58,7 @@ pub fn check_expr_closure(
         self.check_closure(expr, expected_kind, decl, body, gen, expected_sig)
     }
 
-    #[instrument(skip(self, expr, body, decl), level = "debug")]
+    #[instrument(skip(self, expr, body, decl), level = "debug", ret)]
     fn check_closure(
         &self,
         expr: &hir::Expr<'_>,
@@ -158,11 +158,7 @@ fn check_closure(
             },
         );
 
-        let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs);
-
-        debug!(?expr.hir_id, ?closure_type);
-
-        closure_type
+        self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs)
     }
 
     /// Given the expected type, figures out what it can about this closure we
@@ -262,7 +258,7 @@ fn deduce_expectations_from_obligations(
     /// The `cause_span` should be the span that caused us to
     /// have this expected signature, or `None` if we can't readily
     /// know that.
-    #[instrument(level = "debug", skip(self, cause_span))]
+    #[instrument(level = "debug", skip(self, cause_span), ret)]
     fn deduce_sig_from_projection(
         &self,
         cause_span: Option<Span>,
@@ -317,7 +313,6 @@ fn deduce_sig_from_projection(
             hir::Unsafety::Normal,
             Abi::Rust,
         ));
-        debug!(?sig);
 
         Some(ExpectedSig { cause_span, sig })
     }
@@ -576,7 +571,7 @@ fn check_supplied_sig_against_expectation(
     /// types that the user gave into a signature.
     ///
     /// Also, record this closure signature for later.
-    #[instrument(skip(self, decl, body), level = "debug")]
+    #[instrument(skip(self, decl, body), level = "debug", ret)]
     fn supplied_sig_of_closure(
         &self,
         hir_id: hir::HirId,
@@ -629,8 +624,6 @@ fn supplied_sig_of_closure(
             bound_vars,
         );
 
-        debug!(?result);
-
         let c_result = self.inh.infcx.canonicalize_response(result);
         self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
 
@@ -643,7 +636,7 @@ fn supplied_sig_of_closure(
     /// user specified. The "desugared" return type is an `impl
     /// Future<Output = T>`, so we do this by searching through the
     /// obligations to extract the `T`.
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self), level = "debug", ret)]
     fn deduce_future_output_from_obligations(
         &self,
         expr_def_id: DefId,
@@ -704,7 +697,6 @@ fn deduce_future_output_from_obligations(
             );
         self.register_predicates(obligations);
 
-        debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty);
         Some(output_ty)
     }
 
index 804306814a24127b2c352a21ec17364e31c0a077..b6bc244d2b14447a5c3d4a96c942a87f8ca0f58a 100644 (file)
@@ -1308,7 +1308,7 @@ fn compare_type_predicate_entailment<'tcx>(
 /// For default associated types the normalization is not possible (the value
 /// from the impl could be overridden). We also can't normalize generic
 /// associated types (yet) because they contain bound parameters.
-#[tracing::instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx))]
 pub fn check_type_bounds<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ty: &ty::AssocItem,
index 07046f3f0326b119185f836728bd3bf49df39873..b9054898a2e579174a748a275146d570d0074449 100644 (file)
@@ -130,7 +130,7 @@ pub fn demand_coerce(
     ///
     /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!`
     /// will be permitted if the diverges flag is currently "always".
-    #[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
+    #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
     pub fn demand_coerce_diag(
         &self,
         expr: &hir::Expr<'tcx>,
index 20d25d508d224e9749e631f3e8a4cfb5905cbe1e..66b737a493058861a7b36928ba4295eeb1b2ff8a 100644 (file)
@@ -83,7 +83,7 @@ pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty
         self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
     }
 
-    #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")]
+    #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)]
     pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
         &self,
         mut ty: Ty<'tcx>,
@@ -107,10 +107,7 @@ pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
         // indirect dependencies that don't seem worth tracking
         // precisely.
         self.select_obligations_where_possible(false, mutate_fulfillment_errors);
-        ty = self.resolve_vars_if_possible(ty);
-
-        debug!(?ty);
-        ty
+        self.resolve_vars_if_possible(ty)
     }
 
     pub(in super::super) fn record_deferred_call_resolution(
@@ -1405,7 +1402,7 @@ pub(crate) fn add_required_obligations_for_hir(
         })
     }
 
-    #[tracing::instrument(level = "debug", skip(self, code, span, def_id, substs))]
+    #[instrument(level = "debug", skip(self, code, span, def_id, substs))]
     fn add_required_obligations_with_code(
         &self,
         span: Span,
index 03bd485096a9c5046a97d1f072e6532094e56538..7ff4aef2d25716a28c6fa44177c35512bcb140e2 100644 (file)
@@ -153,7 +153,7 @@ pub(in super::super) fn check_argument_types(
     ) {
         let tcx = self.tcx;
 
-        // Conceptually, we've got some number of expected inputs, and some number of provided aguments
+        // Conceptually, we've got some number of expected inputs, and some number of provided arguments
         // and we can form a grid of whether each argument could satisfy a given input:
         //      in1 | in2 | in3 | ...
         // arg1  ?  |     |     |
index 85a0d4e44990602e6a6bcdb7172bf0057d0f685f..2a8c460bb11830921c4e48fdc715d9d3e0980ae2 100644 (file)
@@ -17,7 +17,6 @@
 use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
-use tracing::debug;
 
 mod drop_ranges;
 
index de26a9e56e2d6b57275d2060fb475c540677eda0..a9071cd1fd949efe57c2bfd122425fb0b5277dd7 100644 (file)
@@ -168,7 +168,7 @@ pub(crate) fn suggest_method_call(
     /// * `call_expr`:             the complete method call: (`foo.bar::<T1,...Tn>(...)`)
     /// * `self_expr`:             the self expression (`foo`)
     /// * `args`:                  the expressions of the arguments (`a, b + 1, ...`)
-    #[instrument(level = "debug", skip(self, call_expr, self_expr))]
+    #[instrument(level = "debug", skip(self))]
     pub fn lookup_method(
         &self,
         self_ty: Ty<'tcx>,
@@ -178,11 +178,6 @@ pub fn lookup_method(
         self_expr: &'tcx hir::Expr<'tcx>,
         args: &'tcx [hir::Expr<'tcx>],
     ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
-        debug!(
-            "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
-            segment.ident, self_ty, call_expr, self_expr
-        );
-
         let pick =
             self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
 
@@ -383,7 +378,7 @@ pub(super) fn obligation_for_op_method(
     /// In particular, it doesn't really do any probing: it simply constructs
     /// an obligation for a particular trait with the given self type and checks
     /// whether that trait is implemented.
-    #[instrument(level = "debug", skip(self, span, opt_input_types))]
+    #[instrument(level = "debug", skip(self, span))]
     pub(super) fn lookup_method_in_trait(
         &self,
         span: Span,
@@ -392,11 +387,6 @@ pub(super) fn lookup_method_in_trait(
         self_ty: Ty<'tcx>,
         opt_input_types: Option<&[Ty<'tcx>]>,
     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
-        debug!(
-            "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})",
-            self_ty, m_name, trait_def_id, opt_input_types
-        );
-
         let (obligation, substs) =
             self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
         self.construct_obligation_for_trait(
@@ -576,7 +566,7 @@ fn construct_obligation_for_trait(
     /// * `self_ty`:               the type to search within (`Foo`)
     /// * `self_ty_span`           the span for the type being searched within (span of `Foo`)
     /// * `expr_id`:               the [`hir::HirId`] of the expression composing the entire call
-    #[instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     pub fn resolve_fully_qualified_call(
         &self,
         span: Span,
@@ -585,11 +575,6 @@ pub fn resolve_fully_qualified_call(
         self_ty_span: Span,
         expr_id: hir::HirId,
     ) -> Result<(DefKind, DefId), MethodError<'tcx>> {
-        debug!(
-            "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}",
-            method_name, self_ty, expr_id,
-        );
-
         let tcx = self.tcx;
 
         // Check if we have an enum variant.
@@ -633,21 +618,17 @@ pub fn resolve_fully_qualified_call(
             &pick,
         );
 
-        debug!("resolve_fully_qualified_call: pick={:?}", pick);
+        debug!(?pick);
         {
             let mut typeck_results = self.typeck_results.borrow_mut();
             let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap();
             for import_id in pick.import_ids {
-                debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id);
+                debug!(used_trait_import=?import_id);
                 used_trait_imports.insert(import_id);
             }
         }
 
         let def_kind = pick.item.kind.as_def_kind();
-        debug!(
-            "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}",
-            def_kind, pick.item.def_id
-        );
         tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
         Ok((def_kind, pick.item.def_id))
     }
index d9870060a40bbb974922ac85bce1f00c9d21f838..e9f55ab3406f5df2ea9bc49c8e34985b479c36df 100644 (file)
@@ -253,7 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// would result in an error (basically, the same criteria we
     /// would use to decide if a method is a plausible fit for
     /// ambiguity purposes).
-    #[instrument(level = "debug", skip(self, scope_expr_id))]
+    #[instrument(level = "debug", skip(self))]
     pub fn probe_for_return_type(
         &self,
         span: Span,
@@ -262,10 +262,6 @@ pub fn probe_for_return_type(
         self_ty: Ty<'tcx>,
         scope_expr_id: hir::HirId,
     ) -> Vec<ty::AssocItem> {
-        debug!(
-            "probe(self_ty={:?}, return_type={}, scope_expr_id={})",
-            self_ty, return_type, scope_expr_id
-        );
         let method_names = self
             .probe_op(
                 span,
@@ -299,7 +295,7 @@ pub fn probe_for_return_type(
             .collect()
     }
 
-    #[instrument(level = "debug", skip(self, scope_expr_id))]
+    #[instrument(level = "debug", skip(self))]
     pub fn probe_for_name(
         &self,
         span: Span,
@@ -310,10 +306,6 @@ pub fn probe_for_name(
         scope_expr_id: hir::HirId,
         scope: ProbeScope,
     ) -> PickResult<'tcx> {
-        debug!(
-            "probe(self_ty={:?}, item_name={}, scope_expr_id={})",
-            self_ty, item_name, scope_expr_id
-        );
         self.probe_op(
             span,
             mode,
index 3281dd8298bc3000526cc5bb55fdd577e9235933..66281448d40e88e6c5c45217b5d0183501de1281 100644 (file)
@@ -341,7 +341,6 @@ fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::T
     typeck_with_fallback(tcx, def_id, fallback)
 }
 
-#[instrument(skip(tcx, fallback))]
 fn typeck_with_fallback<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: LocalDefId,
index ba42453bd60e920286239085eeb96203479087a8..86cf12d224047a664cef7b1942e4da58b09f64fd 100644 (file)
@@ -972,7 +972,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
     }
 }
 
-#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))]
+#[instrument(level = "debug", skip(tcx, span, sig_if_method))]
 fn check_associated_item(
     tcx: TyCtxt<'_>,
     item_id: LocalDefId,
@@ -1225,7 +1225,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo
     });
 }
 
-#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
+#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
 fn check_impl<'tcx>(
     tcx: TyCtxt<'tcx>,
     item: &'tcx hir::Item<'tcx>,
@@ -1472,7 +1472,7 @@ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
     wfcx.register_obligations(obligations);
 }
 
-#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))]
+#[instrument(level = "debug", skip(wfcx, span, hir_decl))]
 fn check_fn_or_method<'tcx>(
     wfcx: &WfCheckingCtxt<'_, 'tcx>,
     span: Span,
@@ -1536,7 +1536,7 @@ fn check_fn_or_method<'tcx>(
      `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
      of the previous types except `Self`)";
 
-#[tracing::instrument(level = "debug", skip(wfcx))]
+#[instrument(level = "debug", skip(wfcx))]
 fn check_method_receiver<'tcx>(
     wfcx: &WfCheckingCtxt<'_, 'tcx>,
     fn_sig: &hir::FnSig<'_>,
index ba687bc4da4cc6ae9d736fe11d255829ebcec859..9ecf34e9ad3e71ac81b187cf6fded98962d1e86a 100644 (file)
@@ -501,7 +501,7 @@ fn visit_user_provided_tys(&mut self) {
 
         if !errors_buffer.is_empty() {
             errors_buffer.sort_by_key(|diag| diag.span.primary_span());
-            for mut diag in errors_buffer.drain(..) {
+            for mut diag in errors_buffer {
                 self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
             }
         }
index f1dbe64f13abbb33e3cb4a323abb015be0288489..9f931de6fdedb674b11b6fff3724a49c5b4a361a 100644 (file)
@@ -19,7 +19,6 @@
 /// Computes the relevant generic parameter for a potential generic const argument.
 ///
 /// This should be called using the query `tcx.opt_const_param_of`.
-#[instrument(level = "debug", skip(tcx))]
 pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
     use hir::*;
     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
index 74a5b6e42c30a8a417dce88f60b75322227c08b5..bfc4f061b70f492f7ebfced4da35d8353328e9aa 100644 (file)
@@ -497,7 +497,7 @@ fn walk_local<F>(
         let expr_place = return_if_err!(self.mc.cat_expr(expr));
         f(self);
         if let Some(els) = els {
-            // borrowing because we need to test the descriminant
+            // borrowing because we need to test the discriminant
             self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter());
             self.walk_block(els)
         }
index 6aa1c915542e24bccfbbbdf69771827eddef9854..d4b5e5e2fe44df3e917c11a7d97d9fb72929e067 100644 (file)
@@ -523,6 +523,7 @@ fn suggest(&self, err: &mut Diagnostic) {
                 if self.not_enough_args_provided() {
                     self.suggest_adding_args(err);
                 } else if self.too_many_args_provided() {
+                    self.suggest_moving_args_from_assoc_fn_to_trait(err);
                     self.suggest_removing_args_or_generics(err);
                 } else {
                     unreachable!();
@@ -653,6 +654,123 @@ fn suggest_adding_type_and_const_args(&self, err: &mut Diagnostic) {
         }
     }
 
+    /// Suggests moving redundant argument(s) of an associate function to the
+    /// trait it belongs to.
+    ///
+    /// ```compile_fail
+    /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
+    /// ```
+    fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
+        let trait_ = match self.tcx.trait_of_item(self.def_id) {
+            Some(def_id) => def_id,
+            None => return,
+        };
+
+        // Skip suggestion when the associated function is itself generic, it is unclear
+        // how to split the provided parameters between those to suggest to the trait and
+        // those to remain on the associated type.
+        let num_assoc_fn_expected_args =
+            self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
+        if num_assoc_fn_expected_args > 0 {
+            return;
+        }
+
+        let num_assoc_fn_excess_args =
+            self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
+
+        let trait_generics = self.tcx.generics_of(trait_);
+        let num_trait_generics_except_self =
+            trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
+
+        let msg = format!(
+            "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
+            these = pluralize!("this", num_assoc_fn_excess_args),
+            s = pluralize!(num_assoc_fn_excess_args),
+            name = self.tcx.item_name(trait_),
+            num = num_trait_generics_except_self,
+        );
+
+        if let Some(hir_id) = self.path_segment.hir_id
+        && let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id)
+        && let Some(parent_node) = self.tcx.hir().find(parent_node)
+        && let hir::Node::Expr(expr) = parent_node {
+            match expr.kind {
+                hir::ExprKind::Path(ref qpath) => {
+                    self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+                        err,
+                        qpath,
+                        msg,
+                        num_assoc_fn_excess_args,
+                        num_trait_generics_except_self
+                    )
+                },
+                hir::ExprKind::MethodCall(..) => {
+                    self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+                        err,
+                        trait_,
+                        expr,
+                        msg,
+                        num_assoc_fn_excess_args,
+                        num_trait_generics_except_self
+                    )
+                },
+                _ => return,
+            }
+        }
+    }
+
+    fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+        &self,
+        err: &mut Diagnostic,
+        qpath: &'tcx hir::QPath<'tcx>,
+        msg: String,
+        num_assoc_fn_excess_args: usize,
+        num_trait_generics_except_self: usize,
+    ) {
+        if let hir::QPath::Resolved(_, path) = qpath
+        && let Some(trait_path_segment) = path.segments.get(0) {
+            let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
+
+            if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
+                if let Some(span) = self.gen_args.span_ext()
+                && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                    let sugg = vec![
+                        (self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
+                        (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
+                    ];
+
+                    err.multipart_suggestion(
+                        msg,
+                        sugg,
+                        Applicability::MaybeIncorrect
+                    );
+                }
+            }
+        }
+    }
+
+    fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+        &self,
+        err: &mut Diagnostic,
+        trait_: DefId,
+        expr: &'tcx hir::Expr<'tcx>,
+        msg: String,
+        num_assoc_fn_excess_args: usize,
+        num_trait_generics_except_self: usize,
+    ) {
+        if let hir::ExprKind::MethodCall(_, args, _) = expr.kind {
+            assert_eq!(args.len(), 1);
+            if num_assoc_fn_excess_args == num_trait_generics_except_self {
+                if let Some(gen_args) = self.gen_args.span_ext()
+                && let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args)
+                && let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) {
+                    let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args);
+                    err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect);
+                }
+            }
+        }
+    }
+
     /// Suggests to remove redundant argument(s):
     ///
     /// ```text
index b320cdcc109ddc061fec4a33b2fea4b379ed89e3..a967d881b029b7facb6af929d2d18c3fc6bc6d93 100644 (file)
@@ -666,6 +666,10 @@ changelog-seen = 2
 # target.
 #llvm-config = <none> (path)
 
+# Override detection of whether this is a Rust-patched LLVM. This would be used
+# in conjunction with either an llvm-config or build.submodules = false.
+#llvm-has-rust-patches = if llvm-config { false } else { true }
+
 # Normally the build system can find LLVM's FileCheck utility, but if
 # not, you can specify an explicit file name for it.
 #llvm-filecheck = "/path/to/llvm-version/bin/FileCheck"
index e21c8aa3bd536d1fa4ecfda330f82df66f8985e1..6480fcaf93d4ee5754d74c01fea9cfec49822b33 100644 (file)
@@ -1570,7 +1570,7 @@ pub fn split_before(&mut self) -> LinkedList<T> {
     /// that the cursor points to is unchanged, even if it is the "ghost" node.
     ///
     /// This operation should compute in *O*(1) time.
-    // `push_front` continues to point to "ghost" when it addes a node to mimic
+    // `push_front` continues to point to "ghost" when it adds a node to mimic
     // the behavior of `insert_before` on an empty list.
     #[unstable(feature = "linked_list_cursors", issue = "58533")]
     pub fn push_front(&mut self, elt: T) {
index 1f19b9e594549d534ca82e9c314868ba45082b89..60b36af5e67fcce6af2b855242c0b6e48d2ffe5a 100644 (file)
@@ -436,7 +436,7 @@ pub const fn new() -> Self {
     /// an explanation of the difference between length and capacity, see
     /// *[Capacity and reallocation]*.
     ///
-    /// If it is imporant to know the exact allocated capacity of a `Vec`,
+    /// If it is important to know the exact allocated capacity of a `Vec`,
     /// always use the [`capacity`] method after construction.
     ///
     /// For `Vec<T>` where `T` is a zero-sized type, there will be no allocation
@@ -591,7 +591,7 @@ pub const fn new_in(alloc: A) -> Self {
     /// an explanation of the difference between length and capacity, see
     /// *[Capacity and reallocation]*.
     ///
-    /// If it is imporant to know the exact allocated capacity of a `Vec`,
+    /// If it is important to know the exact allocated capacity of a `Vec`,
     /// always use the [`capacity`] method after construction.
     ///
     /// For `Vec<T, A>` where `T` is a zero-sized type, there will be no allocation
index 81b6d5737ea753d9dd867e3a97f37fba3a22b5c9..20340d42962efbbf1caf5fd8bb06f4b6515d1b91 100644 (file)
@@ -31,7 +31,7 @@
 ///
 /// `unreachable_unchecked()` can be used in situations where the compiler
 /// can't prove invariants that were previously established. Such situations
-/// have a higher chance of occuring if those invariants are upheld by
+/// have a higher chance of occurring if those invariants are upheld by
 /// external code that the compiler can't analyze.
 /// ```
 /// fn prepare_inputs(divisors: &mut Vec<u32>) {
index 5f8e6efa0cf5fd1c4c21fed8c7d193c2f6caaadc..d610f0a02f40310d8b1090e65395c432ce141fd9 100644 (file)
@@ -1082,7 +1082,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
     /// Note that using `transmute` to turn a pointer to a `usize` is (as noted above) [undefined
     /// behavior][ub] in `const` contexts. Also outside of consts, this operation might not behave
     /// as expected -- this is touching on many unspecified aspects of the Rust memory model.
-    /// Depending on what the code is doing, the following alternatives are preferrable to
+    /// Depending on what the code is doing, the following alternatives are preferable to
     /// pointer-to-integer transmutation:
     /// - If the code just wants to store data of arbitrary type in some buffer and needs to pick a
     ///   type for that buffer, it can use [`MaybeUninit`][mem::MaybeUninit].
index 0bd9c8e9acfcb7424908c1ee8a041e2ff108a256..b37e9142d889638edab0c77462b117fad07d5493 100644 (file)
@@ -350,10 +350,12 @@ macro_rules! matches {
 
 /// Unwraps a result or propagates its error.
 ///
-/// The `?` operator was added to replace `try!` and should be used instead.
-/// Furthermore, `try` is a reserved word in Rust 2018, so if you must use
-/// it, you will need to use the [raw-identifier syntax][ris]: `r#try`.
+/// The [`?` operator][propagating-errors] was added to replace `try!`
+/// and should be used instead. Furthermore, `try` is a reserved word
+/// in Rust 2018, so if you must use it, you will need to use the
+/// [raw-identifier syntax][ris]: `r#try`.
 ///
+/// [propagating-errors]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
 /// [ris]: https://doc.rust-lang.org/nightly/rust-by-example/compatibility/raw_identifiers.html
 ///
 /// `try!` matches the given [`Result`]. In case of the `Ok` variant, the
index 6196c4da4e32928670529a82433ff98baaea50ba..1cf306f2103b4ff02ca0dfdeee6918725054fdf1 100644 (file)
@@ -309,8 +309,8 @@ macro_rules! nonzero_unsigned_operations {
     ( $( $Ty: ident($Int: ident); )+ ) => {
         $(
             impl $Ty {
-                /// Add an unsigned integer to a non-zero value.
-                /// Check for overflow and return [`None`] on overflow
+                /// Adds an unsigned integer to a non-zero value.
+                /// Checks for overflow and returns [`None`] on overflow.
                 /// As a consequence, the result cannot wrap to zero.
                 ///
                 ///
@@ -346,7 +346,7 @@ pub const fn checked_add(self, other: $Int) -> Option<$Ty> {
                     }
                 }
 
-                /// Add an unsigned integer to a non-zero value.
+                /// Adds an unsigned integer to a non-zero value.
                 #[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")]
                 ///
                 /// # Examples
@@ -377,7 +377,7 @@ pub const fn saturating_add(self, other: $Int) -> $Ty {
                     unsafe { $Ty::new_unchecked(self.get().saturating_add(other)) }
                 }
 
-                /// Add an unsigned integer to a non-zero value,
+                /// Adds an unsigned integer to a non-zero value,
                 /// assuming overflow cannot occur.
                 /// Overflow is unchecked, and it is undefined behaviour to overflow
                 /// *even if the result would wrap to a non-zero value*.
@@ -409,7 +409,7 @@ pub const fn saturating_add(self, other: $Int) -> $Ty {
                 }
 
                 /// Returns the smallest power of two greater than or equal to n.
-                /// Check for overflow and return [`None`]
+                /// Checks for overflow and returns [`None`]
                 /// if the next power of two is greater than the type’s maximum value.
                 /// As a consequence, the result cannot wrap to zero.
                 ///
@@ -545,7 +545,7 @@ pub const fn abs(self) -> $Ty {
                 }
 
                 /// Checked absolute value.
-                /// Check for overflow and returns [`None`] if
+                /// Checks for overflow and returns [`None`] if
                 #[doc = concat!("`self == ", stringify!($Int), "::MIN`.")]
                 /// The result cannot be zero.
                 ///
@@ -740,8 +740,8 @@ macro_rules! nonzero_unsigned_signed_operations {
     ( $( $signedness:ident $Ty: ident($Int: ty); )+ ) => {
         $(
             impl $Ty {
-                /// Multiply two non-zero integers together.
-                /// Check for overflow and return [`None`] on overflow.
+                /// Multiplies two non-zero integers together.
+                /// Checks for overflow and returns [`None`] on overflow.
                 /// As a consequence, the result cannot wrap to zero.
                 ///
                 /// # Examples
@@ -777,7 +777,7 @@ pub const fn checked_mul(self, other: $Ty) -> Option<$Ty> {
                     }
                 }
 
-                /// Multiply two non-zero integers together.
+                /// Multiplies two non-zero integers together.
                 #[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")]
                 ///
                 /// # Examples
@@ -809,7 +809,7 @@ pub const fn saturating_mul(self, other: $Ty) -> $Ty {
                     unsafe { $Ty::new_unchecked(self.get().saturating_mul(other.get())) }
                 }
 
-                /// Multiply two non-zero integers together,
+                /// Multiplies two non-zero integers together,
                 /// assuming overflow cannot occur.
                 /// Overflow is unchecked, and it is undefined behaviour to overflow
                 /// *even if the result would wrap to a non-zero value*.
@@ -849,8 +849,8 @@ pub const fn saturating_mul(self, other: $Ty) -> $Ty {
                     unsafe { $Ty::new_unchecked(self.get().unchecked_mul(other.get())) }
                 }
 
-                /// Raise non-zero value to an integer power.
-                /// Check for overflow and return [`None`] on overflow.
+                /// Raises non-zero value to an integer power.
+                /// Checks for overflow and returns [`None`] on overflow.
                 /// As a consequence, the result cannot wrap to zero.
                 ///
                 /// # Examples
index ef7b3b1d1470a1778950922e5803339bc50dda4e..f5c72d797553450b5c103d6ff05c48e842a2c489 100644 (file)
@@ -154,7 +154,7 @@ pub fn from_bits(bits: usize) -> Self
     /// This is similar to `self as usize`, which semantically discards *provenance* and
     /// *address-space* information. However, unlike `self as usize`, casting the returned address
     /// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To
-    /// properly restore the lost information and obtain a dereferencable pointer, use
+    /// properly restore the lost information and obtain a dereferenceable pointer, use
     /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
     ///
     /// If using those APIs is not possible because there is no way to preserve a pointer with the
index 203531f66aa367e47ea00c1127124a27665b82ae..41a2685d361c805a1696cc39d899f6da3ff76fd1 100644 (file)
@@ -90,7 +90,7 @@
 //! isn't *pointer*-sized but address-space/offset/allocation-sized (we'll probably continue
 //! to conflate these notions). This would potentially make it possible to more efficiently
 //! target platforms where pointers are larger than offsets, such as CHERI and maybe some
-//! segmented architecures.
+//! segmented architectures.
 //!
 //! ## Provenance
 //!
 //! a pointer to a usize is generally an operation which *only* extracts the address. It is
 //! therefore *impossible* to construct a valid pointer from a usize because there is no way
 //! to restore the address-space and provenance. In other words, pointer-integer-pointer
-//! roundtrips are not possible (in the sense that the resulting pointer is not dereferencable).
+//! roundtrips are not possible (in the sense that the resulting pointer is not dereferenceable).
 //!
 //! The key insight to making this model *at all* viable is the [`with_addr`][] method:
 //!
 //!
 //! * Create an invalid pointer from just an address (see [`ptr::invalid`][]). This can
 //!   be used for sentinel values like `null` *or* to represent a tagged pointer that will
-//!   never be dereferencable. In general, it is always sound for an integer to pretend
+//!   never be dereferenceable. In general, it is always sound for an integer to pretend
 //!   to be a pointer "for fun" as long as you don't use operations on it which require
 //!   it to be valid (offset, read, write, etc).
 //!
index 6a3b9ee9a7daa618a19f9bf933a0067e5997e030..3e4c3ae07567565da53e13364d3ed28a88890fa5 100644 (file)
@@ -160,7 +160,7 @@ pub fn from_bits(bits: usize) -> Self
     /// This is similar to `self as usize`, which semantically discards *provenance* and
     /// *address-space* information. However, unlike `self as usize`, casting the returned address
     /// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To
-    /// properly restore the lost information and obtain a dereferencable pointer, use
+    /// properly restore the lost information and obtain a dereferenceable pointer, use
     /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
     ///
     /// If using those APIs is not possible because there is no way to preserve a pointer with the
index f43b780ec9a900a18f202d2e8e3cf55a574894d1..395c5678451cdc03841c026eab1932bc19260721 100644 (file)
@@ -2754,10 +2754,10 @@ fn nth(&mut self, n: usize) -> Option<&'a mut [T]> {
                 None => 0,
             };
             // SAFETY: This type ensures that self.v is a valid pointer with a correct len.
-            // Therefore the bounds check in split_at_mut guarantess the split point is inbounds.
+            // Therefore the bounds check in split_at_mut guarantees the split point is inbounds.
             let (head, tail) = unsafe { self.v.split_at_mut(start) };
             // SAFETY: This type ensures that self.v is a valid pointer with a correct len.
-            // Therefore the bounds check in split_at_mut guarantess the split point is inbounds.
+            // Therefore the bounds check in split_at_mut guarantees the split point is inbounds.
             let (nth, _) = unsafe { tail.split_at_mut(end - start) };
             self.v = head;
             // SAFETY: Nothing else points to or will point to the contents of this slice.
index d5706c388f0045c4764f3797f8473498e9acccc9..1958745b5868c79f240b45087db2c272608503ce 100644 (file)
@@ -2321,7 +2321,7 @@ pub fn strip_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> Op
     }
 
     /// Binary searches this slice for a given element.
-    /// This behaves similary to [`contains`] if this slice is sorted.
+    /// This behaves similarly to [`contains`] if this slice is sorted.
     ///
     /// If the value is found then [`Result::Ok`] is returned, containing the
     /// index of the matching element. If there are multiple matches, then any
@@ -3530,7 +3530,7 @@ pub unsafe fn align_to_mut<U>(&mut self) -> (&mut [T], &mut [U], &mut [T]) {
         // alignment targeted for U.
         // `crate::ptr::align_offset` is called with a correctly aligned and
         // valid pointer `ptr` (it comes from a reference to `self`) and with
-        // a size that is a power of two (since it comes from the alignement for U),
+        // a size that is a power of two (since it comes from the alignment for U),
         // satisfying its safety constraints.
         let offset = unsafe { crate::ptr::align_offset(ptr, mem::align_of::<U>()) };
         if offset > self.len() {
index 153dc4dbb0835e02c74493061fe904eb1e65e8fc..4f29ecc0fba8b55c681714dce9a14de126f1ee0b 100644 (file)
@@ -1280,7 +1280,7 @@ macro_rules! try_from_secs {
             let rem_msb = nanos_tmp & rem_msb_mask == 0;
             let add_ns = !(rem_msb || (is_even && is_tie));
 
-            // f32 does not have enough presicion to trigger the second branch
+            // f32 does not have enough precision to trigger the second branch
             // since it can not represent numbers between 0.999_999_940_395 and 1.0.
             let nanos = nanos + add_ns as u32;
             if ($mant_bits == 23) || (nanos != NANOS_PER_SEC) { (0, nanos) } else { (1, 0) }
@@ -1299,9 +1299,9 @@ macro_rules! try_from_secs {
             let rem_msb = nanos_tmp & rem_msb_mask == 0;
             let add_ns = !(rem_msb || (is_even && is_tie));
 
-            // f32 does not have enough presicion to trigger the second branch.
+            // f32 does not have enough precision to trigger the second branch.
             // For example, it can not represent numbers between 1.999_999_880...
-            // and 2.0. Bigger values result in even smaller presicion of the
+            // and 2.0. Bigger values result in even smaller precision of the
             // fractional part.
             let nanos = nanos + add_ns as u32;
             if ($mant_bits == 23) || (nanos != NANOS_PER_SEC) {
index 65d3ce9be65ecfa123585f3b6e9ccedb0ae07d58..2235f016c7144e762a0339bc300d0dd76f0a34ae 100644 (file)
@@ -70,7 +70,7 @@ impl ToBitMask<BitMask=u32> for Mask<_, 32>
     impl ToBitMask<BitMask=u64> for Mask<_, 64>
 }
 
-/// Returns the minimum numnber of bytes in a bitmask with `lanes` lanes.
+/// Returns the minimum number of bytes in a bitmask with `lanes` lanes.
 #[cfg(feature = "generic_const_exprs")]
 pub const fn bitmask_len(lanes: usize) -> usize {
     (lanes + 7) / 8
index 1516f084ab8b65b3db8821f78cc314dd6712cd2b..4461b21802adbeb91709c3c1ce7fb7f35fab604f 100644 (file)
@@ -176,8 +176,6 @@ fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
     FreeFunctions,
     TokenStream,
     SourceFile,
-    MultiSpan,
-    Diagnostic,
 
     'interned:
     Span,
index 5cde966bf173d163efa4130f18ae2180ea21fef8..4c1e196b5ad165c5e679a5a9ffdb0a3c434e1450 100644 (file)
@@ -57,6 +57,7 @@ macro_rules! with_api {
                 fn track_env_var(var: &str, value: Option<&str>);
                 fn track_path(path: &str);
                 fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
+                fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);
             },
             TokenStream {
                 fn drop($self: $S::TokenStream);
@@ -87,22 +88,6 @@ fn into_trees(
                 fn path($self: &$S::SourceFile) -> String;
                 fn is_real($self: &$S::SourceFile) -> bool;
             },
-            MultiSpan {
-                fn drop($self: $S::MultiSpan);
-                fn new() -> $S::MultiSpan;
-                fn push($self: &mut $S::MultiSpan, span: $S::Span);
-            },
-            Diagnostic {
-                fn drop($self: $S::Diagnostic);
-                fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic;
-                fn sub(
-                    $self: &mut $S::Diagnostic,
-                    level: Level,
-                    msg: &str,
-                    span: $S::MultiSpan,
-                );
-                fn emit($self: $S::Diagnostic);
-            },
             Span {
                 fn debug($self: $S::Span) -> String;
                 fn source_file($self: $S::Span) -> $S::SourceFile;
@@ -510,6 +495,18 @@ enum TokenTree<TokenStream, Span, Symbol> {
     }
 );
 
+#[derive(Clone, Debug)]
+pub struct Diagnostic<Span> {
+    pub level: Level,
+    pub message: String,
+    pub spans: Vec<Span>,
+    pub children: Vec<Diagnostic<Span>>,
+}
+
+compound_traits!(
+    struct Diagnostic<Span> { level, message, spans, children }
+);
+
 /// Globals provided alongside the initial inputs for a macro expansion.
 /// Provides values such as spans which are used frequently to avoid RPC.
 #[derive(Clone)]
index e068ec60b6af0d654c07db30bf06d39e5fe53173..e47a77f6c1350530e4f985a31701bc0c28c96e39 100644 (file)
@@ -11,8 +11,6 @@ pub trait Types {
     type FreeFunctions: 'static;
     type TokenStream: 'static + Clone;
     type SourceFile: 'static + Clone;
-    type MultiSpan: 'static;
-    type Diagnostic: 'static;
     type Span: 'static + Copy + Eq + Hash;
     type Symbol: 'static;
 }
index 6e46dc0367d07b2dd8dadf6c6e238c81477228f3..5a209f7c7aa1858577d1a4044cb7fddd487f9f54 100644 (file)
@@ -161,22 +161,15 @@ pub fn children(&self) -> Children<'_> {
     /// Emit the diagnostic.
     #[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
     pub fn emit(self) {
-        fn to_internal(spans: Vec<Span>) -> crate::bridge::client::MultiSpan {
-            let mut multi_span = crate::bridge::client::MultiSpan::new();
-            for span in spans {
-                multi_span.push(span.0);
+        fn to_internal(diag: Diagnostic) -> crate::bridge::Diagnostic<crate::bridge::client::Span> {
+            crate::bridge::Diagnostic {
+                level: diag.level,
+                message: diag.message,
+                spans: diag.spans.into_iter().map(|s| s.0).collect(),
+                children: diag.children.into_iter().map(to_internal).collect(),
             }
-            multi_span
         }
 
-        let mut diag = crate::bridge::client::Diagnostic::new(
-            self.level,
-            &self.message[..],
-            to_internal(self.spans),
-        );
-        for c in self.children {
-            diag.sub(c.level, &c.message[..], to_internal(c.spans));
-        }
-        diag.emit();
+        crate::bridge::client::FreeFunctions::emit_diagnostic(to_internal(self));
     }
 }
index 4d3736f79146c8df36976ba06397e108ef532af7..07239746258d109adb6130bd10b95cafe5d3190b 100644 (file)
@@ -986,10 +986,10 @@ pub fn set_output_capture(sink: Option<LocalStream>) -> Option<LocalStream> {
 /// otherwise. `label` identifies the stream in a panic message.
 ///
 /// This function is used to print error messages, so it takes extra
-/// care to avoid causing a panic when `local_s` is unusable.
-/// For instance, if the TLS key for the local stream is
-/// already destroyed, or if the local stream is locked by another
-/// thread, it will just fall back to the global stream.
+/// care to avoid causing a panic when `OUTPUT_CAPTURE` is unusable.
+/// For instance, if the TLS key for output capturing is already destroyed, or
+/// if the local stream is in use by another thread, it will just fall back to
+/// the global stream.
 ///
 /// However, if the actual I/O causes an error, this function does panic.
 fn print_to<T>(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str)
index 7157b5af00cf48fd1440ec7970b8bd1ef7ca481f..a4b0522b050244b2e4e32e9f7a30afd35e219796 100644 (file)
@@ -1921,7 +1921,7 @@ mod type_keyword {}
 /// and [proposal]s exist to use `unsafe {}` blocks inside such functions when
 /// making `unsafe` operations.
 ///
-/// See the [Rustnomicon] and the [Reference] for more informations.
+/// See the [Rustnomicon] and the [Reference] for more information.
 ///
 /// # Examples
 ///
@@ -2113,7 +2113,7 @@ mod use_keyword {}
 /// Add constraints that must be upheld to use an item.
 ///
 /// `where` allows specifying constraints on lifetime and generic parameters.
-/// The [RFC] introducing `where` contains detailed informations about the
+/// The [RFC] introducing `where` contains detailed information about the
 /// keyword.
 ///
 /// # Examples
@@ -2355,7 +2355,7 @@ mod dyn_keyword {}
 /// println!("f = {f} and i = {i}");
 /// ```
 ///
-/// See the [Reference][union] for more informations on `union`s.
+/// See the [Reference][union] for more information on `union`s.
 ///
 /// [`struct`]: keyword.struct.html
 /// [union]: ../reference/items/unions.html
index ce427599c3bddc9ace0a1add4647dd8d51521a82..dbb9829bb666dc89cfbc789204c0b654079ffcd4 100644 (file)
@@ -138,7 +138,7 @@ fn lock_contested(&self, mut state: u32, thread_self: zx_handle_t) {
                 }
             }
 
-            // The state has changed or a wakeup occured, try to lock the mutex.
+            // The state has changed or a wakeup occurred, try to lock the mutex.
             match self.futex.compare_exchange(UNLOCKED, owned_state, Acquire, Relaxed) {
                 Ok(_) => return,
                 Err(updated) => state = updated,
index 99ba86e5f996dce0bb445c5e12c99d494b441fcf..1b5be46c6050230aab31e2bad89031ff6517ce13 100644 (file)
@@ -53,7 +53,7 @@ fn lock_contended(&self) {
             // We avoid an unnecessary write if it as already set to 2,
             // to be friendlier for the caches.
             if state != 2 && self.futex.swap(2, Acquire) == 0 {
-                // We changed it from 0 to 2, so we just succesfully locked it.
+                // We changed it from 0 to 2, so we just successfully locked it.
                 return;
             }
 
index b3bbbf743f84c8ac63302b7a7d63cfae43b3bf4c..0cc92244ecad3a195e2eecb5d18a6792ebf5146a 100644 (file)
@@ -54,7 +54,7 @@ fn is_read_lockable(state: u32) -> bool {
     // We don't allow read-locking if there's readers waiting, even if the lock is unlocked
     // and there's no writers waiting. The only situation when this happens is after unlocking,
     // at which point the unlocking thread might be waking up writers, which have priority over readers.
-    // The unlocking thread will clear the readers waiting bit and wake up readers, if necssary.
+    // The unlocking thread will clear the readers waiting bit and wake up readers, if necessary.
     state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state)
 }
 
index 1a5421facd0c1aba216ebd195b0e72f4800e7560..43ab8c7ee659f5e08d6d7815c27c2d3ff21897c8 100644 (file)
@@ -44,7 +44,7 @@ mod shims {
 }
 
 // On 32-bit x86 MSVC these functions aren't defined, so we just define shims
-// which promote everything fo f64, perform the calculation, and then demote
+// which promote everything to f64, perform the calculation, and then demote
 // back to f32. While not precisely correct should be "correct enough" for now.
 #[cfg(all(target_env = "msvc", target_arch = "x86"))]
 mod shims {
index 0aa7c50ded1c7acfca41ad6c67eaa0dd463171e1..98c8834d38403a9ce4dba30cc3c73b666c439c30 100644 (file)
@@ -3,7 +3,7 @@
 use crate::ffi::OsString;
 use crate::fmt;
 use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
-use crate::mem;
+use crate::mem::{self, MaybeUninit};
 use crate::os::windows::io::{AsHandle, BorrowedHandle};
 use crate::path::{Path, PathBuf};
 use crate::ptr;
@@ -326,7 +326,8 @@ pub fn file_attr(&self) -> io::Result<FileAttr> {
             cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
             let mut reparse_tag = 0;
             if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
-                let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+                let mut b =
+                    Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
                     reparse_tag = (*buf).ReparseTag;
                 }
@@ -389,7 +390,8 @@ pub fn file_attr(&self) -> io::Result<FileAttr> {
             attr.file_size = info.AllocationSize as u64;
             attr.number_of_links = Some(info.NumberOfLinks);
             if attr.file_type().is_reparse_point() {
-                let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+                let mut b =
+                    Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
                     attr.reparse_tag = (*buf).ReparseTag;
                 }
@@ -463,7 +465,7 @@ pub fn duplicate(&self) -> io::Result<File> {
     // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
     fn reparse_point(
         &self,
-        space: &mut Align8<[u8]>,
+        space: &mut Align8<[MaybeUninit<u8>]>,
     ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
         unsafe {
             let mut bytes = 0;
@@ -488,7 +490,7 @@ fn reparse_point(
     }
 
     fn readlink(&self) -> io::Result<PathBuf> {
-        let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+        let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
         let (_bytes, buf) = self.reparse_point(&mut space)?;
         unsafe {
             let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
@@ -658,12 +660,16 @@ fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> io::Result<bool>
 
 /// A buffer for holding directory entries.
 struct DirBuff {
-    buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>,
+    buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
 }
 impl DirBuff {
     const BUFFER_SIZE: usize = 1024;
     fn new() -> Self {
-        Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) }
+        Self {
+            // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
+            // initialization.
+            buffer: unsafe { Box::new_uninit().assume_init() },
+        }
     }
     fn capacity(&self) -> usize {
         self.buffer.0.len()
@@ -676,8 +682,8 @@ fn iter(&self) -> DirBuffIter<'_> {
         DirBuffIter::new(self)
     }
 }
-impl AsRef<[u8]> for DirBuff {
-    fn as_ref(&self) -> &[u8] {
+impl AsRef<[MaybeUninit<u8>]> for DirBuff {
+    fn as_ref(&self) -> &[MaybeUninit<u8>] {
         &self.buffer.0
     }
 }
@@ -686,7 +692,7 @@ fn as_ref(&self) -> &[u8] {
 ///
 /// Currently only returns file names (UTF-16 encoded).
 struct DirBuffIter<'a> {
-    buffer: Option<&'a [u8]>,
+    buffer: Option<&'a [MaybeUninit<u8>]>,
     cursor: usize,
 }
 impl<'a> DirBuffIter<'a> {
@@ -701,9 +707,13 @@ fn next(&mut self) -> Option<Self::Item> {
         let buffer = &self.buffer?[self.cursor..];
 
         // Get the name and next entry from the buffer.
-        // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
-        // last field (the file name) is unsized. So an offset has to be
-        // used to get the file name slice.
+        // SAFETY:
+        // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
+        //   field (the file name) is unsized. So an offset has to be used to
+        //   get the file name slice.
+        // - The OS has guaranteed initialization of the fields of
+        //   `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
+        //   `FileNameLength` bytes)
         let (name, is_directory, next_entry) = unsafe {
             let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
             // Guaranteed to be aligned in documentation for
@@ -1349,7 +1359,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
     let h = f.as_inner().as_raw_handle();
 
     unsafe {
-        let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
+        let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
         let data_ptr = data.0.as_mut_ptr();
         let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
         let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
index 2f7ec433bf26119fff40524224ca5463aa341d52..a71175069052f0539c8af1b850fc9ad48eee3d7d 100644 (file)
@@ -115,7 +115,7 @@ fn test_parse_prefix_verbatim_device() {
     assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe"));
 }
 
-// See #93586 for more infomation.
+// See #93586 for more information.
 #[test]
 fn test_windows_prefix_components() {
     use crate::path::Path;
index 9301c5a2ff300da1103c65ac4f8ddd3ecdf9ba55..cc08ae5f99f0e80d03c36998ad02df043dcf10d0 100644 (file)
@@ -85,7 +85,7 @@ def _download(path, url, probably_big, verbose, exception):
             option = "-#"
         else:
             option = "-s"
-        # If curl is not present on Win32, we shoud not sys.exit
+        # If curl is not present on Win32, we should not sys.exit
         #   but raise `CalledProcessError` or `OSError` instead
         require(["curl", "--version"], exception=platform_is_win32)
         run(["curl", option,
@@ -793,6 +793,8 @@ class RustBuild(object):
 
     def check_vendored_status(self):
         """Check that vendoring is configured properly"""
+        # keep this consistent with the equivalent check in rustbuild:
+        # https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/lib.rs#L399-L405
         if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
             if os.getuid() == 0:
                 self.use_vendored_sources = True
index 1e764811ea7977129ed794dc9176af204d7a7060..14e8ebd6876b45646a2bdbe97ae3b26248f8f35e 100644 (file)
@@ -946,7 +946,7 @@ pub(crate) fn fix_bin_or_dylib(&self, fname: &Path) {
         };
         patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
         if !fname.extension().map_or(false, |ext| ext == "so") {
-            // Finally, set the corret .interp for binaries
+            // Finally, set the correct .interp for binaries
             let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
             // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
             let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
@@ -962,7 +962,7 @@ pub(crate) fn download_component(&self, url: &str, dest_path: &Path, help_on_err
         let tempfile = self.tempdir().join(dest_path.file_name().unwrap());
         // While bootstrap itself only supports http and https downloads, downstream forks might
         // need to download components from other protocols. The match allows them adding more
-        // protocols without worrying about merge conficts if we change the HTTP implementation.
+        // protocols without worrying about merge conflicts if we change the HTTP implementation.
         match url.split_once("://").map(|(proto, _)| proto) {
             Some("http") | Some("https") => {
                 self.download_http_with_retries(&tempfile, url, help_on_error)
index 13e3049f2c81ff001a9bfbbce5efb075c676772c..7c062460c4f16e8eb21475c0e5da443bbc015f64 100644 (file)
@@ -388,6 +388,7 @@ fn eq(&self, other: &&str) -> bool {
 pub struct Target {
     /// Some(path to llvm-config) if using an external LLVM.
     pub llvm_config: Option<PathBuf>,
+    pub llvm_has_rust_patches: Option<bool>,
     /// Some(path to FileCheck) if one was specified.
     pub llvm_filecheck: Option<PathBuf>,
     pub llvm_libunwind: Option<LlvmLibunwind>,
@@ -733,6 +734,7 @@ struct TomlTarget {
         default_linker: Option<PathBuf> = "default-linker",
         linker: Option<String> = "linker",
         llvm_config: Option<String> = "llvm-config",
+        llvm_has_rust_patches: Option<bool> = "llvm-has-rust-patches",
         llvm_filecheck: Option<String> = "llvm-filecheck",
         llvm_libunwind: Option<String> = "llvm-libunwind",
         android_ndk: Option<String> = "android-ndk",
@@ -1109,6 +1111,7 @@ pub fn parse(args: &[String]) -> Config {
                 if let Some(ref s) = cfg.llvm_config {
                     target.llvm_config = Some(config.src.join(s));
                 }
+                target.llvm_has_rust_patches = cfg.llvm_has_rust_patches;
                 if let Some(ref s) = cfg.llvm_filecheck {
                     target.llvm_filecheck = Some(config.src.join(s));
                 }
index 952943b78c6a2c2ff5a081f456b78cabdb9019d5..9336f958cf28ae2c74d2d6de244108422b62dc80 100644 (file)
 use std::process::Command;
 use std::str;
 
+use config::Target;
 use filetime::FileTime;
 use once_cell::sync::OnceCell;
 
@@ -395,13 +396,18 @@ pub fn new(config: Config) -> Build {
         let src = config.src.clone();
         let out = config.out.clone();
 
+        #[cfg(unix)]
+        // keep this consistent with the equivalent check in x.py:
+        // https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797
         let is_sudo = match env::var_os("SUDO_USER") {
-            Some(sudo_user) => match env::var_os("USER") {
-                Some(user) => user != sudo_user,
-                None => false,
-            },
+            Some(_sudo_user) => {
+                let uid = unsafe { libc::getuid() };
+                uid == 0
+            }
             None => false,
         };
+        #[cfg(not(unix))]
+        let is_sudo = false;
 
         let ignore_git = config.ignore_git;
         let rust_info = channel::GitInfo::new(ignore_git, &src);
@@ -834,12 +840,13 @@ fn md_doc_out(&self, target: TargetSelection) -> Interned<PathBuf> {
     ///
     /// If no custom `llvm-config` was specified then Rust's llvm will be used.
     fn is_rust_llvm(&self, target: TargetSelection) -> bool {
-        if self.config.llvm_from_ci && target == self.config.build {
-            return true;
-        }
-
         match self.config.target_config.get(&target) {
-            Some(ref c) => c.llvm_config.is_none(),
+            Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched,
+            Some(Target { llvm_config, .. }) => {
+                // If the user set llvm-config we assume Rust is not patched,
+                // but first check to see if it was configured by llvm-from-ci.
+                (self.config.llvm_from_ci && target == self.config.build) || llvm_config.is_none()
+            }
             None => true,
         }
     }
@@ -1629,7 +1636,7 @@ fn chmod(_path: &Path, _perms: u32) {}
 /// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.)
 /// If the test is running and code is an error code, it will cause a panic.
 fn detail_exit(code: i32) -> ! {
-    // if in test and code is an error code, panic with staus code provided
+    // if in test and code is an error code, panic with status code provided
     if cfg!(test) && code != 0 {
         panic!("status code: {}", code);
     } else {
index e1cc8d671d7fe892a0048627a1b3ba2e3bbad846..62b56994afe70e773b4ba587dfb18dd8d6f2dffb 100644 (file)
@@ -638,7 +638,7 @@ fn configure_cmake(
 
         if target.contains("darwin") {
             // Make sure that CMake does not build universal binaries on macOS.
-            // Explicitly specifiy the one single target architecture.
+            // Explicitly specify the one single target architecture.
             if target.starts_with("aarch64") {
                 // macOS uses a different name for building arm64
                 cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64");
index f1c2a21de59f7fe39b03ca1df7715100c3683b31..f61c95830858fb6f4b869b79701305627c7adc55 100644 (file)
@@ -854,7 +854,10 @@ fn get_browser_ui_test_version_inner(npm: &Path, global: bool) -> Option<String>
         .output()
         .map(|output| String::from_utf8_lossy(&output.stdout).into_owned())
         .unwrap_or(String::new());
-    lines.lines().find_map(|l| l.split(":browser-ui-test@").skip(1).next()).map(|v| v.to_owned())
+    lines
+        .lines()
+        .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
+        .map(|v| v.to_owned())
 }
 
 fn get_browser_ui_test_version(npm: &Path) -> Option<String> {
index 71ea13071bd1f1345d0841f3208fa92f131b77a5..dd74726f856915a73311b92112066fe976d6e34a 100644 (file)
@@ -1,5 +1,6 @@
-FROM ubuntu:16.04
+FROM ubuntu:22.04
 
+ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++-multilib \
   make \
@@ -20,18 +21,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
 RUN mkdir -p /config
 RUN echo "[rust]" > /config/nopt-std-config.toml
 RUN echo "optimize = false" >> /config/nopt-std-config.toml
 
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-
-ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests \
-    --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests
 ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \
   && python3 ../x.py --stage 2 test
index ebeab3dbdb79913c87a55877c553e80e15dca62a..0c36cfd66bdef05ac0842102035c2288fc84b271 100644 (file)
@@ -1,5 +1,6 @@
-FROM ubuntu:16.04
+FROM ubuntu:22.04
 
+ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++-multilib \
   make \
@@ -20,14 +21,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu \
-    --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu
 # Exclude some tests that are unlikely to be platform specific, to speed up
 # this slow job.
 ENV SCRIPT python3 ../x.py --stage 2 test \
index 321b3d6ace486d84f5cc9bbe0cd6df052a96e7e0..d55d5b56ad3f5e8cccfced590d0d2c0a81dcbf0f 100644 (file)
@@ -1,5 +1,6 @@
-FROM ubuntu:16.04
+FROM ubuntu:22.04
 
+ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
@@ -23,13 +24,5 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-
-ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \
-    --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
 ENV RUST_CHECK_TARGET check-aux
index be4def85f14c4735c41b63da42f00a317273d438..80a004501a81bb590553c4d77e8861587f331d07 100644 (file)
@@ -1,5 +1,6 @@
-FROM ubuntu:16.04
+FROM ubuntu:22.04
 
+ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
@@ -19,14 +20,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
+# We are disabling CI LLVM since distcheck is an offline build.
 ENV NO_DOWNLOAD_CI_LLVM 1
 
-ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false \
-    --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false
 ENV SCRIPT python3 ../x.py --stage 2 test distcheck
 ENV DIST_SRC 1
index e2ec4f275001b53d24fececc91607eaa810e1a75..4350ca205862ce742923d79c642b2511ad19f955 100644 (file)
@@ -1,5 +1,6 @@
-FROM ubuntu:16.04
+FROM ubuntu:22.04
 
+ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
@@ -27,6 +28,7 @@ RUN apt-get install -y \
   libdbus-1-3 \
   libexpat1 \
   libfontconfig1 \
+  libgbm1 \
   libgcc1 \
   libgconf-2-4 \
   libgdk-pixbuf2.0-0 \
@@ -59,13 +61,10 @@ RUN apt-get install -y \
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
 COPY host-x86_64/x86_64-gnu-tools/checktools.sh /tmp/
 
-RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | tar -xJ
-ENV NODE_FOLDER=/node-v14.4.0-linux-x64/bin
+RUN curl -sL https://nodejs.org/dist/v14.20.0/node-v14.20.0-linux-x64.tar.xz | tar -xJ
+ENV NODE_FOLDER=/node-v14.20.0-linux-x64/bin
 ENV PATH="$NODE_FOLDER:${PATH}"
 
 COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/
@@ -80,14 +79,10 @@ COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/
 # the local version of the package is different than the one used by the CI.
 RUN npm install -g browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true
 
-# We are intentionally allowing an old toolchain on this builder (and that's
-# incompatible with LLVM downloads today).
-ENV NO_DOWNLOAD_CI_LLVM 1
-
 ENV RUST_CONFIGURE_ARGS \
-  --set llvm.allow-old-toolchain \
   --build=x86_64-unknown-linux-gnu \
   --save-toolstates=/tmp/toolstate/toolstates.json
 
 ENV SCRIPT /tmp/checktools.sh ../x.py && \
-  NODE_PATH=`npm root -g` python3 ../x.py test src/test/rustdoc-gui --stage 2
+  NODE_PATH=`npm root -g` python3 ../x.py test src/test/rustdoc-gui --stage 2 \
+    --test-args "'--no-sandbox --jobs 1'"
diff --git a/src/doc/rustc/src/platform-support/armv4t-none-eabi.md b/src/doc/rustc/src/platform-support/armv4t-none-eabi.md
new file mode 100644 (file)
index 0000000..cf831e1
--- /dev/null
@@ -0,0 +1,70 @@
+# armv4t-none-eabi
+
+Tier 3
+
+Bare-metal target for any cpu in the ARMv4T architecture family, supporting
+ARM/Thumb code interworking (aka `a32`/`t32`), with ARM code as the default code
+generation.
+
+In particular this supports the Gameboy Advance (GBA), but there's nothing GBA
+specific with this target, so any ARMv4T device should work fine.
+
+## Target Maintainers
+
+* [@Lokathor](https://github.com/lokathor)
+
+## Requirements
+
+The target is cross-compiled, and uses static linking.
+
+The linker that comes with rustc cannot link for this platform (the platform is
+too old). You will need the `arm-none-eabi-ld` linker from a GNU Binutils
+targeting ARM. This can be obtained for Windows/Mac/Linux from the [ARM
+Developer Website][arm-dev], or possibly from your OS's package manager.
+
+[arm-dev]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain
+
+This target doesn't provide a linker script, you'll need to bring your own
+according to the specific device you want to target. Pass
+`-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use
+`your_script.ld` during linking.
+
+## Building Rust Programs
+
+Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this target.
+
+Just use the `build-std` nightly cargo feature to build the `core` library. You
+can pass this as a command line argument to cargo, or your `.cargo/config.toml`
+file might include the following lines:
+
+```toml
+[unstable]
+build-std = ["core"]
+```
+
+Most of `core` should work as expected, with the following notes:
+* the target is "soft float", so `f32` and `f64` operations are emulated in
+  software.
+* integer division is also emulated in software.
+* the target is old enough that it doesn't have atomic instructions.
+
+Rust programs are output as ELF files.
+
+For running on hardware, you'll generally need to extract the "raw" program code
+out of the ELF and into a file of its own. The `objcopy` program provided as
+part of the GNU Binutils can do this:
+
+```shell
+arm-none-eabi-objcopy --output-target binary [in_file] [out_file]
+```
+
+## Testing
+
+This is a cross-compiled target that you will need to emulate during testing.
+
+Because this is a device-agnostic target, and the exact emulator that you'll
+need depends on the specific device you want to run your code on.
+
+For example, when programming for the Gameboy Advance, the
+[mgba-test-runner](https://github.com/agbrs/agb) program could be used to make a
+normal set of rust tests be run within the `mgba` emulator.
diff --git a/src/doc/rustc/src/platform-support/armv4t_none_eabi.md b/src/doc/rustc/src/platform-support/armv4t_none_eabi.md
deleted file mode 100644 (file)
index cf831e1..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-# armv4t-none-eabi
-
-Tier 3
-
-Bare-metal target for any cpu in the ARMv4T architecture family, supporting
-ARM/Thumb code interworking (aka `a32`/`t32`), with ARM code as the default code
-generation.
-
-In particular this supports the Gameboy Advance (GBA), but there's nothing GBA
-specific with this target, so any ARMv4T device should work fine.
-
-## Target Maintainers
-
-* [@Lokathor](https://github.com/lokathor)
-
-## Requirements
-
-The target is cross-compiled, and uses static linking.
-
-The linker that comes with rustc cannot link for this platform (the platform is
-too old). You will need the `arm-none-eabi-ld` linker from a GNU Binutils
-targeting ARM. This can be obtained for Windows/Mac/Linux from the [ARM
-Developer Website][arm-dev], or possibly from your OS's package manager.
-
-[arm-dev]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain
-
-This target doesn't provide a linker script, you'll need to bring your own
-according to the specific device you want to target. Pass
-`-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use
-`your_script.ld` during linking.
-
-## Building Rust Programs
-
-Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this target.
-
-Just use the `build-std` nightly cargo feature to build the `core` library. You
-can pass this as a command line argument to cargo, or your `.cargo/config.toml`
-file might include the following lines:
-
-```toml
-[unstable]
-build-std = ["core"]
-```
-
-Most of `core` should work as expected, with the following notes:
-* the target is "soft float", so `f32` and `f64` operations are emulated in
-  software.
-* integer division is also emulated in software.
-* the target is old enough that it doesn't have atomic instructions.
-
-Rust programs are output as ELF files.
-
-For running on hardware, you'll generally need to extract the "raw" program code
-out of the ELF and into a file of its own. The `objcopy` program provided as
-part of the GNU Binutils can do this:
-
-```shell
-arm-none-eabi-objcopy --output-target binary [in_file] [out_file]
-```
-
-## Testing
-
-This is a cross-compiled target that you will need to emulate during testing.
-
-Because this is a device-agnostic target, and the exact emulator that you'll
-need depends on the specific device you want to run your code on.
-
-For example, when programming for the Gameboy Advance, the
-[mgba-test-runner](https://github.com/agbrs/agb) program could be used to make a
-normal set of rust tests be run within the `mgba` emulator.
index c2a1613f288c5c8a7db18addef22bb474d17209b..53a510f080ece9cd899052107f28de1fad2a15ba 100644 (file)
@@ -79,7 +79,7 @@ the following commands:
 
 ```sh
 rustup target add x86_64-fuchsia
-rustup target add aarch_64-fuchsia
+rustup target add aarch64-fuchsia
 ```
 
 After installing our Fuchsia targets, we can now compile a Rust binary that targets
@@ -125,13 +125,20 @@ during compilation:
 [target.x86_64-fuchsia]
 
 rustflags = [
-    "-Lnative", "<SDK_PATH>/arch/x64/sysroot/lib",
-    "-Lnative", "<SDK_PATH>/arch/x64/lib"
+    "-Lnative=<SDK_PATH>/arch/x64/lib",
+    "-Lnative=<SDK_PATH>/arch/x64/sysroot/lib"
 ]
 ```
 
 *Note: Make sure to fill out `<SDK_PATH>` with the path to the downloaded [Fuchsia SDK].*
 
+These options configure the following:
+
+* `-Lnative=${SDK_PATH}/arch/${ARCH}/lib`: Link against Fuchsia libraries from
+  the SDK
+* `-Lnative=${SDK_PATH}/arch/${ARCH}/sysroot/lib`: Link against Fuchsia kernel
+  libraries from the SDK
+
 In total, our new project will look like:
 
 **Current directory structure**
@@ -368,6 +375,7 @@ language called CML. The Fuchsia devsite contains an [overview of CML] and a
 }
 ```
 
+**Current directory structure**
 ```txt
 hello_fuchsia/
 ┗━ pkg/
@@ -386,6 +394,9 @@ ${SDK_PATH}/tools/${ARCH}/cmc compile \
     -o pkg/meta/hello_fuchsia.cm
 ```
 
+*Note: `--includepath` tells the compiler where to look for `include`s from our CML.
+In our case, we're only using `syslog/client.shard.cml`.*
+
 **Current directory structure**
 ```txt
 hello_fuchsia/
@@ -397,19 +408,16 @@ hello_fuchsia/
    ┗━ hello_fuchsia.cml
 ```
 
-*Note: `--includepath` tells the compiler where to look for `include`s from our CML.
-In our case, we're only using `syslog/client.shard.cml`.*
-
 ### Building a Fuchsia package
 
 Next, we'll build a package manifest as defined by our manifest:
 
 ```sh
 ${SDK_PATH}/tools/${ARCH}/pm \
-    -o hello_fuchsia_manifest \
+    -o pkg/hello_fuchsia_manifest \
     -m pkg/hello_fuchsia.manifest \
     build \
-    -output-package-manifest hello_fuchsia_package_manifest
+    -output-package-manifest pkg/hello_fuchsia_package_manifest
 ```
 
 This will produce `pkg/hello_fuchsia_manifest/` which is a package manifest we can
@@ -469,15 +477,15 @@ We can publish our new package to that repository with:
 
 ```sh
 ${SDK_PATH}/tools/${ARCH}/pm publish \
-    -repo repo \
-    -lp -f <(echo "hello_fuchsia_package_manifest")
+    -repo pkg/repo \
+    -lp -f <(echo "pkg/hello_fuchsia_package_manifest")
 ```
 
 Then we can add the repository to `ffx`'s package server as `hello-fuchsia` using:
 
 ```sh
 ${SDK_PATH}/tools/${ARCH}/ffx repository add-from-pm \
-    repo \
+    pkg/repo \
     -r hello-fuchsia
 ```
 
index d325ba3346ab137361a1505112a8d7a7cb130ea6..b18a125f3b095e75e755cfb46a9b83b968f8a250 100644 (file)
@@ -87,7 +87,7 @@ Rust programs can be built for that target:
 rustc --target m68k-unknown-linux-gnu your-code.rs
 ```
 
-Very simple progams can be run using the `qemu-m68k-static` program:
+Very simple programs can be run using the `qemu-m68k-static` program:
 
 ```text
 $ qemu-m68k-static your-code
index 721c234c6e60ad01e38df9087f970d20bac3b021..fb0cea05d4405f1a43447723c3dad2deb9b56c1c 100644 (file)
@@ -25,7 +25,7 @@ Like with any other Windows target created binaries are in PE format.
 
 ## Building the target
 
-For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring corss compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors.
+For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring cross compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors.
 Native bootstrapping builds require rather fragile hacks until host artifacts are available so I won't describe them here.
 
 ## Building Rust programs
index 8f90d9c7453d0a81e479f4ed8eb473cc5e028a4c..295dec0f0e488aeee419db6e29e380c5d534fb42 100644 (file)
@@ -133,7 +133,7 @@ There are 3 common ways to compile native C code for UEFI targets:
 - Use native Windows targets. This means compiling your C code for the Windows
   platform as if it was the UEFI platform. This works for static libraries, but
   needs adjustments when linking into an UEFI executable. You can, however,
-  link such static libraries seemlessly into rust code compiled for UEFI
+  link such static libraries seamlessly into rust code compiled for UEFI
   targets. Be wary of any includes that are not specifically suitable for UEFI
   targets (especially the C standard library includes are not always
   compatible). Freestanding compilations are recommended to avoid
index 021b904debd8f6b8d3b3bd2c30b5390cfb39917d..6932e6a5764bc416e160f0aa8e2c1d8b438629db 100644 (file)
@@ -30,7 +30,7 @@ is 8-bytes large as well as pointers. The tradeoff, though, is that the maximum
 memory size is now the full 64-bit address space instead of the 4GB as limited
 by the 32-bit address space for `wasm32-unknown-unknown`.
 
-This target is not a stable target. The [memory64] WebAssembly proposal is stil
+This target is not a stable target. The [memory64] WebAssembly proposal is still
 in-progress and not standardized. This means that there are not many engines
 which implement the `memory64` feature and if they do they're likely behind a
 flag, for example:
index bfa92e7d32a8eaf1851275a680043049cdc71fff..321992f7b0d75882b1521e9e75273810a719a119 100644 (file)
@@ -143,7 +143,7 @@ fn do_features() {}
 
 #[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in names()
                                  // and because no value checking was enable for "has_feathers"
-                                 // no warning is emited for the value "zapping"
+                                 // no warning is emitted for the value "zapping"
 fn do_zapping() {}
 
 #[cfg(has_mumble_frotz)]    // This is UNEXPECTED because names checking is enable and
index 977d258529f8c41a5b1f48895f955dfbe35efb3a..3890a12b7e684a92c8883581a8cef32aed652503 100644 (file)
@@ -8,7 +8,7 @@ This flag will rewrite absolute paths under the current working directory,
 replacing the current working directory prefix with a specified value.
 
 The given value may be absolute or relative, or empty. This switch takes
-precidence over `--remap-path-prefix` in case they would both match a given
+precedence over `--remap-path-prefix` in case they would both match a given
 path.
 
 This flag helps to produce deterministic output, by removing the current working
index 1c34255919434528778692442631789887f6ba58..2617378ba5fd2d5cec6186e005fedb2b9841ae5a 100755 (executable)
@@ -15,7 +15,7 @@
 # Improvements to this script are greatly appreciated!
 
 if [[ $# != 2 ]]; then
-    echo "expected 2 arguments, recieved $#"
+    echo "expected 2 arguments, received $#"
     echo "example usage: './src/etc/cpu-usage-over-time-plot.sh \
 7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c \
 x86_64-gnu'"
index baf95627c702820ea56bac0fea38841e73383ed6..c97fb4b805473625201e5c46b5ab339ceb10ab64 100644 (file)
@@ -386,7 +386,7 @@ def check_tree_attr(tree, path, attr, pat, regexp):
     return ret
 
 
-# Returns the number of occurences matching the regex (`regexp`) and the text (`pat`).
+# Returns the number of occurrences matching the regex (`regexp`) and the text (`pat`).
 def check_tree_text(tree, path, pat, regexp, stop_at_first):
     path = normalize_xpath(path)
     match_count = 0
index cbdfea89efb8ade3f11bd2265037e01731c99652..7bc35c7d5516f16e537aa809197f831c26c8e6ab 100644 (file)
@@ -10,18 +10,19 @@ path = "lib.rs"
 arrayvec = { version = "0.7", default-features = false }
 askama = { version = "0.11", default-features = false, features = ["config"] }
 atty = "0.2"
-pulldown-cmark = { version = "0.9.2", default-features = false }
+itertools = "0.10.1"
 minifier = "0.2.2"
-serde = { version = "1.0", features = ["derive"] }
+once_cell = "1.10.0"
+pulldown-cmark = { version = "0.9.2", default-features = false }
+regex = "1"
+rustdoc-json-types = { path = "../rustdoc-json-types" }
 serde_json = "1.0"
+serde = { version = "1.0", features = ["derive"] }
 smallvec = "1.8.1"
 tempfile = "3"
-itertools = "0.10.1"
-regex = "1"
-rustdoc-json-types = { path = "../rustdoc-json-types" }
+thin-vec = "0.2.8"
 tracing = "0.1"
 tracing-tree = "0.2.0"
-once_cell = "1.10.0"
 
 [dependencies.tracing-subscriber]
 version = "0.3.3"
index 756e4f3b127e1f93b6d78c2eb35aab0f0da41ca8..175472797cb4622d599dd7dfaa222177a30f1c3b 100644 (file)
@@ -123,7 +123,7 @@ fn generate_for_trait(
             kind: Box::new(ImplItem(Box::new(Impl {
                 unsafety: hir::Unsafety::Normal,
                 generics: new_generics,
-                trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, &[])),
+                trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())),
                 for_: clean_middle_ty(ty, self.cx, None),
                 items: Vec::new(),
                 polarity,
index da15c3c2b1fabe2dd2ebf888fc2d7bb737077cdf..cc734389e070b29e5fc6c87455ad9fb204dba490 100644 (file)
@@ -115,7 +115,7 @@ pub(crate) fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec<Item> {
                             ),
                             // FIXME(eddyb) compute both `trait_` and `for_` from
                             // the post-inference `trait_ref`, as it's more accurate.
-                            trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, &[])),
+                            trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, ThinVec::new())),
                             for_: clean_middle_ty(ty.0, cx, None),
                             items: cx.tcx
                                 .associated_items(impl_def_id)
index 31b805f2ed7edf919a82ff6d41b064158010b6c6..c8aa51c3a49a3913e91067a674bf2ddacb3017c8 100644 (file)
@@ -3,9 +3,10 @@
 use std::iter::once;
 use std::sync::Arc;
 
+use thin_vec::ThinVec;
+
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::thin_vec::ThinVec;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -459,7 +460,7 @@ pub(crate) fn build_impl(
         ),
     };
     let polarity = tcx.impl_polarity(did);
-    let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, &[]));
+    let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, ThinVec::new()));
     if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() {
         super::build_deref_target_impls(cx, &trait_items, ret);
     }
index 41f9eb3cdf6bd5bb040ab7359fd2764cfe831e1d..ebf6c55ee35df7ce96acd8617308df1b77681726 100644 (file)
@@ -33,7 +33,8 @@
 use std::collections::BTreeMap;
 use std::default::Default;
 use std::hash::Hash;
-use std::{mem, vec};
+use std::mem;
+use thin_vec::ThinVec;
 
 use crate::core::{self, DocContext, ImplTraitParam};
 use crate::formats::item_type::ItemType;
@@ -125,7 +126,7 @@ fn clean_generic_bound<'tcx>(
                 bug!("clean: parenthesized `GenericBound::LangItemTrait`");
             };
 
-            let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings);
+            let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, bindings);
             GenericBound::TraitBound(
                 PolyTrait { trait_, generic_params: vec![] },
                 hir::TraitBoundModifier::None,
@@ -147,14 +148,14 @@ fn clean_generic_bound<'tcx>(
 pub(crate) fn clean_trait_ref_with_bindings<'tcx>(
     cx: &mut DocContext<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
-    bindings: &[TypeBinding],
+    bindings: ThinVec<TypeBinding>,
 ) -> Path {
     let kind = cx.tcx.def_kind(trait_ref.def_id).into();
     if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) {
         span_bug!(cx.tcx.def_span(trait_ref.def_id), "`TraitRef` had unexpected kind {:?}", kind);
     }
     inline::record_extern_fqn(cx, trait_ref.def_id, kind);
-    let path = external_path(cx, trait_ref.def_id, true, bindings.to_vec(), trait_ref.substs);
+    let path = external_path(cx, trait_ref.def_id, true, bindings, trait_ref.substs);
 
     debug!("ty::TraitRef\n  subst: {:?}\n", trait_ref.substs);
 
@@ -164,7 +165,7 @@ pub(crate) fn clean_trait_ref_with_bindings<'tcx>(
 fn clean_poly_trait_ref_with_bindings<'tcx>(
     cx: &mut DocContext<'tcx>,
     poly_trait_ref: ty::PolyTraitRef<'tcx>,
-    bindings: &[TypeBinding],
+    bindings: ThinVec<TypeBinding>,
 ) -> GenericBound {
     let poly_trait_ref = poly_trait_ref.lift_to_tcx(cx.tcx).unwrap();
 
@@ -327,7 +328,7 @@ fn clean_poly_trait_predicate<'tcx>(
     let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref);
     Some(WherePredicate::BoundPredicate {
         ty: clean_middle_ty(poly_trait_ref.skip_binder().self_ty(), cx, None),
-        bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, &[])],
+        bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())],
         bound_params: Vec::new(),
     })
 }
@@ -402,7 +403,7 @@ fn clean_projection<'tcx>(
     def_id: Option<DefId>,
 ) -> Type {
     let lifted = ty.lift_to_tcx(cx.tcx).unwrap();
-    let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), &[]);
+    let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), ThinVec::new());
     let self_type = clean_middle_ty(ty.self_ty(), cx, None);
     let self_def_id = if let Some(def_id) = def_id {
         cx.tcx.opt_parent(def_id).or(Some(def_id))
@@ -1591,12 +1592,12 @@ pub(crate) fn clean_middle_ty<'tcx>(
                 AdtKind::Enum => ItemType::Enum,
             };
             inline::record_extern_fqn(cx, did, kind);
-            let path = external_path(cx, did, false, vec![], substs);
+            let path = external_path(cx, did, false, ThinVec::new(), substs);
             Type::Path { path }
         }
         ty::Foreign(did) => {
             inline::record_extern_fqn(cx, did, ItemType::ForeignType);
-            let path = external_path(cx, did, false, vec![], InternalSubsts::empty());
+            let path = external_path(cx, did, false, ThinVec::new(), InternalSubsts::empty());
             Type::Path { path }
         }
         ty::Dynamic(obj, ref reg) => {
@@ -1620,7 +1621,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
             let mut bounds = dids
                 .map(|did| {
                     let empty = cx.tcx.intern_substs(&[]);
-                    let path = external_path(cx, did, false, vec![], empty);
+                    let path = external_path(cx, did, false, ThinVec::new(), empty);
                     inline::record_extern_fqn(cx, did, ItemType::Trait);
                     PolyTrait { trait_: path, generic_params: Vec::new() }
                 })
@@ -1696,7 +1697,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
                         }
                     }
 
-                    let bindings: Vec<_> = bounds
+                    let bindings: ThinVec<_> = bounds
                         .iter()
                         .filter_map(|bound| {
                             if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder()
@@ -1717,7 +1718,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
                         })
                         .collect();
 
-                    Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, &bindings))
+                    Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings))
                 })
                 .collect::<Vec<_>>();
             bounds.extend(regions);
@@ -1848,12 +1849,8 @@ fn clean_generic_args<'tcx>(
             })
             .collect::<Vec<_>>()
             .into();
-        let bindings = generic_args
-            .bindings
-            .iter()
-            .map(|x| clean_type_binding(x, cx))
-            .collect::<Vec<_>>()
-            .into();
+        let bindings =
+            generic_args.bindings.iter().map(|x| clean_type_binding(x, cx)).collect::<ThinVec<_>>();
         GenericArgs::AngleBracketed { args, bindings }
     }
 }
index 909a47d07b1666f92ecf4f44e7478066b3655b09..2808b400a0b531fbfa462e53b565feb42e1f88f3 100644 (file)
@@ -8,6 +8,7 @@
 use std::{cmp, fmt, iter};
 
 use arrayvec::ArrayVec;
+use thin_vec::ThinVec;
 
 use rustc_ast::attr;
 use rustc_ast::util::comments::beautify_doc_string;
@@ -15,7 +16,6 @@
 use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel};
 use rustc_const_eval::const_eval::is_unstable_const_fn;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::thin_vec::ThinVec;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
@@ -1303,7 +1303,7 @@ impl GenericBound {
     pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound {
         let did = cx.tcx.require_lang_item(LangItem::Sized, None);
         let empty = cx.tcx.intern_substs(&[]);
-        let path = external_path(cx, did, false, vec![], empty);
+        let path = external_path(cx, did, false, ThinVec::new(), empty);
         inline::record_extern_fqn(cx, did, ItemType::Trait);
         GenericBound::TraitBound(
             PolyTrait { trait_: path, generic_params: Vec::new() },
index 718cbbd2b83742bf4e97e7d5ce93710e1ea906fe..ac9ab33961676c4b4bc90a387ceccc4240055402 100644 (file)
@@ -12,7 +12,6 @@
 
 use rustc_ast as ast;
 use rustc_ast::tokenstream::TokenTree;
-use rustc_data_structures::thin_vec::ThinVec;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
@@ -23,6 +22,7 @@
 use rustc_span::symbol::{kw, sym, Symbol};
 use std::fmt::Write as _;
 use std::mem;
+use thin_vec::ThinVec;
 
 #[cfg(test)]
 mod tests;
@@ -102,7 +102,7 @@ fn external_generic_args<'tcx>(
     cx: &mut DocContext<'tcx>,
     did: DefId,
     has_self: bool,
-    bindings: Vec<TypeBinding>,
+    bindings: ThinVec<TypeBinding>,
     substs: SubstsRef<'tcx>,
 ) -> GenericArgs {
     let args = substs_to_args(cx, substs, has_self);
@@ -112,7 +112,7 @@ fn external_generic_args<'tcx>(
             // The trait's first substitution is the one after self, if there is one.
             match substs.iter().nth(if has_self { 1 } else { 0 }).unwrap().expect_ty().kind() {
                 ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(t, cx, None)).collect::<Vec<_>>().into(),
-                _ => return GenericArgs::AngleBracketed { args: args.into(), bindings: bindings.into() },
+                _ => return GenericArgs::AngleBracketed { args: args.into(), bindings },
             };
         let output = None;
         // FIXME(#20299) return type comes from a projection now
@@ -130,7 +130,7 @@ pub(super) fn external_path<'tcx>(
     cx: &mut DocContext<'tcx>,
     did: DefId,
     has_self: bool,
-    bindings: Vec<TypeBinding>,
+    bindings: ThinVec<TypeBinding>,
     substs: SubstsRef<'tcx>,
 ) -> Path {
     let def_kind = cx.tcx.def_kind(did);
index 6f49f00f93e5eb98abd62017ee1adaf0c7ca62c3..be10a5c101f7ffa3e5ad5c9aa028f40201a07d45 100644 (file)
@@ -349,8 +349,7 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
         let where_preds = comma_sep(where_predicates, false);
         let clause = if f.alternate() {
             if ending == Ending::Newline {
-                // add a space so stripping <br> tags and breaking spaces still renders properly
-                format!(" where{where_preds}, ")
+                format!(" where{where_preds},")
             } else {
                 format!(" where{where_preds}")
             }
@@ -364,20 +363,16 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
 
             if ending == Ending::Newline {
                 let mut clause = "&nbsp;".repeat(indent.saturating_sub(1));
-                // add a space so stripping <br> tags and breaking spaces still renders properly
-                write!(
-                    clause,
-                    " <span class=\"where fmt-newline\">where{where_preds},&nbsp;</span>"
-                )?;
+                write!(clause, "<span class=\"where fmt-newline\">where{where_preds},</span>")?;
                 clause
             } else {
                 // insert a <br> tag after a single space but before multiple spaces at the start
                 if indent == 0 {
-                    format!(" <br><span class=\"where\">where{where_preds}</span>")
+                    format!("<br><span class=\"where\">where{where_preds}</span>")
                 } else {
                     let mut clause = br_with_padding;
                     clause.truncate(clause.len() - 5 * "&nbsp;".len());
-                    write!(clause, " <span class=\"where\">where{where_preds}</span>")?;
+                    write!(clause, "<span class=\"where\">where{where_preds}</span>")?;
                     clause
                 }
             }
index 4a12d74ddef5a5844dd89255ed451eee1f43e7af..bb8e46af76286d9b74a22e54e868eb464094ed41 100644 (file)
@@ -450,7 +450,7 @@ impl<'a> PeekIter<'a> {
     fn new(iter: TokenIter<'a>) -> Self {
         Self { stored: VecDeque::new(), peek_pos: 0, iter }
     }
-    /// Returns the next item after the current one. It doesn't interfer with `peek_next` output.
+    /// Returns the next item after the current one. It doesn't interfere with `peek_next` output.
     fn peek(&mut self) -> Option<&(TokenKind, &'a str)> {
         if self.stored.is_empty() {
             if let Some(next) = self.iter.next() {
@@ -459,7 +459,7 @@ fn peek(&mut self) -> Option<&(TokenKind, &'a str)> {
         }
         self.stored.front()
     }
-    /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output.
+    /// Returns the next item after the last one peeked. It doesn't interfere with `peek` output.
     fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> {
         self.peek_pos += 1;
         if self.peek_pos - 1 < self.stored.len() {
index eff34047e3c8fb93ac5bfa1b2f72351ecfc892cc..7577c71962388ebc2617ebebd3b722d0ff55af10 100644 (file)
@@ -1737,8 +1737,8 @@ pub(crate) fn render_impl_summary(
     // in documentation pages for trait with automatic implementations like "Send" and "Sync".
     aliases: &[String],
 ) {
-    let id =
-        cx.derive_id(get_id_for_impl(&i.inner_impl().for_, i.inner_impl().trait_.as_ref(), cx));
+    let inner_impl = i.inner_impl();
+    let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx));
     let aliases = if aliases.is_empty() {
         String::new()
     } else {
@@ -1750,9 +1750,9 @@ pub(crate) fn render_impl_summary(
     write!(w, "<h3 class=\"code-header in-band\">");
 
     if let Some(use_absolute) = use_absolute {
-        write!(w, "{}", i.inner_impl().print(use_absolute, cx));
+        write!(w, "{}", inner_impl.print(use_absolute, cx));
         if show_def_docs {
-            for it in &i.inner_impl().items {
+            for it in &inner_impl.items {
                 if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
                     w.write_str("<span class=\"where fmt-newline\">  ");
                     assoc_type(
@@ -1770,11 +1770,11 @@ pub(crate) fn render_impl_summary(
             }
         }
     } else {
-        write!(w, "{}", i.inner_impl().print(false, cx));
+        write!(w, "{}", inner_impl.print(false, cx));
     }
     write!(w, "</h3>");
 
-    let is_trait = i.inner_impl().trait_.is_some();
+    let is_trait = inner_impl.trait_.is_some();
     if is_trait {
         if let Some(portability) = portability(&i.impl_item, Some(parent)) {
             write!(w, "<span class=\"item-info\">{}</span>", portability);
index c117e3ac40dab57e22db93ab76b4e1046ce43edb..abbfecd849781f7fd85006fbe2b182b6e06b47cc 100644 (file)
@@ -207,7 +207,6 @@ h1, h2, h3, h4, h5, h6,
 a.source,
 .search-input,
 .search-results .result-name,
-.content table td:first-child > a,
 .item-left > a,
 .out-of-band,
 span.since,
@@ -759,14 +758,6 @@ pre, .rustdoc.source .example-wrap {
        margin-bottom: 15px;
 }
 
-.content .docblock > .impl-items {
-       margin-left: 20px;
-       margin-top: -34px;
-}
-.content .docblock >.impl-items table td {
-       padding: 0;
-}
-
 .item-info {
        display: block;
 }
index 7d7a63c53847a501ed20f29b785715d60144789f..c27f0ce18c1414d027453b4e8d2799105c980cfa 100644 (file)
@@ -477,7 +477,7 @@ fn resolve<'path>(
             // If there's no `::`, it's not an associated item.
             // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
             .ok_or_else(|| {
-                debug!("found no `::`, assumming {} was correctly not in scope", item_name);
+                debug!("found no `::`, assuming {} was correctly not in scope", item_name);
                 UnresolvedPath {
                     item_id,
                     module_id,
@@ -750,7 +750,7 @@ fn resolve_associated_trait_item<'a>(
 ///
 /// This is just a wrapper around [`TyCtxt::impl_item_implementor_ids()`] and
 /// [`TyCtxt::associated_item()`] (with some helpful logging added).
-#[instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx), ret)]
 fn trait_assoc_to_impl_assoc_item<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_id: DefId,
@@ -760,9 +760,7 @@ fn trait_assoc_to_impl_assoc_item<'tcx>(
     debug!(?trait_to_impl_assoc_map);
     let impl_assoc_id = *trait_to_impl_assoc_map.get(&trait_assoc_id)?;
     debug!(?impl_assoc_id);
-    let impl_assoc = tcx.associated_item(impl_assoc_id);
-    debug!(?impl_assoc);
-    Some(impl_assoc)
+    Some(tcx.associated_item(impl_assoc_id))
 }
 
 /// Given a type, return all trait impls in scope in `module` for that type.
@@ -1256,7 +1254,7 @@ fn resolve_with_disambiguator_cached(
         &mut self,
         key: ResolutionInfo,
         diag: DiagnosticInfo<'_>,
-        // If errors are cached then they are only reported on first ocurrence
+        // If errors are cached then they are only reported on first occurrence
         // which we want in some cases but not in others.
         cache_errors: bool,
     ) -> Option<(Res, Option<UrlFragment>)> {
index 2b12d118bca0e3968e0598e68a2a7a008fcb2599..765f7c61bd392dc433d5e2e5aa2c55ea95828992 100644 (file)
@@ -3,7 +3,7 @@
 
 use crate::clean::cfg::Cfg;
 use crate::clean::inline::{load_attrs, merge_attrs};
-use crate::clean::{Crate, Item};
+use crate::clean::{Crate, Item, ItemKind};
 use crate::core::DocContext;
 use crate::fold::DocFolder;
 use crate::passes::Pass;
@@ -26,30 +26,50 @@ struct CfgPropagator<'a, 'tcx> {
     cx: &'a mut DocContext<'tcx>,
 }
 
-impl<'a, 'tcx> DocFolder for CfgPropagator<'a, 'tcx> {
-    fn fold_item(&mut self, mut item: Item) -> Option<Item> {
-        let old_parent_cfg = self.parent_cfg.clone();
+impl<'a, 'tcx> CfgPropagator<'a, 'tcx> {
+    // Some items need to merge their attributes with their parents' otherwise a few of them
+    // (mostly `cfg` ones) will be missing.
+    fn merge_with_parent_attributes(&mut self, item: &mut Item) {
+        let check_parent = match &*item.kind {
+            // impl blocks can be in different modules with different cfg and we need to get them
+            // as well.
+            ItemKind::ImplItem(_) => false,
+            kind if kind.is_non_assoc() => true,
+            _ => return,
+        };
 
-        if item.kind.is_non_assoc() &&
-            let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) {
-            let hir = self.cx.tcx.hir();
-            let hir_id = hir.local_def_id_to_hir_id(def_id);
+        let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local())
+            else { return };
+
+        let hir = self.cx.tcx.hir();
+        let hir_id = hir.local_def_id_to_hir_id(def_id);
+
+        if check_parent {
             let expected_parent = hir.get_parent_item(hir_id);
+            // If parents are different, it means that `item` is a reexport and we need
+            // to compute the actual `cfg` by iterating through its "real" parents.
+            if self.parent == Some(expected_parent) {
+                return;
+            }
+        }
 
-            // If parents are different, it means that `item` is a reexport and we need to compute
-            // the actual `cfg` by iterating through its "real" parents.
-            if self.parent != Some(expected_parent) {
-                let mut attrs = Vec::new();
-                for (parent_hir_id, _) in hir.parent_iter(hir_id) {
-                    if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) {
-                        attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id()));
-                    }
-                }
-                let (_, cfg) =
-                    merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
-                item.cfg = cfg;
+        let mut attrs = Vec::new();
+        for (parent_hir_id, _) in hir.parent_iter(hir_id) {
+            if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) {
+                attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id()));
             }
         }
+        let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
+        item.cfg = cfg;
+    }
+}
+
+impl<'a, 'tcx> DocFolder for CfgPropagator<'a, 'tcx> {
+    fn fold_item(&mut self, mut item: Item) -> Option<Item> {
+        let old_parent_cfg = self.parent_cfg.clone();
+
+        self.merge_with_parent_attributes(&mut item);
+
         let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) {
             (None, None) => None,
             (Some(rc), None) | (None, Some(rc)) => Some(rc),
index 83ed3752a824cd541aa608be7c46fb97b1d234c3..a9d768f0149d6280d561806d47bb3d038f50e414 100644 (file)
@@ -91,7 +91,7 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
             clean::ExternCrateItem { .. } => {}
             clean::ImportItem(ref imp) => {
                 // Because json doesn't inline imports from private modules, we need to mark
-                // the imported item as retained so it's impls won't be stripped.i
+                // the imported item as retained so it's impls won't be stripped.
                 //
                 // FIXME: Is it necessary to check for json output here: See
                 // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215
index b4fda5f8c8428d0c879382dbf0a6e616af8315f6..4dc9d183b0b20d2d1515e1a092057a411eee80f1 100644 (file)
@@ -24,7 +24,7 @@ trait Freeze { }
 #[lang="copy"]
 trait Copy { }
 
-//x86_64: define win64cc void @has_efiapi
+//x86_64: define dso_local win64cc void @has_efiapi
 //i686: define void @has_efiapi
 //aarch64: define dso_local void @has_efiapi
 //arm: define dso_local void @has_efiapi
index 530164edd468278073cbf036d0393f6fd3c37d60..a038dfe76f7077dbb59134f3f5d9ea4f36c36db5 100644 (file)
@@ -77,7 +77,7 @@ fn update_bar_value() {
     }
 }
 
-// CHECK: define void @test(){{.+}}addrspace(1)
+// CHECK: define dso_local void @test(){{.+}}addrspace(1)
 #[no_mangle]
 pub extern "C" fn test() {
     let mut buf = 7;
index 6c18adbcb3c92b5efc46cdc5122ab49e646c9ea4..f53fa240cd1adba3e52e9a9a92fe6af0aeae0a78 100644 (file)
@@ -1,5 +1,5 @@
 // Test that `wrapping_div` only checks divisor once.
-// This test checks that there is only a single compare agains -1 and -1 is not present as a
+// This test checks that there is only a single compare against -1 and -1 is not present as a
 // switch case (the second check present until rustc 1.12).
 // This test also verifies that a single panic call is generated (for the division by zero case).
 
index 1fe048068601d436e1dc85f71bc23cb446c0caf6..c092e28a05ac8f098769965785aeb9ae4f75f988 100644 (file)
@@ -5,7 +5,7 @@
 #[lang="sized"]
 trait Sized { }
 
-// Test that `nounwind` atributes are correctly applied to exported `aapcs` and
+// Test that `nounwind` attributes are correctly applied to exported `aapcs` and
 // `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We
 // disable optimizations above to prevent LLVM from inferring the attribute.
 
index e817d5715a189c9d321f83cb18369e69056a8295..8447bbeb1ed205e6f6a9b2695339e2c4d43f6de7 100644 (file)
@@ -1,6 +1,6 @@
 // compile-flags: -C panic=abort
 
-// Test that `nounwind` atributes are also applied to extern `C-unwind` Rust functions
+// Test that `nounwind` attributes are also applied to extern `C-unwind` Rust functions
 // when the code is compiled with `panic=abort`.
 
 #![crate_type = "lib"]
index f15765367532261eca351376d3d45337b642e023..e258dbcacd229a3f5aa7bfaffc7e3108546a597f 100644 (file)
@@ -1,6 +1,6 @@
 // compile-flags: -C opt-level=0
 
-// Test that `nounwind` atributes are correctly applied to exported `C` and `C-unwind` extern
+// Test that `nounwind` attributes are correctly applied to exported `C` and `C-unwind` extern
 // functions. `C-unwind` functions MUST NOT have this attribute. We disable optimizations above
 // to prevent LLVM from inferring the attribute.
 
index 52e0d2d6e02526ff3a5484bee386e744e8c5eec2..19a7228839abfb82662085d69e69b643416e16d1 100644 (file)
@@ -1,6 +1,6 @@
 // compile-flags: -C opt-level=0
 
-// Test that `nounwind` atributes are correctly applied to exported `cdecl` and
+// Test that `nounwind` attributes are correctly applied to exported `cdecl` and
 // `cdecl-unwind` extern functions. `cdecl-unwind` functions MUST NOT have this attribute. We
 // disable optimizations above to prevent LLVM from inferring the attribute.
 
index ed23235ebfa809c08f0e5e1f46915b8982f4818c..b74099a5d965b0f386e361404dd1121908f9852c 100644 (file)
@@ -5,7 +5,7 @@
 #[lang="sized"]
 trait Sized { }
 
-// Test that `nounwind` atributes are correctly applied to exported `fastcall` and
+// Test that `nounwind` attributes are correctly applied to exported `fastcall` and
 // `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We
 // disable optimizations above to prevent LLVM from inferring the attribute.
 
index 9a4b3d3b4848080f8c07696429bba0b669d1f229..106d593b21de23bda5e1a941117403b7692bddb1 100644 (file)
@@ -3,7 +3,7 @@
 
 #![crate_type = "lib"]
 
-// We disable optimizations to prevent LLVM from infering the attribute.
+// We disable optimizations to prevent LLVM from inferring the attribute.
 
 // CHECK: Function Attrs:{{.*}}nounwind
 // CHECK-NEXT: @foo
index 2783c83d3efe08e6c94827e33dfe89e45cc28999..c1c5bbdda3456ba8121cd62d24a090bc50172408 100644 (file)
@@ -3,7 +3,7 @@
 
 #![crate_type = "lib"]
 
-// We disable optimizations to prevent LLVM from infering the attribute.
+// We disable optimizations to prevent LLVM from inferring the attribute.
 
 extern "C" {
     fn bar();
index cfc140361f62363bb03c27eba653bc554dd3916a..c46d717331b465fda746b35aa9e6da5b329361c5 100644 (file)
@@ -4,7 +4,7 @@
 #![crate_type = "lib"]
 #![feature(c_unwind)]
 
-// We disable optimizations to prevent LLVM from infering the attribute.
+// We disable optimizations to prevent LLVM from inferring the attribute.
 
 // CHECK: Function Attrs:{{.*}}nounwind
 // CHECK-NEXT: @foo
index f1dff27ad67b25d1413423e67dd4b11d7c7710cd..8eff0719f8fa7c19826e56420e01d80b1b63f40a 100644 (file)
@@ -5,7 +5,7 @@
 #[lang="sized"]
 trait Sized { }
 
-// Test that `nounwind` atributes are correctly applied to exported `stdcall` and `stdcall-unwind`
+// Test that `nounwind` attributes are correctly applied to exported `stdcall` and `stdcall-unwind`
 // extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable
 // optimizations above to prevent LLVM from inferring the attribute.
 
index c4d51328352c06ca138f792ad2734913258244e3..2591c1d4814302f1cfb4882dc9d6bbf58b17536c 100644 (file)
@@ -1,6 +1,6 @@
 // compile-flags: -C opt-level=0
 
-// Test that `nounwind` atributes are correctly applied to exported `system` and `system-unwind`
+// Test that `nounwind` attributes are correctly applied to exported `system` and `system-unwind`
 // extern functions. `system-unwind` functions MUST NOT have this attribute. We disable
 // optimizations above to prevent LLVM from inferring the attribute.
 
index a38736f2a1f956ab60991d07d10752bca316d959..694fde17c3cbeedc5593ed638e7f925216c3b898 100644 (file)
@@ -5,7 +5,7 @@
 #[lang="sized"]
 trait Sized { }
 
-// Test that `nounwind` atributes are correctly applied to exported `sysv64` and
+// Test that `nounwind` attributes are correctly applied to exported `sysv64` and
 // `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We
 // disable optimizations above to prevent LLVM from inferring the attribute.
 
index d2cf041b72d2371efa2b994ce219d3b74cf3f227..7e81367fc5b78d5165c5c744e251620a6e105603 100644 (file)
@@ -5,7 +5,7 @@
 #[lang="sized"]
 trait Sized { }
 
-// Test that `nounwind` atributes are correctly applied to exported `thiscall` and
+// Test that `nounwind` attributes are correctly applied to exported `thiscall` and
 // `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We
 // disable optimizations above to prevent LLVM from inferring the attribute.
 
index 0fb9612a5e4e86a3657cbe01982f125884f58c30..d7eca2a97000298b15ba2dc919fe35754d3b074b 100644 (file)
@@ -5,7 +5,7 @@
 #[lang="sized"]
 trait Sized { }
 
-// Test that `nounwind` atributes are correctly applied to exported `vectorcall` and
+// Test that `nounwind` attributes are correctly applied to exported `vectorcall` and
 // `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute.
 // We disable optimizations above to prevent LLVM from inferring the attribute.
 
index 5d8482da63056cdc635d33fdb37eb74900fa035f..6591348c35d3d436d1409281be8091f227cefd92 100644 (file)
@@ -5,7 +5,7 @@
 #[lang="sized"]
 trait Sized { }
 
-// Test that `nounwind` atributes are correctly applied to exported `win64` and
+// Test that `nounwind` attributes are correctly applied to exported `win64` and
 // `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We
 // disable optimizations above to prevent LLVM from inferring the attribute.
 
index c939235fb5006c121d4afa0c5402b0e2a21a58d9..6ac3c079f81ba5da6457a56cb81f463a2de546cf 100644 (file)
@@ -5,7 +5,7 @@
 #![feature(c_unwind)]
 
 // Make sure these all do *not* get the attribute.
-// We disable optimizations to prevent LLVM from infering the attribute.
+// We disable optimizations to prevent LLVM from inferring the attribute.
 // CHECK-NOT: nounwind
 
 // "C" ABI
index 8124141418bc3cfbe53396a658f9e5077fbccc84..355d334585249bcc28e5c1ceb74250defd7547ae 100644 (file)
@@ -2,7 +2,7 @@
 // compile-flags: -Z query-dep-graph
 // aux-build:cached_hygiene.rs
 
-// This tests the folllowing scenario
+// This tests the following scenario
 // 1. A foreign crate is compiled with incremental compilation.
 //    This causes hygiene information to be saved to the incr cache.
 // 2. One function is the foreign crate is modified. This causes the
index 50d8fb86930776580195074f87c6dfc8556d36ee..8d13718b8d847a767c3c6f8a52d2b7f2dfd2c92f 100644 (file)
@@ -1,5 +1,5 @@
 // Regression test for hashing involving canonical variables.  In this
-// test -- which has an intensional error -- the type of the value
+// test -- which has an intentional error -- the type of the value
 // being dropped winds up including a type variable. Canonicalization
 // would then produce a `?0` which -- in turn -- triggered an ICE in
 // hashing.
index 4d48a5f0ac528e2d138504350b4a2463abed0447..95f3b8ae4d9868fa8050317b99dffdb67693b1ec 100644 (file)
@@ -3,7 +3,7 @@
 
 // rust-lang/rust#69798:
 //
-// This is analgous to cgu_invalidated_when_import_added, but it covers a
+// This is analogous to cgu_invalidated_when_import_added, but it covers a
 // problem uncovered where a change to the *export* set caused a link failure
 // when reusing post-LTO optimized object code.
 
index e85b4856f3a96ef98297eec55e1f042c73e50b2e..e86ebd354b1a9601b4ae29b2e13d17ba6ee1640f 100644 (file)
@@ -3,7 +3,7 @@
 
 // rust-lang/rust#69798:
 //
-// This is analgous to cgu_invalidated_when_export_added, but it covers the
+// This is analogous to cgu_invalidated_when_export_added, but it covers the
 // other direction. This is analogous to cgu_invalidated_when_import_added: we
 // include it, because it may uncover bugs in variant implementation strategies.
 
index 68c834dfbbf27bb491e76f51e1369074848ee83b..eb6cb09fc455492d9dcf2201b7565c83382f1316 100644 (file)
@@ -1,4 +1,4 @@
-//! Tests that we can propogate into places that are projections into unions
+//! Tests that we can propagate into places that are projections into unions
 // compile-flags: -Zunsound-mir-opts
 fn val() -> u32 {
     1
index 51dc6b53a4504cfdb9f2674394ae4c23f5f71901..f42ea620fb9d13313b4ab3e1e22bc84bccf3b5cb 100644 (file)
@@ -19,7 +19,7 @@ all:
        # Dump all the symbols from the staticlib into `syms`
        "$(LLVM_BIN_DIR)"/llvm-objdump -t $(TMPDIR)/libdownstream.a > $(TMPDIR)/syms
        # Count the global instances of `issue64153_test_function`. There'll be 2
-       # if the `upstream` object file got erronously included twice.
+       # if the `upstream` object file got erroneously included twice.
        # The line we are testing for with the regex looks something like:
        # 0000000000000000 g     F .text.issue64153_test_function       00000023 issue64153_test_function
        grep -c -e "[[:space:]]g[[:space:]]*F[[:space:]].*issue64153_test_function" $(TMPDIR)/syms > $(TMPDIR)/count
index cac881ece1296ef5b502e5f2c85ff3736d427f88..6fc2a6bada9a05d3028fdaa863d67a5773f52711 100644 (file)
@@ -60,7 +60,7 @@ endif
 # for now, but it is effectively ignored for all tests that don't include this file anyway.
 #
 # (Note that it's also possible the `_counters.<test>.txt` and `<test>.json` files (if generated)
-# may order results from multiple files inconsistently, which might also have to be accomodated
+# may order results from multiple files inconsistently, which might also have to be accommodated
 # if and when we allow `llvm-cov` to produce results for multiple files. Note, the path separators
 # appear to be normalized to `/` in those files, thankfully.)
 LLVM_COV_IGNORE_FILES=\
@@ -157,7 +157,7 @@ else
        # `// ignore-llvm-cov-show-diffs` anymore. This directive exists to work around a limitation
        # with `llvm-cov show`. When reporting coverage for multiple instantiations of a generic function,
        # with different type substitutions, `llvm-cov show` prints these in a non-deterministic order,
-       # breaking the `diff` comparision.
+       # breaking the `diff` comparison.
        #
        # A partial workaround is implemented below, with `diff --ignore-matching-lines=RE`
        # to ignore each line prefixing each generic instantiation coverage code region.
index 2f69adbd81c51eb884cb9ca25e4ac269a016b146..87ccb6c43eab3e344d29ee59325d1793a937fe44 100644 (file)
@@ -55,7 +55,7 @@
    53|      1|            1 // This line appears covered, but the 1-character expression span covering the `1`
                           ^0
    54|      1|              // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because
-   55|      1|              // `fn j()` executes the open brace for the funciton body, followed by the function's
+   55|      1|              // `fn j()` executes the open brace for the function body, followed by the function's
    56|      1|              // first executable statement, `match x`. Inner function declarations are not
    57|      1|              // "visible" to the MIR for `j()`, so the code region counts all lines between the
    58|      1|              // open brace and the first statement as executed, which is, in a sense, true.
index a6e387747068abedd73e2360cdcad9d4e6e598ec..efd9e62d64e1d3453f00a3f3a48700c52269e225 100644 (file)
@@ -52,7 +52,7 @@ fn c(x: u8) -> u8 {
         if x == 8 {
             1 // This line appears covered, but the 1-character expression span covering the `1`
               // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because
-              // `fn j()` executes the open brace for the funciton body, followed by the function's
+              // `fn j()` executes the open brace for the function body, followed by the function's
               // first executable statement, `match x`. Inner function declarations are not
               // "visible" to the MIR for `j()`, so the code region counts all lines between the
               // open brace and the first statement as executed, which is, in a sense, true.
index 54645e9e257c97d6d95ef979b8384ab3c96cde2c..944343df6e587fea2b6aefb2f4af315ec40ac893 100644 (file)
@@ -45,7 +45,7 @@ check cc_plus_one_cxx cc_plus_one_cxx.checks
 check cc_plus_one_cxx_asm cc_plus_one_cxx_asm.checks
 check cc_plus_one_asm cc_plus_one_asm.checks \
   || echo "warning: the cc crate forwards assembly files to the CC compiler." \
-           "Clang uses its own intergrated assembler, which does not include the LVI passes."
+           "Clang uses its own integrated assembler, which does not include the LVI passes."
 
 check cmake_plus_one_c cmake_plus_one_c.checks
 check cmake_plus_one_c_asm cmake_plus_one_c_asm.checks
index 87f91be3ac82cec26d87785a1afe2debd038aed8..7f3172878bfb5f74325ee5b1a0f75db7143c30d1 100644 (file)
@@ -143,3 +143,30 @@ pub trait SimpleTrait {}
 /// Some docs.
 #[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "emscripten", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
 impl SimpleTrait for LongItemInfo2 {}
+
+pub struct WhereWhitespace<T>;
+
+impl<T> WhereWhitespace<T> {
+    pub fn new<F>(f: F) -> Self
+    where
+        F: FnMut() -> i32,
+    {}
+}
+
+impl<K, T> Whitespace<&K> for WhereWhitespace<T>
+where
+    K: std::fmt::Debug,
+{
+    type Output = WhereWhitespace<T>;
+    fn index(&self, _key: &K) -> &Self::Output {
+        self
+    }
+}
+
+pub trait Whitespace<Idx>
+where
+    Idx: ?Sized,
+{
+    type Output;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
index 22212a31728dca6dc06524be2c9cc60e216a10fc..85e03c45e700e985bebd41ecd88898398fc70a30 100644 (file)
@@ -32,6 +32,6 @@ assert-property: (".item-decl pre", {"scrollWidth": "950"})
 size: (600, 600)
 goto: file://|DOC_PATH|/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html
 // It shouldn't have an overflow in the topbar either.
-assert-property: (".mobile-topbar .location", {"scrollWidth": "492"})
-assert-property: (".mobile-topbar .location", {"clientWidth": "492"})
+assert-property: (".mobile-topbar .location", {"scrollWidth": "502"})
+assert-property: (".mobile-topbar .location", {"clientWidth": "502"})
 assert-css: (".mobile-topbar .location", {"overflow-x": "hidden"})
diff --git a/src/test/rustdoc-gui/where-whitespace.goml b/src/test/rustdoc-gui/where-whitespace.goml
new file mode 100644 (file)
index 0000000..1a3ff1f
--- /dev/null
@@ -0,0 +1,27 @@
+// This test ensures that the where conditions are correctly displayed.
+goto: file://|DOC_PATH|/lib2/trait.Whitespace.html
+show-text: true
+// First, we check in the trait definition if the where clause is "on its own" (not on the same
+// line than "pub trait Whitespace<Idx>").
+compare-elements-position-false: (".item-decl code", ".where.fmt-newline", ("y"))
+// And that the code following it isn't on the same line either.
+compare-elements-position-false: (".item-decl .fnname", ".where.fmt-newline", ("y"))
+
+goto: file://|DOC_PATH|/lib2/struct.WhereWhitespace.html
+// We make the screen a bit wider to ensure that the trait impl is on one line.
+size: (915, 915)
+
+compare-elements-position-false: ("#method\.new .fnname", "#method\.new .where.fmt-newline", ("y"))
+// We ensure that both the trait name and the struct name are on the same line in
+// "impl<K, T> Whitespace<&K> for WhereWhitespace<T>".
+compare-elements-position: (
+    "#trait-implementations-list .impl h3 .trait",
+    "#trait-implementations-list .impl h3 .struct",
+    ("y"),
+)
+// And we now check that the where condition isn't on the same line.
+compare-elements-position-false: (
+    "#trait-implementations-list .impl h3 .trait",
+    "#trait-implementations-list .impl h3 .where.fmt-newline",
+    ("y"),
+)
index f48cad373cdedc9b083305b0cd552df1bbd0f8e7..14ffac1e1dce6c40ab9326f07ca9003a79be52a4 100644 (file)
@@ -1,5 +1,5 @@
 // check-pass
-// Regresion test for <https://github.com/rust-lang/rust/issues/79459>.
+// Regression test for <https://github.com/rust-lang/rust/issues/79459>.
 pub trait Query {}
 
 pub trait AsQuery {
index 352a8e646bb49fe76e9fc548a87be15378b2dd20..87d2f29e26055461a284f5b581469cc2e421e399 100644 (file)
@@ -31,12 +31,12 @@ impl Trait<{1 + 2}> for u8 {}
 impl<const N: usize> Trait<N> for [u8; N] {}
 
 // @has foo/struct.Foo.html '//pre[@class="rust struct"]' \
-//      'pub struct Foo<const N: usize> where u8: Trait<N>'
+//      'pub struct Foo<const N: usize>where u8: Trait<N>'
 pub struct Foo<const N: usize> where u8: Trait<N>;
 // @has foo/struct.Bar.html '//pre[@class="rust struct"]' 'pub struct Bar<T, const N: usize>(_)'
 pub struct Bar<T, const N: usize>([T; N]);
 
-// @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Foo<M> where u8: Trait<M>'
+// @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Foo<M>where u8: Trait<M>'
 impl<const M: usize> Foo<M> where u8: Trait<M> {
     // @has - '//*[@id="associatedconstant.FOO_ASSOC"]' 'pub const FOO_ASSOC: usize'
     pub const FOO_ASSOC: usize = M + 13;
@@ -50,14 +50,14 @@ pub fn hey<const N: usize>(&self) -> Bar<u8, N> {
 // @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8%2C%20M%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Bar<u8, M>'
 impl<const M: usize> Bar<u8, M> {
     // @has - '//*[@id="method.hey"]' \
-    //      'pub fn hey<const N: usize>(&self) -> Foo<N> where u8: Trait<N>'
+    //      'pub fn hey<const N: usize>(&self) -> Foo<N>where u8: Trait<N>'
     pub fn hey<const N: usize>(&self) -> Foo<N> where u8: Trait<N> {
         Foo
     }
 }
 
 // @has foo/fn.test.html '//pre[@class="rust fn"]' \
-//      'pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N>'
+//      'pub fn test<const N: usize>() -> impl Trait<N>where u8: Trait<N>'
 pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N> {
     2u8
 }
diff --git a/src/test/rustdoc/doc_auto_cfg_nested_impl.rs b/src/test/rustdoc/doc_auto_cfg_nested_impl.rs
new file mode 100644 (file)
index 0000000..4d73e0d
--- /dev/null
@@ -0,0 +1,24 @@
+// Regression test for <https://github.com/rust-lang/rust/issues/101129>.
+
+#![feature(doc_auto_cfg)]
+#![crate_type = "lib"]
+#![crate_name = "foo"]
+
+pub struct S;
+pub trait MyTrait1 {}
+pub trait MyTrait2 {}
+
+// @has foo/struct.S.html
+// @has - '//*[@id="impl-MyTrait1-for-S"]//*[@class="stab portability"]' \
+//        'Available on non-crate feature coolstuff only.'
+#[cfg(not(feature = "coolstuff"))]
+impl MyTrait1 for S {}
+
+#[cfg(not(feature = "coolstuff"))]
+mod submod {
+    use crate::{S, MyTrait2};
+    // This impl should also have the `not(feature = "coolstuff")`.
+    // @has - '//*[@id="impl-MyTrait2-for-S"]//*[@class="stab portability"]' \
+    //        'Available on non-crate feature coolstuff only.'
+    impl MyTrait2 for S {}
+}
index 9b4ceb4f9cd86bbd29c0eb55840e718dc423a040..006132ef8aa40f8994c068399c4fc3670c0913a9 100644 (file)
@@ -3,7 +3,7 @@
 // rust-lang/rust#75225
 //
 // Since Rust 2018 we encourage writing out <'_> explicitly to make it clear
-// that borrowing is occuring. Make sure rustdoc is following the same idiom.
+// that borrowing is occurring. Make sure rustdoc is following the same idiom.
 
 #![crate_name = "foo"]
 
index ae981b9499a67cb20d131528639b836c2196ae34..2b9d4952d04eecd6c670d920b4f530b62dfc009d 100644 (file)
@@ -3,7 +3,7 @@
 
 // @has foo/trait.LendingIterator.html
 pub trait LendingIterator {
-    // @has - '//*[@id="associatedtype.Item"]//h4[@class="code-header"]' "type Item<'a> where Self: 'a"
+    // @has - '//*[@id="associatedtype.Item"]//h4[@class="code-header"]' "type Item<'a>where Self: 'a"
     type Item<'a> where Self: 'a;
 
     // @has - '//*[@id="tymethod.next"]//h4[@class="code-header"]' \
@@ -24,7 +24,7 @@ fn next<'a>(&self) -> () {}
 pub struct Infinite<T>(T);
 
 // @has foo/trait.LendingIterator.html
-// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a> where Self: 'a = &'a T"
+// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a>where Self: 'a = &'a T"
 impl<T> LendingIterator for Infinite<T> {
     type Item<'a> where Self: 'a = &'a T;
 
index b75b8de52f9cb6c3fab04e3a1dbf86caa7d4e019..59b5b6e5797cc47e3037fdd914df2f6476a24187 100644 (file)
@@ -4,7 +4,7 @@
 pub trait Trait<'x> {}
 
 // @has foo/fn.test1.html
-// @has - '//pre' "pub fn test1<T>() where for<'a> &'a T: Iterator,"
+// @has - '//pre' "pub fn test1<T>()where for<'a> &'a T: Iterator,"
 pub fn test1<T>()
 where
     for<'a> &'a T: Iterator,
@@ -12,7 +12,7 @@ pub fn test1<T>()
 }
 
 // @has foo/fn.test2.html
-// @has - '//pre' "pub fn test2<T>() where for<'a, 'b> &'a T: Trait<'b>,"
+// @has - '//pre' "pub fn test2<T>()where for<'a, 'b> &'a T: Trait<'b>,"
 pub fn test2<T>()
 where
     for<'a, 'b> &'a T: Trait<'b>,
@@ -20,7 +20,7 @@ pub fn test2<T>()
 }
 
 // @has foo/fn.test3.html
-// @has - '//pre' "pub fn test3<F>() where F: for<'a, 'b> Fn(&'a u8, &'b u8),"
+// @has - '//pre' "pub fn test3<F>()where F: for<'a, 'b> Fn(&'a u8, &'b u8),"
 pub fn test3<F>()
 where
     F: for<'a, 'b> Fn(&'a u8, &'b u8),
@@ -38,7 +38,7 @@ pub struct Foo<'a> {
 // @has - '//span[@id="structfield.some_trait"]' "some_trait: &'a dyn for<'b> Trait<'b>"
 
 impl<'a> Foo<'a> {
-    // @has - '//h4[@class="code-header"]' "pub fn bar<T>() where T: Trait<'a>,"
+    // @has - '//h4[@class="code-header"]' "pub fn bar<T>()where T: Trait<'a>,"
     pub fn bar<T>()
     where
         T: Trait<'a>,
index 249158c1a1f89bca9a9f1b63f1da16c76785e388..b1481e1f27978045e6864c92d42841fdf5d0755a 100644 (file)
@@ -6,7 +6,7 @@
 pub struct Foo<T> { field: T }
 
 // @has impl_parts/struct.Foo.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-//     "impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync,"
+//     "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync,"
 // @has impl_parts/trait.AnAutoTrait.html '//*[@class="item-list"]//h3[@class="code-header in-band"]' \
-//     "impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync,"
+//     "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync,"
 impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync {}
index 96a43323ce29d2432ca9163030267dadff0d6c29..f037a8e1a83d48fb5ab3a7590ffb8628f11f7b92 100644 (file)
@@ -7,7 +7,7 @@
 // @has 'foo/builders/struct.ActionRowBuilder.html'
 // @has - '//*[@id="synthetic-implementations"]' 'Auto Trait Implementations'
 
-// And that the link in the module is targetting it.
+// And that the link in the module is targeting it.
 // @has 'foo/builders/index.html'
 // @has - '//a[@href="struct.ActionRowBuilder.html"]' 'ActionRowBuilder'
 
index 84fc6f94a265a62a5d5d1becf5daf1f7efaf389d..643f93875909390b0b8bf410c325ecfd9393a309 100644 (file)
@@ -25,7 +25,7 @@ pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
 
 pub mod reexport {
     // @has issue_20727_4/reexport/trait.Index.html
-    // @has - '//*[@class="rust trait"]' 'trait Index<Idx> where Idx: ?Sized, {'
+    // @has - '//*[@class="rust trait"]' 'trait Index<Idx>where Idx: ?Sized,{'
     // @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
     // @has - '//*[@class="rust trait"]' \
     //        'fn index(&self, index: Idx) -> &Self::Output'
@@ -33,7 +33,7 @@ pub mod reexport {
 
     // @has issue_20727_4/reexport/trait.IndexMut.html
     // @has - '//*[@class="rust trait"]' \
-    //        'trait IndexMut<Idx>: Index<Idx> where Idx: ?Sized, {'
+    //        'trait IndexMut<Idx>: Index<Idx>where Idx: ?Sized,{'
     // @has - '//*[@class="rust trait"]' \
     //        'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
     pub use issue_20727::IndexMut;
index 2a586b6ff6cdcfe3f427800bfe57cdeabb89c50f..29d2ec64c206d007d1d1a395fc0c38a3feb10615 100644 (file)
@@ -5,5 +5,5 @@
 
 // @has issue_21801/struct.Foo.html
 // @has - '//*[@id="method.new"]' \
-//        'fn new<F>(f: F) -> Foo where F: FnMut() -> i32'
+//        'fn new<F>(f: F) -> Foowhere F: FnMut() -> i32'
 pub use issue_21801::Foo;
index 635c3175f8138a39e40bc83d04f2bc1a6c0386be..134821e1ef3ea9bc724bb81e119a664724597461 100644 (file)
@@ -5,7 +5,7 @@ pub trait MyTrait {
     fn my_string(&self) -> String;
 }
 
-// @has - "//div[@id='implementors-list']//*[@id='impl-MyTrait-for-T']//h3[@class='code-header in-band']" "impl<T> MyTrait for T where T: Debug"
+// @has - "//div[@id='implementors-list']//*[@id='impl-MyTrait-for-T']//h3[@class='code-header in-band']" "impl<T> MyTrait for Twhere T: Debug"
 impl<T> MyTrait for T
 where
     T: fmt::Debug,
index 4184086f622abaff7f57fb211360c358ccfe15c8..91b67757453d2e788343eb82a15fa1d26313d309 100644 (file)
@@ -2,5 +2,5 @@
 
 pub trait Bar {}
 
-// @has foo/struct.Foo.html '//pre' 'pub struct Foo<T>(pub T) where T: Bar;'
+// @has foo/struct.Foo.html '//pre' 'pub struct Foo<T>(pub T)where T: Bar;'
 pub struct Foo<T>(pub T) where T: Bar;
index d88c29217023a9f9b5da233a3766297a872eeb0b..43fb705f58994717d9868b4942bdf5801c7fb847 100644 (file)
@@ -11,8 +11,8 @@ impl<B, C> Signal2 for B where B: Signal<Item = C> {
 }
 
 // @has issue_50159/struct.Switch.html
-// @has - '//h3[@class="code-header in-band"]' 'impl<B> Send for Switch<B> where <B as Signal>::Item: Send'
-// @has - '//h3[@class="code-header in-band"]' 'impl<B> Sync for Switch<B> where <B as Signal>::Item: Sync'
+// @has - '//h3[@class="code-header in-band"]' 'impl<B> Send for Switch<B>where <B as Signal>::Item: Send'
+// @has - '//h3[@class="code-header in-band"]' 'impl<B> Sync for Switch<B>where <B as Signal>::Item: Sync'
 // @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
 // @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]' 5
 pub struct Switch<B: Signal> {
index ee11ccc6811633fac9cd3f6b09abfc35b7e0a62f..aa5890a84514fc56364e8724fbcaf5891fdbf6d0 100644 (file)
@@ -8,7 +8,7 @@ pub trait Owned<'a> {
 
 // @has issue_51236/struct.Owned.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<T> Send for Owned<T> where <T as Owned<'static>>::Reader: Send"
+// "impl<T> Send for Owned<T>where <T as Owned<'static>>::Reader: Send"
 pub struct Owned<T> where T: for<'a> ::traits::Owned<'a> {
     marker: PhantomData<<T as ::traits::Owned<'static>>::Reader>,
 }
index bedaf5c4ddc360a311c83fc415943cab0b1e08f2..ce0f85d25da56d70009b7fee9911db47c06bd05e 100644 (file)
@@ -1,13 +1,11 @@
 pub trait ScopeHandle<'scope> {}
 
-
-
 // @has issue_54705/struct.ScopeFutureContents.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'scope, S> Send for ScopeFutureContents<'scope, S> where S: Sync"
+// "impl<'scope, S> Send for ScopeFutureContents<'scope, S>where S: Sync"
 //
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'scope, S> Sync for ScopeFutureContents<'scope, S> where S: Sync"
+// "impl<'scope, S> Sync for ScopeFutureContents<'scope, S>where S: Sync"
 pub struct ScopeFutureContents<'scope, S>
     where S: ScopeHandle<'scope>,
 {
index 83e08094c09534abf9da016c5c95fd7c42c8e9e9..a8841f137fecff11bb32a8a7fa028e850e30d40d 100644 (file)
@@ -8,7 +8,7 @@
 
 extern crate issue_98697_reexport_with_anonymous_lifetime;
 
-// @has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'fn repro<F>() where F: Fn(&str)'
+// @has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'fn repro<F>()where F: Fn(&str)'
 // @!has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'for<'
 pub use issue_98697_reexport_with_anonymous_lifetime::repro;
 
index ee3010514417a7da47aaaf9b9761b7ebb166a740..d3cf7e1406503f72ef67f4c38fb8614776410c5e 100644 (file)
@@ -4,7 +4,7 @@
 // (yes, that's a thing), rustdoc lists both of them on the index page,
 // but only documents the first one on the page for the macro.
 // Fortunately, this can only happen in document private items mode,
-// but it still isn't ideal beahvior.
+// but it still isn't ideal behavior.
 //
 // See https://github.com/rust-lang/rust/pull/88019#discussion_r693920453
 //
index b3f511bc1f153fc423b713ea2866bccc7cc4d65f..7f8f74ff457a5ab906ec670ea3d930d4e049adaa 100644 (file)
@@ -7,8 +7,8 @@
 // @has - '//span[@class="in-band"]' 'Primitive Type slice'
 // @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
 // @has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations'
-// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Send for [T] where T: Send'
-// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Sync for [T] where T: Sync'
+// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Send for [T]where T: Send'
+// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Sync for [T]where T: Sync'
 #[doc(primitive = "slice")]
 /// this is a test!
 mod slice_prim {}
index 54c54fdbf68a8acafc9e50e48d0af6f9f7d3bc7b..19138fd1aceb20d1c1c8edb5699b83befb86cac9 100644 (file)
@@ -1,6 +1,6 @@
 // @has basic/struct.Foo.html
-// @has - '//h3[@class="code-header in-band"]' 'impl<T> Send for Foo<T> where T: Send'
-// @has - '//h3[@class="code-header in-band"]' 'impl<T> Sync for Foo<T> where T: Sync'
+// @has - '//h3[@class="code-header in-band"]' 'impl<T> Send for Foo<T>where T: Send'
+// @has - '//h3[@class="code-header in-band"]' 'impl<T> Sync for Foo<T>where T: Sync'
 // @count - '//*[@id="implementations-list"]//*[@class="impl has-srclink"]' 0
 // @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]' 5
 pub struct Foo<T> {
index f9017b90caee774d2022797e0321002bb77b630f..39f78983da2b031766ab2c8968a2d26d73e4f320 100644 (file)
@@ -21,7 +21,7 @@ pub struct Foo<T> {
 
 // @has complex/struct.NotOuter.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K> where K: for<'b> Fn((&'b bool, &'a u8)) \
+// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K>where K: for<'b> Fn((&'b bool, &'a u8)) \
 // -> &'b i8, T: MyTrait<'a>, <T as MyTrait<'a>>::MyItem: Copy, 'a: 'static"
 
 pub use foo::{Foo, Inner as NotInner, MyTrait as NotMyTrait, Outer as NotOuter};
index ee1393f9729c176ccff505f02e489c9e2116a2dc..0c94850e78608c2cb12b5f90bf4cf80d6fd18e77 100644 (file)
@@ -10,10 +10,10 @@ unsafe impl<'a, T> Send for Inner<'a, T>
 
 // @has lifetimes/struct.Foo.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'c, K> Send for Foo<'c, K> where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static"
+// "impl<'c, K> Send for Foo<'c, K>where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static"
 //
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'c, K> Sync for Foo<'c, K> where K: Sync"
+// "impl<'c, K> Sync for Foo<'c, K>where K: Sync"
 pub struct Foo<'c, K: 'c> {
     inner_field: Inner<'c, K>,
 }
index 49bad162211b7041be4ba179022091d2d11f3b9f..35047e3e8c0717349eca64a02043ac1b06c4bc97 100644 (file)
@@ -1,6 +1,6 @@
 // @has manual/struct.Foo.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// 'impl<T> Sync for Foo<T> where T: Sync'
+// 'impl<T> Sync for Foo<T>where T: Sync'
 //
 // @has - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
 // 'impl<T> Send for Foo<T>'
index 69edbee619e31e8316c1796bcc7916cfd9cd9c28..09587bcc30f13954f3c742080c7ec24be6453d9e 100644 (file)
@@ -10,10 +10,10 @@ unsafe impl<T> Send for Inner<T>
 
 // @has nested/struct.Foo.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// 'impl<T> Send for Foo<T> where T: Copy'
+// 'impl<T> Send for Foo<T>where T: Copy'
 //
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// 'impl<T> Sync for Foo<T> where T: Sync'
+// 'impl<T> Sync for Foo<T>where T: Sync'
 pub struct Foo<T> {
     inner_field: Inner<T>,
 }
index 16ab876e829ef8f25f4d82d79529e4c89c84f3ca..41375decc8a4aa6c9971764ab82fab822973f17c 100644 (file)
@@ -10,7 +10,7 @@ unsafe impl<T> Send for Inner<T>
 
 // @has no_redundancy/struct.Outer.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<T> Send for Outer<T> where T: Send + Copy"
+// "impl<T> Send for Outer<T>where T: Send + Copy"
 pub struct Outer<T> {
     inner_field: Inner<T>,
 }
index 8b020582563f3d3ac9fe6cd6be4975c2c66641bf..e80b1b1dc9bcf99c96fc535bcc8d9ca7f9ed470a 100644 (file)
@@ -24,10 +24,10 @@ unsafe impl<'a, T> Sync for Inner<'a, T>
 
 // @has project/struct.Foo.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'c, K> Send for Foo<'c, K> where K: MyTrait<MyItem = bool>, 'c: 'static"
+// "impl<'c, K> Send for Foo<'c, K>where K: MyTrait<MyItem = bool>, 'c: 'static"
 //
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<'c, K> Sync for Foo<'c, K> where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, \
+// "impl<'c, K> Sync for Foo<'c, K>where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, \
 // 'c: 'static,"
 pub struct Foo<'c, K: 'c> {
     inner_field: Inner<'c, K>,
index ccef901b18da3e57b359973eb52e04cb3f5f19ad..d15a8de7d2fe14fdb20381086bc6dc7a6762e58c 100644 (file)
@@ -24,6 +24,6 @@ impl<T> Pattern for Wrapper<T> {
 
 // @has self_referential/struct.WriteAndThen.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<P1> Send for WriteAndThen<P1>  where  <P1 as Pattern>::Value: Send"
+// "impl<P1> Send for WriteAndThen<P1>where    <P1 as Pattern>::Value: Send"
 pub struct WriteAndThen<P1>(pub P1::Value,pub <Constrain<P1, Wrapper<P1::Value>> as Pattern>::Value)
     where P1: Pattern;
index 36e985144b0e09026e60796a8908419b766eb363..08e9567313e22bb78f0faf6f5701537b6c9e3ecf 100644 (file)
@@ -4,7 +4,7 @@ pub trait OwnedTrait<'a> {
 
 // @has static_region/struct.Owned.html
 // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-// "impl<T> Send for Owned<T> where <T as OwnedTrait<'static>>::Reader: Send"
+// "impl<T> Send for Owned<T>where <T as OwnedTrait<'static>>::Reader: Send"
 pub struct Owned<T> where T: OwnedTrait<'static> {
     marker: <T as OwnedTrait<'static>>::Reader,
 }
index 3150a8ea05f41f321dde58bbe2dded183deead48..b8502e10a48c4dcb5ad05e763930a673bae55046 100644 (file)
@@ -7,7 +7,7 @@ pub trait SomeTrait<Rhs = Self>
 }
 
 // @has 'foo/trait.SomeTrait.html'
-// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, "
+// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, "
 impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)
 where
     A: PartialOrd<A> + PartialEq<A>,
index 50a5722fbaff6926e5feec51c6ffd18d531bac5a..c1a630e25ba0ef00881c4682afd10ed922f97351 100644 (file)
@@ -3,17 +3,17 @@
 
 pub trait MyTrait { fn dummy(&self) { } }
 
-// @has foo/struct.Alpha.html '//pre' "pub struct Alpha<A>(_) where A: MyTrait"
+// @has foo/struct.Alpha.html '//pre' "pub struct Alpha<A>(_)where A: MyTrait"
 pub struct Alpha<A>(A) where A: MyTrait;
-// @has foo/trait.Bravo.html '//pre' "pub trait Bravo<B> where B: MyTrait"
+// @has foo/trait.Bravo.html '//pre' "pub trait Bravo<B>where B: MyTrait"
 pub trait Bravo<B> where B: MyTrait { fn get(&self, B: B); }
-// @has foo/fn.charlie.html '//pre' "pub fn charlie<C>() where C: MyTrait"
+// @has foo/fn.charlie.html '//pre' "pub fn charlie<C>()where C: MyTrait"
 pub fn charlie<C>() where C: MyTrait {}
 
 pub struct Delta<D>(D);
 
 // @has foo/struct.Delta.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-//          "impl<D> Delta<D> where D: MyTrait"
+//          "impl<D> Delta<D>where D: MyTrait"
 impl<D> Delta<D> where D: MyTrait {
     pub fn delta() {}
 }
@@ -33,19 +33,19 @@ pub trait TraitWhere {
 }
 
 // @has foo/struct.Echo.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-//          "impl<E> MyTrait for Echo<E> where E: MyTrait"
+//          "impl<E> MyTrait for Echo<E>where E: MyTrait"
 // @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header in-band"]' \
-//          "impl<E> MyTrait for Echo<E> where E: MyTrait"
-impl<E> MyTrait for Echo<E> where E: MyTrait {}
+//          "impl<E> MyTrait for Echo<E>where E: MyTrait"
+impl<E> MyTrait for Echo<E>where E: MyTrait {}
 
 pub enum Foxtrot<F> { Foxtrot1(F) }
 
 // @has foo/enum.Foxtrot.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \
-//          "impl<F> MyTrait for Foxtrot<F> where F: MyTrait"
+//          "impl<F> MyTrait for Foxtrot<F>where F: MyTrait"
 // @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header in-band"]' \
-//          "impl<F> MyTrait for Foxtrot<F> where F: MyTrait"
-impl<F> MyTrait for Foxtrot<F> where F: MyTrait {}
+//          "impl<F> MyTrait for Foxtrot<F>where F: MyTrait"
+impl<F> MyTrait for Foxtrot<F>where F: MyTrait {}
 
 // @has foo/type.Golf.html '//pre[@class="rust typedef"]' \
-//          "type Golf<T> where T: Clone, = (T, T)"
+//          "type Golf<T>where T: Clone, = (T, T)"
 pub type Golf<T> where T: Clone = (T, T);
index 33192433bbf38ca80e6d6b5a427f286cd77c7589..0e449256153a2224949094a65297991e4b39938d 100644 (file)
@@ -1,6 +1,7 @@
 // compile-flags: -Z unstable-options
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 #![feature(rustc_private)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
@@ -71,3 +72,10 @@ pub fn make_diagnostics<'a>(sess: &'a ParseSess) {
     //~^ ERROR diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
     //~^^ ERROR diagnostics should be created using translatable messages
 }
+
+// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted.
+
+#[rustc_lint_diagnostics]
+pub fn skipped_because_of_annotation<'a>(sess: &'a ParseSess) {
+    let _diag = sess.struct_err("untranslatable diagnostic"); // okay!
+}
index bae78ffdc021b5fff868b5189e422d60a77332d9..ed5105dabcd3ff8d686257031a5d6106df10d4b5 100644 (file)
@@ -1,41 +1,41 @@
 error: diagnostics should be created using translatable messages
-  --> $DIR/diagnostics.rs:36:14
+  --> $DIR/diagnostics.rs:37:14
    |
 LL |         sess.struct_err("untranslatable diagnostic")
    |              ^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/diagnostics.rs:5:9
+  --> $DIR/diagnostics.rs:6:9
    |
 LL | #![deny(rustc::untranslatable_diagnostic)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostics should be created using translatable messages
-  --> $DIR/diagnostics.rs:53:14
+  --> $DIR/diagnostics.rs:54:14
    |
 LL |         diag.note("untranslatable diagnostic");
    |              ^^^^
 
 error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
-  --> $DIR/diagnostics.rs:67:22
+  --> $DIR/diagnostics.rs:68:22
    |
 LL |     let _diag = sess.struct_err(fluent::parser::expect_path);
    |                      ^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/diagnostics.rs:6:9
+  --> $DIR/diagnostics.rs:7:9
    |
 LL | #![deny(rustc::diagnostic_outside_of_impl)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
-  --> $DIR/diagnostics.rs:70:22
+  --> $DIR/diagnostics.rs:71:22
    |
 LL |     let _diag = sess.struct_err("untranslatable diagnostic");
    |                      ^^^^^^^^^^
 
 error: diagnostics should be created using translatable messages
-  --> $DIR/diagnostics.rs:70:22
+  --> $DIR/diagnostics.rs:71:22
    |
 LL |     let _diag = sess.struct_err("untranslatable diagnostic");
    |                      ^^^^^^^^^^
index b1f557cb94de8538e58ef71051a7628f05fbf842..c1c109ac1eade556f168b1c2000ef8ff04d624e2 100644 (file)
@@ -2,7 +2,7 @@
 // Tests error conditions for specifying diagnostics using #[derive(SessionDiagnostic)]
 
 // normalize-stderr-test "the following other types implement trait `IntoDiagnosticArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr"
-
+// normalize-stderr-test "diagnostic_builder\.rs:[0-9]+:[0-9]+" -> "diagnostic_builder.rs:LL:CC"
 // The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly,
 // changing the output of this test. Since SessionDiagnostic is strictly internal to the compiler
 // the test is just ignored on stable and beta:
index 621c59f448951ed0d972684a86272be7438063bf..ab5c28fe473326aa02b4a5a42cd62e32a4dc57e5 100644 (file)
@@ -453,7 +453,7 @@ LL | #[derive(SessionDiagnostic)]
    |
    = help: normalized in stderr
 note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
-  --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:569:19
+  --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC
    |
 LL |         arg: impl IntoDiagnosticArg,
    |                   ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
index 89eaec78c6f1169b041f56958b2f61091b5212f8..f0843c60543df4203c9a15cb6a8560196567aa82 100644 (file)
@@ -167,8 +167,8 @@ enum P {
 #[derive(SessionSubdiagnostic)]
 enum Q {
     #[bar]
-    //~^ ERROR `#[bar]` is not a valid attribute
-    //~^^ ERROR cannot find attribute `bar` in this scope
+//~^ ERROR `#[bar]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
     A {
         #[primary_span]
         span: Span,
@@ -179,8 +179,8 @@ enum Q {
 #[derive(SessionSubdiagnostic)]
 enum R {
     #[bar = "..."]
-    //~^ ERROR `#[bar = ...]` is not a valid attribute
-    //~^^ ERROR cannot find attribute `bar` in this scope
+//~^ ERROR `#[bar = ...]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
     A {
         #[primary_span]
         span: Span,
@@ -191,8 +191,8 @@ enum R {
 #[derive(SessionSubdiagnostic)]
 enum S {
     #[bar = 4]
-    //~^ ERROR `#[bar = ...]` is not a valid attribute
-    //~^^ ERROR cannot find attribute `bar` in this scope
+//~^ ERROR `#[bar = ...]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
     A {
         #[primary_span]
         span: Span,
@@ -203,8 +203,8 @@ enum S {
 #[derive(SessionSubdiagnostic)]
 enum T {
     #[bar("...")]
-    //~^ ERROR `#[bar(...)]` is not a valid attribute
-    //~^^ ERROR cannot find attribute `bar` in this scope
+//~^ ERROR `#[bar("...")]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
     A {
         #[primary_span]
         span: Span,
@@ -215,7 +215,7 @@ enum T {
 #[derive(SessionSubdiagnostic)]
 enum U {
     #[label(code = "...")]
-    //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
+//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
     A {
         #[primary_span]
         span: Span,
@@ -232,7 +232,7 @@ enum V {
         var: String,
     },
     B {
-    //~^ ERROR subdiagnostic kind not specified
+//~^ ERROR subdiagnostic kind not specified
         #[primary_span]
         span: Span,
         var: String,
@@ -307,16 +307,6 @@ union AC {
     b: u64
 }
 
-#[derive(SessionSubdiagnostic)]
-#[label(parser::add_paren)]
-//~^ NOTE previously specified here
-#[label(parser::add_paren)]
-//~^ ERROR specified multiple times
-struct AD {
-    #[primary_span]
-    span: Span,
-}
-
 #[derive(SessionSubdiagnostic)]
 #[label(parser::add_paren, parser::add_paren)]
 //~^ ERROR `#[label(parser::add_paren)]` is not a valid attribute
@@ -329,16 +319,16 @@ struct AE {
 #[label(parser::add_paren)]
 struct AF {
     #[primary_span]
-    //~^ NOTE previously specified here
+//~^ NOTE previously specified here
     span_a: Span,
     #[primary_span]
-    //~^ ERROR specified multiple times
+//~^ ERROR specified multiple times
     span_b: Span,
 }
 
 #[derive(SessionSubdiagnostic)]
 struct AG {
-    //~^ ERROR subdiagnostic kind not specified
+//~^ ERROR subdiagnostic kind not specified
     #[primary_span]
     span: Span,
 }
@@ -390,25 +380,27 @@ struct AK {
     #[primary_span]
     span: Span,
     #[applicability]
-    //~^ NOTE previously specified here
+//~^ NOTE previously specified here
     applicability_a: Applicability,
     #[applicability]
-    //~^ ERROR specified multiple times
+//~^ ERROR specified multiple times
     applicability_b: Applicability,
 }
 
 #[derive(SessionSubdiagnostic)]
 #[suggestion(parser::add_paren, code = "...")]
+//~^ ERROR suggestion without `applicability`
 struct AL {
     #[primary_span]
     span: Span,
     #[applicability]
-    //~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
+//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
     applicability: Span,
 }
 
 #[derive(SessionSubdiagnostic)]
 #[suggestion(parser::add_paren, code = "...")]
+//~^ ERROR suggestion without `applicability`
 struct AM {
     #[primary_span]
     span: Span,
@@ -444,7 +436,8 @@ struct AP {
 
 #[derive(SessionSubdiagnostic)]
 #[suggestion(parser::add_paren, code = "...")]
-//~^ ERROR suggestion without `#[primary_span]` field
+//~^ ERROR suggestion without `applicability`
+//~^^ ERROR suggestion without `#[primary_span]` field
 struct AR {
     var: String,
 }
@@ -514,120 +507,3 @@ struct AZ {
     #[primary_span]
     span: Span,
 }
-
-#[derive(SessionSubdiagnostic)]
-#[suggestion(parser::add_paren, code = "...")]
-//~^ ERROR suggestion without `#[primary_span]` field
-struct BA {
-    #[suggestion_part]
-    //~^ ERROR `#[suggestion_part]` is not a valid attribute
-    span: Span,
-    #[suggestion_part(code = "...")]
-    //~^ ERROR `#[suggestion_part(...)]` is not a valid attribute
-    span2: Span,
-    #[applicability]
-    applicability: Applicability,
-    var: String,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
-//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
-//~| ERROR `code` is not a valid nested attribute of a `multipart_suggestion` attribute
-struct BBa {
-    var: String,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
-struct BBb {
-    #[suggestion_part]
-    //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
-    span1: Span,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
-struct BBc {
-    #[suggestion_part()]
-    //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
-    span1: Span,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren)]
-//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
-struct BC {
-    #[primary_span]
-    //~^ ERROR `#[primary_span]` is not a valid attribute
-    span: Span,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren)]
-struct BD {
-    #[suggestion_part]
-    //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
-    span1: Span,
-    #[suggestion_part()]
-    //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
-    span2: Span,
-    #[suggestion_part(foo = "bar")]
-    //~^ ERROR `#[suggestion_part(foo = ...)]` is not a valid attribute
-    span4: Span,
-    #[suggestion_part(code = "...")]
-    //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
-    s1: String,
-    #[suggestion_part()]
-    //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
-    s2: String,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
-struct BE {
-    #[suggestion_part(code = "...", code = ",,,")]
-    //~^ ERROR specified multiple times
-    //~| NOTE previously specified here
-    span: Span,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
-struct BF {
-    #[suggestion_part(code = "(")]
-    first: Span,
-    #[suggestion_part(code = ")")]
-    second: Span,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren)]
-struct BG {
-    #[applicability]
-    appl: Applicability,
-    #[suggestion_part(code = "(")]
-    first: Span,
-    #[suggestion_part(code = ")")]
-    second: Span,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
-//~^ NOTE previously specified here
-struct BH {
-    #[applicability]
-    //~^ ERROR specified multiple times
-    appl: Applicability,
-    #[suggestion_part(code = "(")]
-    first: Span,
-    #[suggestion_part(code = ")")]
-    second: Span,
-}
-
-#[derive(SessionSubdiagnostic)]
-#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
-struct BI {
-    #[suggestion_part(code = "")]
-    spans: Vec<Span>,
-}
index 75a34f44bbe7241bb61e0a293eac4ef41ae06c60..6bd9144dbf6f0b8e8be1ca2dfb273f4cb1b17cb8 100644 (file)
@@ -65,16 +65,16 @@ LL | #[label()]
    | ^^^^^^^^^^
 
 error: `code` is not a valid nested attribute of a `label` attribute
-  --> $DIR/subdiagnostic-derive.rs:137:28
+  --> $DIR/subdiagnostic-derive.rs:137:1
    |
 LL | #[label(parser::add_paren, code = "...")]
-   |                            ^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `applicability` is not a valid nested attribute of a `label` attribute
-  --> $DIR/subdiagnostic-derive.rs:146:28
+  --> $DIR/subdiagnostic-derive.rs:146:1
    |
 LL | #[label(parser::add_paren, applicability = "machine-applicable")]
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unsupported type attribute for subdiagnostic enum
   --> $DIR/subdiagnostic-derive.rs:155:1
@@ -100,11 +100,13 @@ error: `#[bar = ...]` is not a valid attribute
 LL |     #[bar = 4]
    |     ^^^^^^^^^^
 
-error: `#[bar(...)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:205:5
+error: `#[bar("...")]` is not a valid attribute
+  --> $DIR/subdiagnostic-derive.rs:205:11
    |
 LL |     #[bar("...")]
-   |     ^^^^^^^^^^^^^
+   |           ^^^^^
+   |
+   = help: first argument of the attribute should be the diagnostic slug
 
 error: diagnostic slug must be first argument of a `#[label(...)]` attribute
   --> $DIR/subdiagnostic-derive.rs:217:5
@@ -161,8 +163,6 @@ error: `#[bar(...)]` is not a valid attribute
    |
 LL |     #[bar("...")]
    |     ^^^^^^^^^^^^^
-   |
-   = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
 
 error: unexpected unsupported untagged union
   --> $DIR/subdiagnostic-derive.rs:304:1
@@ -174,20 +174,8 @@ LL | |     b: u64
 LL | | }
    | |_^
 
-error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:313:1
-   |
-LL | #[label(parser::add_paren)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:311:1
-   |
-LL | #[label(parser::add_paren)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: `#[label(parser::add_paren)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:321:28
+  --> $DIR/subdiagnostic-derive.rs:311:28
    |
 LL | #[label(parser::add_paren, parser::add_paren)]
    |                            ^^^^^^^^^^^^^^^^^
@@ -195,225 +183,133 @@ LL | #[label(parser::add_paren, parser::add_paren)]
    = help: a diagnostic slug must be the first argument to the attribute
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:334:5
+  --> $DIR/subdiagnostic-derive.rs:324:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:331:5
+  --> $DIR/subdiagnostic-derive.rs:321:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
 
 error: subdiagnostic kind not specified
-  --> $DIR/subdiagnostic-derive.rs:340:8
+  --> $DIR/subdiagnostic-derive.rs:330:8
    |
 LL | struct AG {
    |        ^^
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:377:47
+  --> $DIR/subdiagnostic-derive.rs:367:47
    |
 LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
    |                                               ^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:377:33
+  --> $DIR/subdiagnostic-derive.rs:367:33
    |
 LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
    |                                 ^^^^^^^^^^^^
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:395:5
+  --> $DIR/subdiagnostic-derive.rs:385:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:392:5
+  --> $DIR/subdiagnostic-derive.rs:382:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
 error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
-  --> $DIR/subdiagnostic-derive.rs:405:5
+  --> $DIR/subdiagnostic-derive.rs:396:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
-error: suggestion without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:418:1
-   |
-LL | #[suggestion(parser::add_paren)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: invalid applicability
-  --> $DIR/subdiagnostic-derive.rs:428:46
-   |
-LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
-   |                                              ^^^^^^^^^^^^^^^^^^^^^
-
-error: suggestion without `#[primary_span]` field
-  --> $DIR/subdiagnostic-derive.rs:446:1
+error: suggestion without `applicability`
+  --> $DIR/subdiagnostic-derive.rs:391:1
    |
 LL | / #[suggestion(parser::add_paren, code = "...")]
 LL | |
-LL | | struct AR {
-LL | |     var: String,
+LL | | struct AL {
+LL | |     #[primary_span]
+...  |
+LL | |     applicability: Span,
 LL | | }
    | |_^
 
-error: unsupported type attribute for subdiagnostic enum
-  --> $DIR/subdiagnostic-derive.rs:460:1
-   |
-LL | #[label]
-   | ^^^^^^^^
-
-error: `var` doesn't refer to a field on this type
-  --> $DIR/subdiagnostic-derive.rs:480:39
-   |
-LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
-   |                                       ^^^^^^^
-
-error: `var` doesn't refer to a field on this type
-  --> $DIR/subdiagnostic-derive.rs:499:43
-   |
-LL |     #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
-   |                                           ^^^^^^^
-
-error: `#[suggestion_part]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:522:5
-   |
-LL |     #[suggestion_part]
-   |     ^^^^^^^^^^^^^^^^^^
-   |
-   = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead
-
-error: `#[suggestion_part(...)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:525:5
+error: suggestion without `applicability`
+  --> $DIR/subdiagnostic-derive.rs:402:1
    |
-LL |     #[suggestion_part(code = "...")]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: `#[suggestion_part(...)]` is only valid in multipart suggestions
+LL | / #[suggestion(parser::add_paren, code = "...")]
+LL | |
+LL | | struct AM {
+LL | |     #[primary_span]
+LL | |     span: Span,
+LL | | }
+   | |_^
 
-error: suggestion without `#[primary_span]` field
-  --> $DIR/subdiagnostic-derive.rs:519:1
+error: suggestion without `code = "..."`
+  --> $DIR/subdiagnostic-derive.rs:410:1
    |
-LL | / #[suggestion(parser::add_paren, code = "...")]
+LL | / #[suggestion(parser::add_paren)]
 LL | |
-LL | | struct BA {
-LL | |     #[suggestion_part]
+LL | | struct AN {
+LL | |     #[primary_span]
 ...  |
-LL | |     var: String,
+LL | |     applicability: Applicability,
 LL | | }
    | |_^
 
-error: `code` is not a valid nested attribute of a `multipart_suggestion` attribute
-  --> $DIR/subdiagnostic-derive.rs:534:43
+error: invalid applicability
+  --> $DIR/subdiagnostic-derive.rs:420:46
    |
-LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
-   |                                           ^^^^^^^^^^^^
+LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
+   |                                              ^^^^^^^^^^^^^^^^^^^^^
 
-error: multipart suggestion without any `#[suggestion_part(...)]` fields
-  --> $DIR/subdiagnostic-derive.rs:534:1
+error: suggestion without `applicability`
+  --> $DIR/subdiagnostic-derive.rs:438:1
    |
-LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
+LL | / #[suggestion(parser::add_paren, code = "...")]
 LL | |
 LL | |
-LL | | struct BBa {
+LL | | struct AR {
 LL | |     var: String,
 LL | | }
    | |_^
 
-error: `#[suggestion_part(...)]` attribute without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:544:5
-   |
-LL |     #[suggestion_part]
-   |     ^^^^^^^^^^^^^^^^^^
-
-error: `#[suggestion_part(...)]` attribute without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:552:5
-   |
-LL |     #[suggestion_part()]
-   |     ^^^^^^^^^^^^^^^^^^^^
-
-error: `#[primary_span]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:561:5
-   |
-LL |     #[primary_span]
-   |     ^^^^^^^^^^^^^^^
-   |
-   = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]`
-
-error: multipart suggestion without any `#[suggestion_part(...)]` fields
-  --> $DIR/subdiagnostic-derive.rs:558:1
+error: suggestion without `#[primary_span]` field
+  --> $DIR/subdiagnostic-derive.rs:438:1
    |
-LL | / #[multipart_suggestion(parser::add_paren)]
+LL | / #[suggestion(parser::add_paren, code = "...")]
 LL | |
-LL | | struct BC {
-LL | |     #[primary_span]
 LL | |
-LL | |     span: Span,
+LL | | struct AR {
+LL | |     var: String,
 LL | | }
    | |_^
 
-error: `#[suggestion_part(...)]` attribute without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:569:5
-   |
-LL |     #[suggestion_part]
-   |     ^^^^^^^^^^^^^^^^^^
-
-error: `#[suggestion_part(...)]` attribute without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:572:5
-   |
-LL |     #[suggestion_part()]
-   |     ^^^^^^^^^^^^^^^^^^^^
-
-error: `#[suggestion_part(foo = ...)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:575:23
-   |
-LL |     #[suggestion_part(foo = "bar")]
-   |                       ^^^^^^^^^^^
-   |
-   = help: `code` is the only valid nested attribute
-
-error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
-  --> $DIR/subdiagnostic-derive.rs:578:5
-   |
-LL |     #[suggestion_part(code = "...")]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
-  --> $DIR/subdiagnostic-derive.rs:581:5
+error: unsupported type attribute for subdiagnostic enum
+  --> $DIR/subdiagnostic-derive.rs:453:1
    |
-LL |     #[suggestion_part()]
-   |     ^^^^^^^^^^^^^^^^^^^^
+LL | #[label]
+   | ^^^^^^^^
 
-error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:589:37
-   |
-LL |     #[suggestion_part(code = "...", code = ",,,")]
-   |                                     ^^^^^^^^^^^^
-   |
-note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:589:23
+error: `var` doesn't refer to a field on this type
+  --> $DIR/subdiagnostic-derive.rs:473:39
    |
-LL |     #[suggestion_part(code = "...", code = ",,,")]
-   |                       ^^^^^^^^^^^^
+LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
+   |                                       ^^^^^^^
 
-error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:619:5
-   |
-LL |     #[applicability]
-   |     ^^^^^^^^^^^^^^^^
-   |
-note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:616:43
+error: `var` doesn't refer to a field on this type
+  --> $DIR/subdiagnostic-derive.rs:492:43
    |
-LL | #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
-   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
+   |                                           ^^^^^^^
 
 error: cannot find attribute `foo` in this scope
   --> $DIR/subdiagnostic-derive.rs:63:3
@@ -475,6 +371,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
 LL | #[label(slug)]
    |         ^^^^ not found in `rustc_errors::fluent`
 
-error: aborting due to 64 previous errors
+error: aborting due to 50 previous errors
 
 For more information about this error, try `rustc --explain E0425`.
index 54b7c8bb9c6f81af6fa62276202c17c1c53e73de..851da231a73441708317aabc4c681f92e967f2dc 100644 (file)
@@ -86,7 +86,7 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
 
 // Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed.
 // However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions
-// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
+// in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
 // unwind. So, for this test case we will define the symbol.
 #[lang = "eh_personality"]
 extern fn rust_eh_personality() {}
index ffa331a992c8b42265769f2763721fd1a9c498ff..30ce0f162c7ac55d21b1e362078abbee6f8c22dd 100644 (file)
@@ -73,7 +73,7 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
 
 // Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed.
 // However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions
-// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
+// in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
 // unwind. So, for this test case we will define the symbol.
 #[lang = "eh_personality"]
 extern fn rust_eh_personality() {}
index 4a533939931b2c7c7d4fc0ad951ccab0e2dedcb2..d00def571666bdaf38857f9dea57b551b6dcb8fd 100644 (file)
@@ -1,7 +1,7 @@
 #![feature(associated_type_bounds)]
 #![feature(anonymous_lifetime_in_impl_trait)]
 
-// The same thing should happen for constaints in dyn trait.
+// The same thing should happen for constraints in dyn trait.
 fn f(x: &mut dyn Iterator<Item: Iterator<Item = &'_ ()>>) -> Option<&'_ ()> { x.next() }
 //~^ ERROR missing lifetime specifier
 //~| ERROR mismatched types
diff --git a/src/test/ui/borrowck/do-not-suggest-adding-move-when-closure-is-already-marked-as-move.rs b/src/test/ui/borrowck/do-not-suggest-adding-move-when-closure-is-already-marked-as-move.rs
new file mode 100644 (file)
index 0000000..5244592
--- /dev/null
@@ -0,0 +1,8 @@
+fn main() {
+    let mut vec: Vec<i32> = Vec::new();
+    let closure = move || {
+        vec.clear();
+        let mut iter = vec.iter();
+        move || { iter.next() } //~ ERROR captured variable cannot escape `FnMut` closure bod
+    };
+}
diff --git a/src/test/ui/borrowck/do-not-suggest-adding-move-when-closure-is-already-marked-as-move.stderr b/src/test/ui/borrowck/do-not-suggest-adding-move-when-closure-is-already-marked-as-move.stderr
new file mode 100644 (file)
index 0000000..78ca090
--- /dev/null
@@ -0,0 +1,18 @@
+error: captured variable cannot escape `FnMut` closure body
+  --> $DIR/do-not-suggest-adding-move-when-closure-is-already-marked-as-move.rs:6:9
+   |
+LL |     let mut vec: Vec<i32> = Vec::new();
+   |         ------- variable defined here
+LL |     let closure = move || {
+   |                         - inferred to be a `FnMut` closure
+LL |         vec.clear();
+   |         --- variable captured here
+LL |         let mut iter = vec.iter();
+LL |         move || { iter.next() }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ returns a closure that contains a reference to a captured variable, which then escapes the closure body
+   |
+   = note: `FnMut` closures only have access to their captured variables while they are executing...
+   = note: ...therefore, they cannot allow references to captured variables to escape
+
+error: aborting due to previous error
+
index 3d395d1f264a0d8407b3c17ca2e3cc08188ba12b..da238205b402f1562159a05fc91c7d64dd062e86 100644 (file)
@@ -131,13 +131,13 @@ fn coerce_index_op() {
     let mut i = I(10);
     i[i[3]] = 4;
     //~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
-    // Shoud be accepted with g2p
+    // Should be accepted with g2p
 
     i[3] = i[4];
 
     i[i[3]] = i[4];
     //~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
-    // Shoud be accepted with g2p
+    // Should be accepted with g2p
 }
 
 fn main() {
index e0f4afa75276dbc3d98549ec145eb7735d98467d..0463e22b3c2d16bc8555d1d18d1d51481d0c5261 100644 (file)
@@ -40,6 +40,6 @@ fn main() {
     //
     // (At least in theory; part of the reason this test fails is that
     // the constructed MIR throws in extra &mut reborrows which
-    // flummoxes our attmpt to delay the activation point here.)
+    // flummoxes our attempt to delay the activation point here.)
     delay.push(2);
 }
index bdfbc3d54a2c58ec3d8af494a8d5751132cabcc9..8fadcc1f9f06c2261e590c528922e5d8cc482e61 100644 (file)
@@ -1,2 +1,2 @@
-error: invalid `--check-cfg` argument: `names("NOT_IDENT")` (`names()` arguments must be simple identifers)
+error: invalid `--check-cfg` argument: `names("NOT_IDENT")` (`names()` arguments must be simple identifiers)
 
index b25882baaf3e634d135217138a34a074b588297f..061d3f0e971c32e724fbee8cfb02b6fe9f3f18b8 100644 (file)
@@ -1,2 +1,2 @@
-error: invalid `--check-cfg` argument: `values("NOT_IDENT")` (`values()` first argument must be a simple identifer)
+error: invalid `--check-cfg` argument: `values("NOT_IDENT")` (`values()` first argument must be a simple identifier)
 
index 93131b2ac4e4d47999a4f761a9953b9cfc64c9e0..f97e60daf43a88afbefcbd5046ff5a3725666cdb 100644 (file)
@@ -68,7 +68,7 @@ fn arrays_5() {
         arr[1] += 10;
     };
 
-    // c will capture `arr` completely, therefore we cannot borrow other indecies
+    // c will capture `arr` completely, therefore we cannot borrow other indices
     // into the array.
     println!("{:#?}", &arr[3..2]);
     //~^ ERROR: cannot borrow `arr` as immutable because it is also borrowed as mutable
index 3ed780f51c73b6b6f4a54130faccad04c9afbdfc..f23670f63acb7d0f5f63a68797a6d8c0192bf94a 100644 (file)
@@ -31,7 +31,7 @@ struct Foo { x: u8, y: u8 }
     c();
 }
 
-// `String`, `u16` are not aligned at a one byte boundry and are thus affected by repr(packed).
+// `String`, `u16` are not aligned at a one byte boundary and are thus affected by repr(packed).
 //
 // Here we test that the closure doesn't capture a reference point to `foo.x` but
 // rather capture `foo` entirely.
index 2c828aed528bfcb98c09731835fb5c1dc968a091..f8752fe1cec0407a18b7021cbcaa9087f96fc523 100644 (file)
@@ -1,8 +1,8 @@
 // edition:2021
 // run-pass
 
-// Test that ByValue captures compile sucessefully especially when the captures are
-// derefenced within the closure.
+// Test that ByValue captures compile successfully especially when the captures are
+// dereferenced within the closure.
 
 #[derive(Debug, Default)]
 struct SomeLargeType;
index 88a9816a05263b4024f4f18b299c2fb52da371aa..03400e0ee8d56fdd8bacc323eca72dbb49c25a31 100644 (file)
@@ -1,7 +1,7 @@
 // edition:2021
 // run-pass
 
-// Tests that if a closure uses indivual fields of the same object
+// Tests that if a closure uses individual fields of the same object
 // then that case is handled properly.
 
 #![allow(unused)]
index b8e464031813bd42a71e8966570c7fe8e96827ab..624e0ff22568d73227054e1605896a23014ff689 100644 (file)
@@ -1,10 +1,10 @@
 // edition:2021
 // run-pass
 
-// Test that closures can catpure paths that are more precise than just one level
+// Test that closures can capture paths that are more precise than just one level
 // from the root variable.
 //
-// If the closures can handle such precison we should be able to mutate one path in the closure
+// If the closures can handle such precision we should be able to mutate one path in the closure
 // while being able to mutate another path outside the closure, where the two paths are disjoint
 // after applying two projections on the root variable.
 
index 11a324d8a34e361c7da8db413e55b22ba87ad153..bd8addd37812a10edab027e12855701a8e98dd2d 100644 (file)
@@ -3,7 +3,7 @@
 
 #![allow(unused)]
 
-// If the closures can handle such precison we should be able to read one path in the closure
+// If the closures can handle such precision we should be able to read one path in the closure
 // while being able mutate another path outside the closure, where the two paths are disjoint
 // after applying two projections on the root variable.
 
index bb784774b8cd439180a05d2a6e12e15db57b6982..a85335438a9fbd301c40d0aa003ed7e499028017 100644 (file)
@@ -5,7 +5,7 @@
 // that is captured by the closure
 
 // More specifically we test that the if the mutable reference isn't root variable of a capture
-// but rather accessed while acessing the precise capture.
+// but rather accessed while accessing the precise capture.
 
 fn mut_tuple() {
     let mut t = (10, 10);
index f81d7cfaa654bc9efe1976840968d2a86e8786fe..30733871b855f0fadf41a883c777fa476f208a30 100644 (file)
@@ -1,5 +1,5 @@
 // test for issue 84128
-// missing suggestion for similar ADT type with diffetent generic paramenter
+// missing suggestion for similar ADT type with diffetent generic parameter
 // on closure ReturnNoExpression
 
 struct Foo<T>(T);
index 91d8e4f381a368af580596eacf6a6bd236212725..69a0b486d689f8585b3dfd953e1f07bab7b56fc4 100644 (file)
@@ -18,7 +18,7 @@ fn main() {
     let exe = me.file_name().unwrap();
     let cwd = me.parent().unwrap();
     eprintln!("cwd={:?}", cwd);
-    // Change directory to where the exectuable is located, since this test
+    // Change directory to where the executable is located, since this test
     // fundamentally needs to use relative paths. In some cases (like
     // remote-test-server), the current_dir can be somewhere else, so make
     // sure it is something we can use. We assume we can write to this
index b1b359619dcfabb83144f4b2f563f4bef0f6d7b6..d955b4f9651daa4906530e94bb19a0278ac331af 100644 (file)
@@ -2,15 +2,22 @@ error[E0107]: this associated function takes 0 generic arguments but 1 generic a
   --> $DIR/invalid-const-arg-for-type-param.rs:6:23
    |
 LL |     let _: u32 = 5i32.try_into::<32>().unwrap();
-   |                       ^^^^^^^^------ help: remove these generics
-   |                       |
-   |                       expected 0 generic arguments
+   |                       ^^^^^^^^ expected 0 generic arguments
    |
 note: associated function defined here, with 0 generic parameters
   --> $SRC_DIR/core/src/convert/mod.rs:LL:COL
    |
 LL |     fn try_into(self) -> Result<T, Self::Error>;
    |        ^^^^^^^^
+help: consider moving this generic argument to the `TryInto` trait, which takes up to 1 argument
+   |
+LL |     let _: u32 = TryInto::<32>::try_into(5i32).unwrap();
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: remove these generics
+   |
+LL -     let _: u32 = 5i32.try_into::<32>().unwrap();
+LL +     let _: u32 = 5i32.try_into().unwrap();
+   |
 
 error[E0599]: no method named `f` found for struct `S` in the current scope
   --> $DIR/invalid-const-arg-for-type-param.rs:9:7
index c488a663fbb002167bc811f735dc594ed21c798f..73c9301011d58693e52f12d50925c538d706d58f 100644 (file)
@@ -1,5 +1,5 @@
 // regression test for #83466- tests that generic arg mismatch errors between
-// consts and types are not supressed when there are explicit late bound lifetimes
+// consts and types are not suppressed when there are explicit late bound lifetimes
 
 struct S;
 impl S {
index 0e8744e790f6bdf95fb6cd99d31ac255b34a2d22..d8dc6d057a73d94675516cccc12ee55d876d3740 100644 (file)
@@ -93,7 +93,7 @@ enum UninhDiscriminant {
 //~^ ERROR is undefined behavior
 
 // All variants are uninhabited but also have data.
-// Use `0` as constant to make behavior endianess-independent.
+// Use `0` as constant to make behavior endianness-independent.
 const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
 //~^ ERROR evaluation of constant value failed
 const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) };
index 850d83be6845dc44b1e54c876cd8264d960a2d2c..f412ba84c6ba02cca01525873b048664ae5d7a55 100644 (file)
@@ -1,4 +1,4 @@
-// Test for diagnostics when we have mismatched lifetime due to implict 'static lifetime in GATs
+// Test for diagnostics when we have mismatched lifetime due to implicit 'static lifetime in GATs
 
 // check-fail
 
index 403cf970bcb0a52a49336c0ecf0dd2dcc2ff433a..cdb130d600c5ef2167ac509dff06e39965a91632 100644 (file)
@@ -97,7 +97,7 @@ pub fn main() {
     // ever hands f_A off to instances of GaspA, and thus one should
     // be able to prove the invariant that f_A is *only* invoked from
     // from an instance of GaspA (either via the GaspA drop
-    // implementation or the E drop implementaton). Yet the old (bad)
+    // implementation or the E drop implementation). Yet the old (bad)
     // behavior allowed a call to f_A to leak in while we are tearing
     // down a value of type GaspB.
 }
index ffbe89a14e3bbd4501f66355ca5a5c8b018d328c..6d12db8d13730a860153df747af5df481c2d3c9c 100644 (file)
@@ -1,6 +1,6 @@
 // run-pass
 //! This snippet causes the type length to blowup exponentially,
-//! so check that we don't accidentially exceed the type length limit.
+//! so check that we don't accidentally exceed the type length limit.
 // FIXME: Once the size of iterator adaptors is further reduced,
 // increase the complexity of this test.
 use std::collections::VecDeque;
index c66037e9a73ae6d01acbe5e466208575ff956df7..b4fc3317487d29c8a4844726c6d5479cfa62d52e 100644 (file)
@@ -5,7 +5,7 @@
 // If you turn off deduplicate diagnostics (which rustc turns on by default but
 // compiletest turns off when it runs ui tests), then the errors are
 // (unfortunately) repeated here because the checking is done as we read in the
-// errors, and curretly that happens two or three different times, depending on
+// errors, and currently that happens two or three different times, depending on
 // compiler flags.
 //
 // I decided avoiding the redundant output was not worth the time in engineering
diff --git a/src/test/ui/modules/auxiliary/dummy_lib.rs b/src/test/ui/modules/auxiliary/dummy_lib.rs
new file mode 100644 (file)
index 0000000..ef805c1
--- /dev/null
@@ -0,0 +1,2 @@
+#[allow(dead_code)]
+pub struct Dummy;
diff --git a/src/test/ui/modules/special_module_name.rs b/src/test/ui/modules/special_module_name.rs
new file mode 100644 (file)
index 0000000..15c59b2
--- /dev/null
@@ -0,0 +1,8 @@
+mod lib;
+//~^ WARN found module declaration for lib.rs
+//~| ERROR file not found for module `lib`
+mod main;
+//~^ WARN found module declaration for main.rs
+//~| ERROR file not found for module `main`
+
+fn main() {}
diff --git a/src/test/ui/modules/special_module_name.stderr b/src/test/ui/modules/special_module_name.stderr
new file mode 100644 (file)
index 0000000..8b3da29
--- /dev/null
@@ -0,0 +1,37 @@
+error[E0583]: file not found for module `lib`
+  --> $DIR/special_module_name.rs:1:1
+   |
+LL | mod lib;
+   | ^^^^^^^^
+   |
+   = help: to create the module `lib`, create file "$DIR/lib.rs" or "$DIR/lib/mod.rs"
+
+error[E0583]: file not found for module `main`
+  --> $DIR/special_module_name.rs:4:1
+   |
+LL | mod main;
+   | ^^^^^^^^^
+   |
+   = help: to create the module `main`, create file "$DIR/main.rs" or "$DIR/main/mod.rs"
+
+warning: found module declaration for lib.rs
+  --> $DIR/special_module_name.rs:1:1
+   |
+LL | mod lib;
+   | ^^^^^^^^
+   |
+   = note: `#[warn(special_module_name)]` on by default
+   = note: lib.rs is the root of this crate's library target
+   = help: to refer to it from other targets, use the library's name as the path
+
+warning: found module declaration for main.rs
+  --> $DIR/special_module_name.rs:4:1
+   |
+LL | mod main;
+   | ^^^^^^^^^
+   |
+   = note: a binary crate cannot be used as library
+
+error: aborting due to 2 previous errors; 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0583`.
diff --git a/src/test/ui/modules/special_module_name_ignore.rs b/src/test/ui/modules/special_module_name_ignore.rs
new file mode 100644 (file)
index 0000000..07cea9b
--- /dev/null
@@ -0,0 +1,9 @@
+// run-pass
+
+#[path = "auxiliary/dummy_lib.rs"]
+mod lib;
+
+#[path = "auxiliary/dummy_lib.rs"]
+mod main;
+
+fn main() {}
index 35052da6760b64ecfe443cbc67eed82304b30aba..f88355bb285f39a8e7eba473b62142fc53612578 100644 (file)
@@ -3,7 +3,7 @@
 //
 // This particular test case currently fails as the inference to `()` rather
 // than `!` happens as a result of an `as` cast, which is not currently tracked.
-// Crater did not find many cases of this occuring, but it is included for
+// Crater did not find many cases of this occurring, but it is included for
 // awareness.
 //
 // revisions: nofallback fallback
index 4cd1e406f94400dc4b81b6b56b01126f983eb4ac..ad3eb248351d4c18cefe473f2adff2137212bf48 100644 (file)
@@ -53,7 +53,7 @@ impl<F> R<F> { fn new(f: F) -> Self { R { w: 0, f } } }
 // * local/field: Is the structure in a local or a field
 // * fully/partial/void: Are we fully initializing it before using any part?
 //                       Is whole type empty due to a void component?
-// * init/reinit: First initialization, or did we previously inititalize and then move out?
+// * init/reinit: First initialization, or did we previously initialize and then move out?
 // * struct/tuple: Is this a struct or a (X, Y).
 //
 // As a shorthand for the cases above, adding a numeric summary to
index c4cf20389ac8d9e127c7b229bfb9267d889a326a..696bf61cefde04f818610fc389a9aca0dea6ef25 100644 (file)
@@ -1,7 +1,7 @@
 #![allow(dead_code)]
 
 // This tests the various kinds of assignments there are. Polonius used to generate `killed`
-// facts only on simple assigments, but not projections, incorrectly causing errors to be emitted
+// facts only on simple assignments, but not projections, incorrectly causing errors to be emitted
 // for code accepted by NLL. They are all variations from example code in the NLL RFC.
 
 // check-pass
index 84552f2e7331580a306292e947c733dadbf775b9..156285e0f9fc97356f5251ab7ff4254ce6dc4b73 100644 (file)
@@ -1,5 +1,5 @@
 // Here we test that rest patterns, i.e. `..`, are not allowed
-// outside of slice (+ ident patterns witin those), tuple,
+// outside of slice (+ ident patterns within those), tuple,
 // and tuple struct patterns and that duplicates are caught in these contexts.
 
 #![feature(box_patterns)]
index 8c1a9dc80265e17e87eb0b3be1f707ce43592649..6103acb7b6bd9ef82e9f3c7cc73f5605f434448a 100644 (file)
@@ -9,7 +9,7 @@
 
 // FIXME: This don't work when crate-type is specified by attribute
 // `#![crate_type = "proc-macro"]`, not by `--crate-type=proc-macro`
-// command line flag. This is beacuse the list of `cfg` symbols is generated
+// command line flag. This is because the list of `cfg` symbols is generated
 // before attributes are parsed. See rustc_interface::util::add_configuration
 #[cfg(target_feature = "crt-static")]
 compile_error!("crt-static is enabled");
index bdd1ae91f7d764e9ad8a894dc951be9035932470..a8f7a41c4428495bd821ae66fc46d8726c2b93fd 100644 (file)
@@ -5,7 +5,7 @@
 // by the function.
 //
 // This works today, which precludes changing things so that closures
-// follow the same lifetime-elision rules used elsehwere. See
+// follow the same lifetime-elision rules used elsewhere. See
 // rust-lang/rust#56537
 
 // check-pass
index 3c62782a89799c87d196784c13107c297d3fbbfd..5c2459a59036de4b2498813792a9bfc314b7a4e2 100644 (file)
@@ -22,6 +22,10 @@ enum D {
     Unit,
 }
 
+enum E {
+    TupleWithFields(()),
+}
+
 fn main() {
     // Only variants without fields are suggested (and others mentioned in a note) where an enum
     // is used rather than a variant.
@@ -34,6 +38,8 @@ fn main() {
     //~^ ERROR expected value, found enum `C`
     D.foo();
     //~^ ERROR expected value, found enum `D`
+    E.foo();
+    //~^ ERROR expected value, found enum `E`
 
     // Only tuple variants are suggested in calls or tuple struct pattern matching.
 
index 59bb98a340a5f894803cdf0d2fab79e10a48d67a..a2ca46f0ce964d1cdfbbc4edc4edda39432555fc 100644 (file)
@@ -1,5 +1,5 @@
 error[E0423]: expected value, found enum `A`
-  --> $DIR/issue-73427.rs:29:5
+  --> $DIR/issue-73427.rs:33:5
    |
 LL |     A.foo();
    |     ^
@@ -23,7 +23,7 @@ LL |     (A::Tuple()).foo();
    |     ~~~~~~~~~~~~
 LL |     A::Unit.foo();
    |     ~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
    |
 LL |     (A::StructWithFields { /* fields */ }).foo();
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -31,7 +31,7 @@ LL |     (A::TupleWithFields(/* fields */)).foo();
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0423]: expected value, found enum `B`
-  --> $DIR/issue-73427.rs:31:5
+  --> $DIR/issue-73427.rs:35:5
    |
 LL |     B.foo();
    |     ^
@@ -52,7 +52,7 @@ LL |     (B::TupleWithFields(/* fields */)).foo();
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0423]: expected value, found enum `C`
-  --> $DIR/issue-73427.rs:33:5
+  --> $DIR/issue-73427.rs:37:5
    |
 LL |     C.foo();
    |     ^
@@ -70,7 +70,7 @@ help: you might have meant to use the following enum variant
    |
 LL |     C::Unit.foo();
    |     ~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
    |
 LL |     (C::StructWithFields { /* fields */ }).foo();
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -78,7 +78,7 @@ LL |     (C::TupleWithFields(/* fields */)).foo();
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0423]: expected value, found enum `D`
-  --> $DIR/issue-73427.rs:35:5
+  --> $DIR/issue-73427.rs:39:5
    |
 LL |     D.foo();
    |     ^
@@ -95,13 +95,37 @@ help: you might have meant to use the following enum variant
    |
 LL |     D::Unit.foo();
    |     ~~~~~~~
-help: the following enum variant is available
+help: alternatively, the following enum variant is available
    |
 LL |     (D::TupleWithFields(/* fields */)).foo();
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+error[E0423]: expected value, found enum `E`
+  --> $DIR/issue-73427.rs:41:5
+   |
+LL |     E.foo();
+   |     ^
+   |
+note: the enum is defined here
+  --> $DIR/issue-73427.rs:25:1
+   |
+LL | / enum E {
+LL | |     TupleWithFields(()),
+LL | | }
+   | |_^
+help: the following enum variant is available
+   |
+LL |     (E::TupleWithFields(/* fields */)).foo();
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: consider importing one of these items instead
+   |
+LL | use std::f32::consts::E;
+   |
+LL | use std::f64::consts::E;
+   |
+
 error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
-  --> $DIR/issue-73427.rs:40:13
+  --> $DIR/issue-73427.rs:46:13
    |
 LL |     let x = A(3);
    |             ^
@@ -126,7 +150,7 @@ LL |     let x = A::TupleWithFields(3);
    |             ~~~~~~~~~~~~~~~~~~
 
 error[E0532]: expected tuple struct or tuple variant, found enum `A`
-  --> $DIR/issue-73427.rs:42:12
+  --> $DIR/issue-73427.rs:48:12
    |
 LL |     if let A(3) = x { }
    |            ^
@@ -150,7 +174,7 @@ LL |     if let A::Tuple(3) = x { }
 LL |     if let A::TupleWithFields(3) = x { }
    |            ~~~~~~~~~~~~~~~~~~
 
-error: aborting due to 6 previous errors
+error: aborting due to 7 previous errors
 
 Some errors have detailed explanations: E0423, E0532.
 For more information about an error, try `rustc --explain E0423`.
index 7cf32775a33ef3363aa23bcf53517798bd7be233..a369dc6db52812a9ad4cfa408ecb2548669974e1 100644 (file)
@@ -19,7 +19,7 @@ help: you might have meant to use the following enum variant
    |
 LL |         m::Z::Unit;
    |         ~~~~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
    |
 LL |         (m::Z::Fn(/* fields */));
    |         ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -47,7 +47,7 @@ help: you might have meant to use the following enum variant
    |
 LL |         m::Z::Unit;
    |         ~~~~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
    |
 LL |         (m::Z::Fn(/* fields */));
    |         ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -89,7 +89,7 @@ help: you might have meant to use the following enum variant
    |
 LL |     let _: E = E::Unit;
    |                ~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
    |
 LL |     let _: E = (E::Fn(/* fields */));
    |                ~~~~~~~~~~~~~~~~~~~~~
@@ -143,7 +143,7 @@ help: you might have meant to use the following enum variant
    |
 LL |     let _: E = E::Unit;
    |                ~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
    |
 LL |     let _: E = (E::Fn(/* fields */));
    |                ~~~~~~~~~~~~~~~~~~~~~
@@ -203,7 +203,7 @@ help: you might have meant to use the following enum variant
    |
 LL |     let _: Z = m::Z::Unit;
    |                ~~~~~~~~~~
-help: the following enum variants are available
+help: alternatively, the following enum variants are also available
    |
 LL |     let _: Z = (m::Z::Fn(/* fields */));
    |                ~~~~~~~~~~~~~~~~~~~~~~~~
index 4d19230df6badfacbed2d791e4fb94bb56fdf3c8..8dbadf58d5c888262bd183ea911f656e6142925f 100644 (file)
@@ -1,4 +1,4 @@
-// Test to ensure that trait bounds are propertly
+// Test to ensure that trait bounds are properly
 // checked on specializable associated types
 
 #![allow(incomplete_features)]
diff --git a/src/test/ui/suggestions/issue-89064.rs b/src/test/ui/suggestions/issue-89064.rs
new file mode 100644 (file)
index 0000000..fa5fc89
--- /dev/null
@@ -0,0 +1,35 @@
+use std::convert::TryInto;
+
+trait A<T> {
+    fn foo() {}
+}
+
+trait B<T, U> {
+    fn bar() {}
+}
+
+struct S;
+
+impl<T> A<T> for S {}
+impl<T, U> B<T, U> for S {}
+
+fn main() {
+    let _ = A::foo::<S>();
+    //~^ ERROR
+    //~| HELP remove these generics
+    //~| HELP consider moving this generic argument
+
+    let _ = B::bar::<S, S>();
+    //~^ ERROR
+    //~| HELP remove these generics
+    //~| HELP consider moving these generic arguments
+
+    let _ = A::<S>::foo::<S>();
+    //~^ ERROR
+    //~| HELP remove these generics
+
+    let _ = 42.into::<Option<_>>();
+    //~^ ERROR
+    //~| HELP remove these generics
+    //~| HELP consider moving this generic argument
+}
diff --git a/src/test/ui/suggestions/issue-89064.stderr b/src/test/ui/suggestions/issue-89064.stderr
new file mode 100644 (file)
index 0000000..8b2a388
--- /dev/null
@@ -0,0 +1,82 @@
+error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/issue-89064.rs:17:16
+   |
+LL |     let _ = A::foo::<S>();
+   |                ^^^ expected 0 generic arguments
+   |
+note: associated function defined here, with 0 generic parameters
+  --> $DIR/issue-89064.rs:4:8
+   |
+LL |     fn foo() {}
+   |        ^^^
+help: consider moving this generic argument to the `A` trait, which takes up to 1 argument
+   |
+LL -     let _ = A::foo::<S>();
+LL +     let _ = A::<S>::foo();
+   |
+help: remove these generics
+   |
+LL -     let _ = A::foo::<S>();
+LL +     let _ = A::foo();
+   |
+
+error[E0107]: this associated function takes 0 generic arguments but 2 generic arguments were supplied
+  --> $DIR/issue-89064.rs:22:16
+   |
+LL |     let _ = B::bar::<S, S>();
+   |                ^^^ expected 0 generic arguments
+   |
+note: associated function defined here, with 0 generic parameters
+  --> $DIR/issue-89064.rs:8:8
+   |
+LL |     fn bar() {}
+   |        ^^^
+help: consider moving these generic arguments to the `B` trait, which takes up to 2 arguments
+   |
+LL -     let _ = B::bar::<S, S>();
+LL +     let _ = B::<S, S>::bar();
+   |
+help: remove these generics
+   |
+LL -     let _ = B::bar::<S, S>();
+LL +     let _ = B::bar();
+   |
+
+error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/issue-89064.rs:27:21
+   |
+LL |     let _ = A::<S>::foo::<S>();
+   |                     ^^^----- help: remove these generics
+   |                     |
+   |                     expected 0 generic arguments
+   |
+note: associated function defined here, with 0 generic parameters
+  --> $DIR/issue-89064.rs:4:8
+   |
+LL |     fn foo() {}
+   |        ^^^
+
+error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/issue-89064.rs:31:16
+   |
+LL |     let _ = 42.into::<Option<_>>();
+   |                ^^^^ expected 0 generic arguments
+   |
+note: associated function defined here, with 0 generic parameters
+  --> $SRC_DIR/core/src/convert/mod.rs:LL:COL
+   |
+LL |     fn into(self) -> T;
+   |        ^^^^
+help: consider moving this generic argument to the `Into` trait, which takes up to 1 argument
+   |
+LL |     let _ = Into::<Option<_>>::into(42);
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: remove these generics
+   |
+LL -     let _ = 42.into::<Option<_>>();
+LL +     let _ = 42.into();
+   |
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0107`.
index 7cf536f7966e0c342df53f2671cf10ffd0103523..14fef1b524892d78877981ad584138923f6c1ae6 100644 (file)
@@ -1,5 +1,5 @@
 // Ensure that the compiler include the blanklet implementation suggestion
-// when inside a `impl` statment are used two local traits.
+// when inside a `impl` statement are used two local traits.
 //
 // edition:2021
 use std::fmt;
index 1314f9cb0932d6b98ef0842b0f950dc9bc324350..17ddaa312f7c440b1965df00f96e986e4eba9b6d 100644 (file)
@@ -1,5 +1,5 @@
 // This tests issue #79683: note in the error message that the trait is
-// explicitely unimplemented instead of suggesting to implement it.
+// explicitly unimplemented instead of suggesting to implement it.
 
 #![feature(negative_impls)]
 
index 65b79650d4d72495e6b7312fba7e03edb3d3000f..e49731725d51271c2f0bf24700985acc207fca69 100644 (file)
@@ -52,7 +52,7 @@ fn foo<'a>(y: &'a i32) {
 // Do this instead:
 type T4<U> = <U as Bound>::Assoc;
 
-// Make sure the help about associatd types is not shown incorrectly
+// Make sure the help about associated types is not shown incorrectly
 type T5<U: Bound> = <U as Bound>::Assoc;  //~ WARN not enforced in type aliases
 type T6<U: Bound> = ::std::vec::Vec<U>;  //~ WARN not enforced in type aliases
 
diff --git a/src/test/ui/unpretty/avoid-crash.rs b/src/test/ui/unpretty/avoid-crash.rs
new file mode 100644 (file)
index 0000000..fd84b70
--- /dev/null
@@ -0,0 +1,4 @@
+// normalize-stderr-test "error `.*`" -> "$$ERROR_MESSAGE"
+// compile-flags: -o/tmp/ -Zunpretty=ast-tree
+
+fn main() {}
diff --git a/src/test/ui/unpretty/avoid-crash.stderr b/src/test/ui/unpretty/avoid-crash.stderr
new file mode 100644 (file)
index 0000000..11cd386
--- /dev/null
@@ -0,0 +1,4 @@
+error: pretty-print failed to write `/tmp/` due to $ERROR_MESSAGE
+
+error: aborting due to previous error
+
index 7f59067483b9643499b0e1e86bf6886d03926cfc..40781fbf082b648047a6431cdf4d37dae84b4dcd 100644 (file)
@@ -1,4 +1,4 @@
-// Test various uses of structs with distint variances to make sure
+// Test various uses of structs with distinct variances to make sure
 // they permit lifetimes to be approximated as expected.
 
 struct SomeStruct<T>(fn(T));
index 2113eb2addb9bc492b3b2020796213c7470f1f89..d4b2d08342a9bd0f9c0d0770aec5ac31179300c6 100644 (file)
@@ -1,4 +1,4 @@
-// Test various uses of structs with distint variances to make sure
+// Test various uses of structs with distinct variances to make sure
 // they permit lifetimes to be approximated as expected.
 
 #![allow(dead_code)]
index d40dbceb5f8c41990f5a65052797ba0f1fdedba3..72f50f3459d1a47d8002a84043e6117f60b0d5ff 100644 (file)
@@ -1,4 +1,4 @@
-// Test various uses of structs with distint variances to make sure
+// Test various uses of structs with distinct variances to make sure
 // they permit lifetimes to be approximated as expected.
 
 struct SomeStruct<T>(*mut T);
index 0e27cc927acea4c010f810cd350f047eebece429..fac2c99714d9bf808d6a13f6bae1b6a82af59356 100644 (file)
@@ -24,6 +24,7 @@ env:
   RUST_BACKTRACE: 1
   CARGO_TARGET_DIR: '${{ github.workspace }}/target'
   NO_FMT_TEST: 1
+  CARGO_INCREMENTAL: 0
 
 jobs:
   base:
index 97453303cd6aae58c0959ede46d21704e254464e..30607af490124589f44ce0cf831ca57ab8edad98 100644 (file)
@@ -10,6 +10,7 @@ env:
   RUST_BACKTRACE: 1
   CARGO_TARGET_DIR: '${{ github.workspace }}/target'
   NO_FMT_TEST: 1
+  CARGO_INCREMENTAL: 0
 
 defaults:
   run:
index 380cd451987bfc982d00448668e1699fb999ffc9..c488c142e46fc2847fe41d7c3c3582508a705eac 100644 (file)
@@ -6,11 +6,157 @@ document.
 
 ## Unreleased / In Rust Nightly
 
-[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
+[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master)
+
+## Rust 1.63
+
+Current stable, released 2022-08-11
+
+[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
+
+### New Lints
+
+* [`borrow_deref_ref`]
+  [#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
+* [`doc_link_with_quotes`]
+  [#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
+* [`no_effect_replace`]
+  [#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
+* [`rc_clone_in_vec_init`]
+  [#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
+* [`derive_partial_eq_without_eq`]
+  [#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
+* [`mismatching_type_param_order`]
+  [#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
+* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
+* [`unused_rounding`]
+  [#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
+* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
+* [`swap_ptr_to_ref`]
+  [#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
+* [`almost_complete_letter_range`]
+  [#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
+* [`needless_parens_on_range_literals`]
+  [#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
+* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
+
+### Moves and Deprecations
+
+* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
+  `nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
+
+### Enhancements
+
+* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
+  [#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
+* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
+  [#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
+* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
+  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
+* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
+  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
+* [`disallowed_methods`]: Now also lints indirect usages
+  [#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
+* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
+  [#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
+* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
+  [#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
+* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
+  [#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
+* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
+  [#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
+* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
+  [#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
+* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
+  method chains inside `map`
+  [#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
+* [`needless_return`]: Now also lints on macro expressions in return statements
+  [#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
+* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
+  should extend the default and not replace it
+  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
+* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
+  config should extend the default and not replace it
+  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
+* [`never_loop`]: Now checks for `continue` in struct expression
+  [#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
+
+### False Positive Fixes
+
+* [`useless_transmute`]: No longer lints on types with erased regions
+  [#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
+* [`vec_init_then_push`]: No longer lints when further extended
+  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
+* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
+  [#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
+* [`redundant_allocation`]: No longer lints on fat pointers that would become
+  thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
+* [`derive_partial_eq_without_eq`]:
+    * Handle differing predicates applied by `#[derive(PartialEq)]` and
+      `#[derive(Eq)]`
+      [#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
+    * No longer lints on non-public types and better handles generics
+      [#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
+* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
+  string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
+* [`branches_sharing_code`]: No longer lints when using different binding names
+  [#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
+* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
+  desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
+* [`checked_conversions`]: No longer lints in `const` contexts
+  [#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
+* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
+  `T::Item` doesn't implement `IntoIterator`
+  [#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
+
+### Suggestion Fixes/Improvements
+
+* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
+  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
+* [`manual_range_contains`]: Fix suggestion for integers with different signs
+  [#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
+* [`identity_op`]: Add parenthesis to suggestions where required
+  [#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
+* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
+  [#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
+* [`rc_clone_in_vec_init`]: Add suggestion
+  [#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
+* The "unknown field" error messages for config files now wraps the field names
+  [#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
+* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
+  [#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
+* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
+  references and not trivially clone-able
+  [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
+* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
+  `iter_mut()` or `into_iter()`
+  [#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
+
+### ICE Fixes
+
+* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
+  [#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
+* Fix ICEs on callable `static`/`const`s
+  [#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
+* [`needless_late_init`]
+  [#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
+* Fix ICE in shadow lints
+  [#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
+
+### Documentation Improvements
+
+* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
+  [#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
+* Add a *copy lint name*-button to Clippy's lint list
+  [#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
+* Display past names of renamed lints on Clippy's lint list
+  [#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
+* Add the ability to show the lint output in the lint list
+  [#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
 
 ## Rust 1.62
 
-Current stable, released 2022-06-30
+Released 2022-06-30
 
 [d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
 
@@ -3481,6 +3627,7 @@ Released 2018-09-13
 [`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 [`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 [`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
+[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
 [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
@@ -3496,6 +3643,7 @@ Released 2018-09-13
 [`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
+[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
 [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
@@ -3656,6 +3804,8 @@ Released 2018-09-13
 [`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
 [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
+[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
+[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
 [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 [`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
@@ -3697,6 +3847,7 @@ Released 2018-09-13
 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 [`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
+[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
 [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 [`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
@@ -3747,6 +3898,7 @@ Released 2018-09-13
 [`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 [`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 [`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
+[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
 [`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
 [`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
 [`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
@@ -3827,6 +3979,7 @@ Released 2018-09-13
 [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
 [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
+[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
@@ -3872,6 +4025,7 @@ Released 2018-09-13
 [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 [`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
+[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
 [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
@@ -3930,6 +4084,7 @@ Released 2018-09-13
 [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
 [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
 [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
+[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
 [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
@@ -4002,6 +4157,7 @@ Released 2018-09-13
 [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
+[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
 [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
 [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
index f5c51b9474fcd878cf631441cc03a194d239301b..92b2771f3fe73aeccc27b51c1edd243108f0e35d 100644 (file)
@@ -37,7 +37,7 @@ fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
         return;
     }
 
-    let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
+    let test_output_file = fs::read(test_output_path).expect("Unable to read test output file");
     let reference_file = fs::read(&reference_file_path).unwrap_or_default();
 
     if test_output_file != reference_file {
index 3b27f061eb0b4f3fc57964044297bc26e8c3a413..357cf6fc43aadada9baabbce3ac08cd70c5d0f93 100644 (file)
@@ -46,7 +46,7 @@ fn try_run(context: &FmtContext) -> Result<bool, CliError> {
         // dependency
         if fs::read_to_string(project_root.join("Cargo.toml"))
             .expect("Failed to read clippy Cargo.toml")
-            .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
+            .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
         {
             return Err(CliError::IntellijSetupActive);
         }
@@ -193,10 +193,10 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
     let args = &["--version"];
 
     if context.verbose {
-        println!("{}", format_command(&program, &dir, args));
+        println!("{}", format_command(program, &dir, args));
     }
 
-    let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
+    let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?;
 
     if output.status.success() {
         Ok(())
@@ -207,7 +207,7 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
         Err(CliError::RustfmtNotInstalled)
     } else {
         Err(CliError::CommandFailed(
-            format_command(&program, &dir, args),
+            format_command(program, &dir, args),
             std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
         ))
     }
index 10a8f31f4573f790d63a4c786037abff9ce40004..be05e67d724dfc120acfec0d1385dda0cd3df741 100644 (file)
@@ -155,7 +155,7 @@ fn to_camel_case(name: &str) -> String {
     name.split('_')
         .map(|s| {
             if s.is_empty() {
-                String::from("")
+                String::new()
             } else {
                 [&s[0..1].to_uppercase(), &s[1..]].concat()
             }
index 05e79a241884f43bb9175afd3b01b483e6596959..c503142e5e4552b1cdc28877dca9ab3734ba2083 100644 (file)
@@ -418,7 +418,7 @@ fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
             .expect("failed to find `impl_lint_pass` terminator");
 
         impl_lint_pass_end += impl_lint_pass_start;
-        if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) {
+        if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
             let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
             for c in content[lint_name_end..impl_lint_pass_end].chars() {
                 // Remove trailing whitespace
@@ -451,7 +451,7 @@ fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
                 }
 
                 let mut content =
-                    fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
+                    fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
 
                 eprintln!(
                     "warn: you will have to manually remove any code related to `{}` from `{}`",
diff --git a/src/tools/clippy/clippy_lints/src/as_underscore.rs b/src/tools/clippy/clippy_lints/src/as_underscore.rs
deleted file mode 100644 (file)
index 0bdef9d..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, TyKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Check for the usage of `as _` conversion using inferred type.
-    ///
-    /// ### Why is this bad?
-    /// The conversion might include lossy conversion and dangerous cast that might go
-    /// undetected du to the type being inferred.
-    ///
-    /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
-    ///
-    /// ### Example
-    /// ```rust
-    /// fn foo(n: usize) {}
-    /// let n: u16 = 256;
-    /// foo(n as _);
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// fn foo(n: usize) {}
-    /// let n: u16 = 256;
-    /// foo(n as usize);
-    /// ```
-    #[clippy::version = "1.63.0"]
-    pub AS_UNDERSCORE,
-    restriction,
-    "detects `as _` conversion"
-}
-declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]);
-
-impl<'tcx> LateLintPass<'tcx> for AsUnderscore {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-
-        if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind {
-
-            let ty_resolved = cx.typeck_results().expr_ty(expr);
-            if let ty::Error(_) = ty_resolved.kind() {
-                span_lint_and_help(
-                    cx,
-                AS_UNDERSCORE,
-                expr.span,
-                "using `as _` conversion",
-                None,
-                "consider giving the type explicitly",
-                );
-            } else {
-            span_lint_and_then(
-                cx,
-                AS_UNDERSCORE,
-                expr.span,
-                "using `as _` conversion",
-                |diag| {
-                    diag.span_suggestion(
-                        ty.span,
-                        "consider giving the type explicitly",
-                        ty_resolved,
-                        Applicability::MachineApplicable,
-                    );
-            }
-            );
-        }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs
deleted file mode 100644 (file)
index 0993adb..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_no_std_crate;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for the usage of `&expr as *const T` or
-    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
-    /// `ptr::addr_of_mut` instead.
-    ///
-    /// ### Why is this bad?
-    /// This would improve readability and avoid creating a reference
-    /// that points to an uninitialized value or unaligned place.
-    /// Read the `ptr::addr_of` docs for more information.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let val = 1;
-    /// let p = &val as *const i32;
-    ///
-    /// let mut val_mut = 1;
-    /// let p_mut = &mut val_mut as *mut i32;
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let val = 1;
-    /// let p = std::ptr::addr_of!(val);
-    ///
-    /// let mut val_mut = 1;
-    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
-    /// ```
-    #[clippy::version = "1.60.0"]
-    pub BORROW_AS_PTR,
-    pedantic,
-    "borrowing just to cast to a raw pointer"
-}
-
-impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);
-
-pub struct BorrowAsPtr {
-    msrv: Option<RustcVersion>,
-}
-
-impl BorrowAsPtr {
-    #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
-            return;
-        }
-
-        if expr.span.from_expansion() {
-            return;
-        }
-
-        if_chain! {
-            if let ExprKind::Cast(left_expr, ty) = &expr.kind;
-            if let TyKind::Ptr(_) = ty.kind;
-            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;
-
-            then {
-                let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
-                let macro_name = match mutability {
-                    Mutability::Not => "addr_of",
-                    Mutability::Mut => "addr_of_mut",
-                };
-
-                span_lint_and_sugg(
-                    cx,
-                    BORROW_AS_PTR,
-                    expr.span,
-                    "borrow as raw pointer",
-                    "try",
-                    format!(
-                        "{}::ptr::{}!({})",
-                        core_or_std,
-                        macro_name,
-                        snippet_opt(cx, e.span).unwrap()
-                    ),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-    }
-
-    extract_msrv_attr!(LateContext);
-}
index 937765b66147912aa6c9172a2b61b4369f91cbec..c4520d003928e3832053fe749045c643407ed2c1 100644 (file)
     ///
     /// ### Example
     /// ```rust
-    /// fn foo(_x: &str) {}
-    ///
     /// let s = &String::new();
     ///
     /// let a: &String = &* s;
-    /// foo(&*s);
     /// ```
     ///
     /// Use instead:
     /// ```rust
-    /// # fn foo(_x: &str) {}
     /// # let s = &String::new();
     /// let a: &String = s;
-    /// foo(&**s);
     /// ```
-    #[clippy::version = "1.59.0"]
+    #[clippy::version = "1.63.0"]
     pub BORROW_DEREF_REF,
     complexity,
     "deref on an immutable reference returns the same type as itself"
diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs
deleted file mode 100644 (file)
index 326ce34..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::match_type;
-use clippy_utils::visitors::is_local_used;
-use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, UintTy};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for naive byte counts
-    ///
-    /// ### Why is this bad?
-    /// The [`bytecount`](https://crates.io/crates/bytecount)
-    /// crate has methods to count your bytes faster, especially for large slices.
-    ///
-    /// ### Known problems
-    /// If you have predominantly small slices, the
-    /// `bytecount::count(..)` method may actually be slower. However, if you can
-    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
-    /// faster in those cases.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let vec = vec![1_u8];
-    /// let count = vec.iter().filter(|x| **x == 0u8).count();
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust,ignore
-    /// # let vec = vec![1_u8];
-    /// let count = bytecount::count(&vec, 0u8);
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub NAIVE_BYTECOUNT,
-    pedantic,
-    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
-}
-
-declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
-
-impl<'tcx> LateLintPass<'tcx> for ByteCount {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if_chain! {
-            if let ExprKind::MethodCall(count, [count_recv], _) = expr.kind;
-            if count.ident.name == sym::count;
-            if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind;
-            if filter.ident.name == sym!(filter);
-            if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
-            let body = cx.tcx.hir().body(body);
-            if let [param] = body.params;
-            if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
-            if let ExprKind::Binary(ref op, l, r) = body.value.kind;
-            if op.node == BinOpKind::Eq;
-            if match_type(cx,
-                       cx.typeck_results().expr_ty(filter_recv).peel_refs(),
-                       &paths::SLICE_ITER);
-            let operand_is_arg = |expr| {
-                let expr = peel_ref_operators(cx, peel_blocks(expr));
-                path_to_local_id(expr, arg_id)
-            };
-            let needle = if operand_is_arg(l) {
-                r
-            } else if operand_is_arg(r) {
-                l
-            } else {
-                return;
-            };
-            if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
-            if !is_local_used(cx, needle, arg_id);
-            then {
-                let haystack = if let ExprKind::MethodCall(path, args, _) =
-                        filter_recv.kind {
-                    let p = path.ident.name;
-                    if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
-                        &args[0]
-                    } else {
-                        filter_recv
-                    }
-                } else {
-                    filter_recv
-                };
-                let mut applicability = Applicability::MaybeIncorrect;
-                span_lint_and_sugg(
-                    cx,
-                    NAIVE_BYTECOUNT,
-                    expr.span,
-                    "you appear to be counting bytes the naive way",
-                    "consider using the bytecount crate",
-                    format!("bytecount::count({}, {})",
-                            snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
-                            snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
-                    applicability,
-                );
-            }
-        };
-    }
-}
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
deleted file mode 100644 (file)
index d70dbf5..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-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
-                );
-            }
-        };
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
deleted file mode 100644 (file)
index 7eff71d..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
-use rustc_hir::{Expr, ExprKind, PathSegment};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{source_map::Spanned, symbol::sym, Span};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for calls to `ends_with` with possible file extensions
-    /// and suggests to use a case-insensitive approach instead.
-    ///
-    /// ### Why is this bad?
-    /// `ends_with` is case-sensitive and may not detect files with a valid extension.
-    ///
-    /// ### Example
-    /// ```rust
-    /// fn is_rust_file(filename: &str) -> bool {
-    ///     filename.ends_with(".rs")
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// fn is_rust_file(filename: &str) -> bool {
-    ///     let filename = std::path::Path::new(filename);
-    ///     filename.extension()
-    ///         .map(|ext| ext.eq_ignore_ascii_case("rs"))
-    ///         .unwrap_or(false)
-    /// }
-    /// ```
-    #[clippy::version = "1.51.0"]
-    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
-    pedantic,
-    "Checks for calls to ends_with with case-sensitive file extensions"
-}
-
-declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
-
-fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
-    if_chain! {
-        if let ExprKind::MethodCall(PathSegment { ident, .. }, [obj, extension, ..], span) = expr.kind;
-        if ident.as_str() == "ends_with";
-        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_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() {
-                ty::Ref(_, ty, ..) => *ty,
-                _ => ty
-            };
-
-            match ty.kind() {
-                ty::Str => {
-                    return Some(span);
-                },
-                ty::Adt(def, _) => {
-                    if ctx.tcx.is_diagnostic_item(sym::String, def.did()) {
-                        return Some(span);
-                    }
-                },
-                _ => { return None; }
-            }
-        }
-    }
-    None
-}
-
-impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
-    fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
-            span_lint_and_help(
-                ctx,
-                CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
-                span,
-                "case-sensitive file extension comparison",
-                None,
-                "consider using a case-insensitive comparison instead",
-            );
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs b/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
new file mode 100644 (file)
index 0000000..56e894c
--- /dev/null
@@ -0,0 +1,25 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, Ty, TyKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::AS_UNDERSCORE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) {
+    if matches!(ty.kind, TyKind::Infer) {
+        span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
+            let ty_resolved = cx.typeck_results().expr_ty(expr);
+            if let ty::Error(_) = ty_resolved.kind() {
+                diag.help("consider giving the type explicitly");
+            } else {
+                diag.span_suggestion(
+                    ty.span,
+                    "consider giving the type explicitly",
+                    ty_resolved,
+                    Applicability::MachineApplicable,
+                );
+            }
+        });
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
new file mode 100644 (file)
index 0000000..6e1f8cd
--- /dev/null
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_no_std_crate;
+use clippy_utils::source::snippet_with_context;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
+use rustc_lint::LateContext;
+
+use super::BORROW_AS_PTR;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    cast_expr: &'tcx Expr<'_>,
+    cast_to: &'tcx Ty<'_>,
+) {
+    if matches!(cast_to.kind, TyKind::Ptr(_))
+        && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
+    {
+        let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
+        let macro_name = match mutability {
+            Mutability::Not => "addr_of",
+            Mutability::Mut => "addr_of_mut",
+        };
+        let mut app = Applicability::MachineApplicable;
+        let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
+
+        span_lint_and_sugg(
+            cx,
+            BORROW_AS_PTR,
+            expr.span,
+            "borrow as raw pointer",
+            "try",
+            format!("{}::ptr::{}!({})", core_or_std, macro_name, snip),
+            Applicability::MachineApplicable,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
new file mode 100644 (file)
index 0000000..284ef16
--- /dev/null
@@ -0,0 +1,63 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{def_id::DefId, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+use rustc_semver::RustcVersion;
+
+use super::CAST_SLICE_FROM_RAW_PARTS;
+
+enum RawPartsKind {
+    Immutable,
+    Mutable,
+}
+
+fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
+    if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) {
+        Some(RawPartsKind::Immutable)
+    } else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) {
+        Some(RawPartsKind::Mutable)
+    } else {
+        None
+    }
+}
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_expr: &Expr<'_>,
+    cast_to: Ty<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if_chain! {
+        if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
+        if let ty::RawPtr(ptrty) = cast_to.kind();
+        if let ty::Slice(_) = ptrty.ty.kind();
+        if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().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 Some(rpk) = raw_parts_kind(cx, fun_def_id);
+        then {
+            let func = match rpk {
+                RawPartsKind::Immutable => "from_raw_parts",
+                RawPartsKind::Mutable => "from_raw_parts_mut"
+            };
+            let span = expr.span;
+            let mut applicability = Applicability::MachineApplicable;
+            let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
+            let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
+            span_lint_and_sugg(
+                cx,
+                CAST_SLICE_FROM_RAW_PARTS,
+                span,
+                &format!("casting the result of `{func}` to {cast_to}"),
+                "replace with",
+                format!("core::ptr::slice_{func}({ptr}, {len})"),
+                applicability
+            );
+        }
+    }
+}
index af3798a0cc8c068520c2efad604a4947fb1c3079..cc5d346b954e3d393e71fa6090c4bcc0839bafa7 100644 (file)
@@ -1,3 +1,5 @@
+mod as_underscore;
+mod borrow_as_ptr;
 mod cast_abs_to_unsigned;
 mod cast_enum_constructor;
 mod cast_lossless;
@@ -8,6 +10,7 @@
 mod cast_ref_to_mut;
 mod cast_sign_loss;
 mod cast_slice_different_sizes;
+mod cast_slice_from_raw_parts;
 mod char_lit_as_u8;
 mod fn_to_numeric_cast;
 mod fn_to_numeric_cast_any;
@@ -16,7 +19,7 @@
 mod unnecessary_cast;
 mod utils;
 
-use clippy_utils::is_hir_ty_cfg_dependant;
+use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
     "casting the result of `abs()` to an unsigned integer can panic"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Check for the usage of `as _` conversion using inferred type.
+    ///
+    /// ### Why is this bad?
+    /// The conversion might include lossy conversion and dangerous cast that might go
+    /// undetected due to the type being inferred.
+    ///
+    /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn foo(n: usize) {}
+    /// let n: u16 = 256;
+    /// foo(n as _);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn foo(n: usize) {}
+    /// let n: u16 = 256;
+    /// foo(n as usize);
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub AS_UNDERSCORE,
+    restriction,
+    "detects `as _` conversion"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for the usage of `&expr as *const T` or
+    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
+    /// `ptr::addr_of_mut` instead.
+    ///
+    /// ### Why is this bad?
+    /// This would improve readability and avoid creating a reference
+    /// that points to an uninitialized value or unaligned place.
+    /// Read the `ptr::addr_of` docs for more information.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let val = 1;
+    /// let p = &val as *const i32;
+    ///
+    /// let mut val_mut = 1;
+    /// let p_mut = &mut val_mut as *mut i32;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let val = 1;
+    /// let p = std::ptr::addr_of!(val);
+    ///
+    /// let mut val_mut = 1;
+    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
+    /// ```
+    #[clippy::version = "1.60.0"]
+    pub BORROW_AS_PTR,
+    pedantic,
+    "borrowing just to cast to a raw pointer"
+}
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for a raw slice being cast to a slice pointer
+    ///
+    /// ### Why is this bad?
+    /// This can result in multiple `&mut` references to the same location when only a pointer is
+    /// required.
+    /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
+    /// the same [safety requirements] to be upheld.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
+    /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
+    /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
+    /// ```
+    /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
+    #[clippy::version = "1.64.0"]
+    pub CAST_SLICE_FROM_RAW_PARTS,
+    suspicious,
+    "casting a slice created from a pointer and length to a slice pointer"
+}
+
 pub struct Casts {
     msrv: Option<RustcVersion>,
 }
@@ -534,7 +624,10 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
     PTR_AS_PTR,
     CAST_ENUM_TRUNCATION,
     CAST_ENUM_CONSTRUCTOR,
-    CAST_ABS_TO_UNSIGNED
+    CAST_ABS_TO_UNSIGNED,
+    AS_UNDERSCORE,
+    BORROW_AS_PTR,
+    CAST_SLICE_FROM_RAW_PARTS
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -547,8 +640,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             return;
         }
 
-        if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
-            if is_hir_ty_cfg_dependant(cx, cast_to) {
+        if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
+            if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
                 return;
             }
             let (cast_from, cast_to) = (
@@ -559,7 +652,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
                 return;
             }
-
+            cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
             fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
@@ -575,6 +668,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
                 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
             }
+
+            as_underscore::check(cx, expr, cast_to_hir);
+
+            if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
+                borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
+            }
         }
 
         cast_ref_to_mut::check(cx, expr);
index fff7da8e33f2fff24aca3a5760557b58f893ac4a..19d2e6e1d1298e448619d608056a591ba13d88eb 100644 (file)
@@ -90,13 +90,20 @@ pub(super) fn check<'tcx>(
 
 fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
+    let replaced_literal;
+    let matchless = if literal_str.contains(['(', ')']) {
+        replaced_literal = literal_str.replace(['(', ')'], "");
+        &replaced_literal
+    } else {
+        literal_str
+    };
     span_lint_and_sugg(
         cx,
         UNNECESSARY_CAST,
         expr.span,
         &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
         "try",
-        format!("{}_{}", literal_str.trim_end_matches('.'), cast_to),
+        format!("{}_{}", matchless.trim_end_matches('.'), cast_to),
         Applicability::MachineApplicable,
     );
 }
index 59f10247a11d4c98ea4821a3746ee2479c37aff9..1506ea604f0dd21ab827db4062d57bd9a2939532 100644 (file)
@@ -1,24 +1,34 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
-use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage};
+use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
+use clippy_utils::{
+    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
+    walk_to_expr_usage,
+};
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
-    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
-    ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
-    TraitItemKind, TyKind, UnOp,
+    self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
+    GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
+    Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
 };
+use rustc_index::bit_set::BitSet;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
+use rustc_middle::ty::{
+    self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
+    ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
+};
+use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
-use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
+use std::collections::VecDeque;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -151,6 +161,7 @@ pub struct Dereferencing {
     /// been finished. Note we can't lint at the end of every body as they can be nested within each
     /// other.
     current_body: Option<BodyId>,
+
     /// The list of locals currently being checked by the lint.
     /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
     /// This is needed for or patterns where one of the branches can be linted, but another can not
@@ -158,6 +169,19 @@ pub struct Dereferencing {
     ///
     /// e.g. `m!(x) | Foo::Bar(ref x)`
     ref_locals: FxIndexMap<HirId, Option<RefPat>>,
+
+    // `IntoIterator` for arrays requires Rust 1.53.
+    msrv: Option<RustcVersion>,
+}
+
+impl Dereferencing {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self {
+            msrv,
+            ..Dereferencing::default()
+        }
+    }
 }
 
 struct StateData {
@@ -170,6 +194,7 @@ struct StateData {
 struct DerefedBorrow {
     count: usize,
     msg: &'static str,
+    snip_expr: Option<HirId>,
 }
 
 enum State {
@@ -250,7 +275,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         match (self.state.take(), kind) {
             (None, kind) => {
                 let expr_ty = typeck.expr_ty(expr);
-                let (position, adjustments) = walk_parents(cx, expr);
+                let (position, adjustments) = walk_parents(cx, expr, self.msrv);
 
                 match kind {
                     RefOp::Deref => {
@@ -331,20 +356,23 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         let deref_msg =
                             "this expression creates a reference which is immediately dereferenced by the compiler";
                         let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
+                        let impl_msg = "the borrowed expression implements the required traits";
 
-                        let (required_refs, msg) = if position.can_auto_borrow() {
-                            (1, if deref_count == 1 { borrow_msg } else { deref_msg })
+                        let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
+                            (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
+                        } else if let Position::ImplArg(hir_id) = position {
+                            (0, impl_msg, Some(hir_id))
                         } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
                             next_adjust.map(|a| &a.kind)
                         {
                             if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
                             {
-                                (3, deref_msg)
+                                (3, deref_msg, None)
                             } else {
-                                (2, deref_msg)
+                                (2, deref_msg, None)
                             }
                         } else {
-                            (2, deref_msg)
+                            (2, deref_msg, None)
                         };
 
                         if deref_count >= required_refs {
@@ -354,6 +382,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                                     // can't be removed without breaking the code. See earlier comment.
                                     count: deref_count - required_refs,
                                     msg,
+                                    snip_expr,
                                 }),
                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
                             ));
@@ -510,7 +539,7 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
                             spans: vec![pat.span],
                             app,
                             replacements: vec![(pat.span, snip.into())],
-                            hir_id: pat.hir_id
+                            hir_id: pat.hir_id,
                         }),
                     );
                 }
@@ -542,6 +571,8 @@ fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
             self.current_body = None;
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 fn try_parse_ref_op<'tcx>(
@@ -594,6 +625,7 @@ enum Position {
     /// The method is defined on a reference type. e.g. `impl Foo for &T`
     MethodReceiverRefImpl,
     Callee,
+    ImplArg(HirId),
     FieldAccess(Symbol),
     Postfix,
     Deref,
@@ -630,7 +662,7 @@ fn precedence(self) -> i8 {
             | Self::Callee
             | Self::FieldAccess(_)
             | Self::Postfix => PREC_POSTFIX,
-            Self::Deref => PREC_PREFIX,
+            Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
             Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
         }
     }
@@ -639,8 +671,12 @@ fn precedence(self) -> i8 {
 /// Walks up the parent expressions attempting to determine both how stable the auto-deref result
 /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
 /// locations as those follow different rules.
-#[allow(clippy::too_many_lines)]
-fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
+#[expect(clippy::too_many_lines)]
+fn walk_parents<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    msrv: Option<RustcVersion>,
+) -> (Position, &'tcx [Adjustment<'tcx>]) {
     let mut adjustments = [].as_slice();
     let mut precedence = 0i8;
     let ctxt = e.span.ctxt();
@@ -745,13 +781,20 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                     .iter()
                     .position(|arg| arg.hir_id == child_id)
                     .zip(expr_sig(cx, func))
-                    .and_then(|(i, sig)| sig.input_with_hir(i))
-                    .map(|(hir_ty, ty)| match hir_ty {
-                        // Type inference for closures can depend on how they're called. Only go by the explicit
-                        // types here.
-                        Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
-                        None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
-                            .position_for_arg(),
+                    .and_then(|(i, sig)| {
+                        sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
+                            // Type inference for closures can depend on how they're called. Only go by the explicit
+                            // types here.
+                            Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
+                            None => {
+                                if let ty::Param(param_ty) = ty.skip_binder().kind() {
+                                    needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
+                                } else {
+                                    ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
+                                        .position_for_arg()
+                                }
+                            },
+                        })
                     }),
                 ExprKind::MethodCall(_, args, _) => {
                     let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
@@ -773,7 +816,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                                     .and_then(|subs| subs.get(1..))
                                 {
                                     Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
-                                    None => cx.tcx.mk_substs([].iter()),
+                                    None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
                                 } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
                                     // Trait methods taking `&self`
                                     sub_ty
@@ -792,12 +835,17 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                                 Position::MethodReceiver
                             }
                         } else {
-                            ty_auto_deref_stability(
-                                cx,
-                                cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
-                                precedence,
-                            )
-                            .position_for_arg()
+                            let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
+                            if let ty::Param(param_ty) = ty.kind() {
+                                needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
+                            } else {
+                                ty_auto_deref_stability(
+                                    cx,
+                                    cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
+                                    precedence,
+                                )
+                                .position_for_arg()
+                            }
                         }
                     })
                 },
@@ -948,6 +996,205 @@ fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
     v.0
 }
 
+// Checks whether:
+// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
+// * `e`'s type implements `Trait` and is copyable
+// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
+//   The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
+// be moved, but it cannot be.
+fn needless_borrow_impl_arg_position<'tcx>(
+    cx: &LateContext<'tcx>,
+    parent: &Expr<'tcx>,
+    arg_index: usize,
+    param_ty: ParamTy,
+    mut expr: &Expr<'tcx>,
+    precedence: i8,
+    msrv: Option<RustcVersion>,
+) -> Position {
+    let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
+    let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
+
+    let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
+    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+    let substs_with_expr_ty = cx
+        .typeck_results()
+        .node_substs(if let ExprKind::Call(callee, _) = parent.kind {
+            callee.hir_id
+        } else {
+            parent.hir_id
+        });
+
+    let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
+    let projection_predicates = predicates
+        .iter()
+        .filter_map(|predicate| {
+            if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
+                Some(projection_predicate)
+            } else {
+                None
+            }
+        })
+        .collect::<Vec<_>>();
+
+    let mut trait_with_ref_mut_self_method = false;
+
+    // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
+    if predicates
+        .iter()
+        .filter_map(|predicate| {
+            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+                && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
+            {
+                Some(trait_predicate.trait_ref.def_id)
+            } else {
+                None
+            }
+        })
+        .inspect(|trait_def_id| {
+            trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
+        })
+        .all(|trait_def_id| {
+            Some(trait_def_id) == destruct_trait_def_id
+                || Some(trait_def_id) == sized_trait_def_id
+                || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
+        })
+    {
+        return Position::Other(precedence);
+    }
+
+    // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
+    // elements are modified each time `check_referent` is called.
+    let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
+
+    let mut check_referent = |referent| {
+        let referent_ty = cx.typeck_results().expr_ty(referent);
+
+        if !is_copy(cx, referent_ty) {
+            return false;
+        }
+
+        // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+        if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
+            return false;
+        }
+
+        if !replace_types(
+            cx,
+            param_ty,
+            referent_ty,
+            fn_sig,
+            arg_index,
+            &projection_predicates,
+            &mut substs_with_referent_ty,
+        ) {
+            return false;
+        }
+
+        predicates.iter().all(|predicate| {
+            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+                && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
+                && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
+                && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
+                && ty.is_array()
+                && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
+            {
+                return false;
+            }
+
+            let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
+            let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
+            cx.tcx
+                .infer_ctxt()
+                .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+        })
+    };
+
+    let mut needless_borrow = false;
+    while let ExprKind::AddrOf(_, _, referent) = expr.kind {
+        if !check_referent(referent) {
+            break;
+        }
+        expr = referent;
+        needless_borrow = true;
+    }
+
+    if needless_borrow {
+        Position::ImplArg(expr.hir_id)
+    } else {
+        Position::Other(precedence)
+    }
+}
+
+fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
+    cx.tcx
+        .associated_items(trait_def_id)
+        .in_definition_order()
+        .any(|assoc_item| {
+            if assoc_item.fn_has_self_parameter {
+                let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
+                matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
+            } else {
+                false
+            }
+        })
+}
+
+// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
+// projected type that is a type parameter. Returns `false` if replacing the types would have an
+// effect on the function signature beyond substituting `new_ty` for `param_ty`.
+// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
+fn replace_types<'tcx>(
+    cx: &LateContext<'tcx>,
+    param_ty: ParamTy,
+    new_ty: Ty<'tcx>,
+    fn_sig: FnSig<'tcx>,
+    arg_index: usize,
+    projection_predicates: &[ProjectionPredicate<'tcx>],
+    substs: &mut [ty::GenericArg<'tcx>],
+) -> bool {
+    let mut replaced = BitSet::new_empty(substs.len());
+
+    let mut deque = VecDeque::with_capacity(substs.len());
+    deque.push_back((param_ty, new_ty));
+
+    while let Some((param_ty, new_ty)) = deque.pop_front() {
+        // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
+        if !fn_sig
+            .inputs_and_output
+            .iter()
+            .enumerate()
+            .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
+        {
+            return false;
+        }
+
+        substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
+
+        // The `replaced.insert(...)` check provides some protection against infinite loops.
+        if replaced.insert(param_ty.index) {
+            for projection_predicate in projection_predicates {
+                if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
+                    && let ty::Term::Ty(term_ty) = projection_predicate.term
+                    && let ty::Param(term_param_ty) = term_ty.kind()
+                {
+                    let item_def_id = projection_predicate.projection_ty.item_def_id;
+                    let assoc_item = cx.tcx.associated_item(item_def_id);
+                    let projection = cx.tcx
+                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
+
+                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
+                        && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
+                    {
+                        deque.push_back((*term_param_ty, projected_ty));
+                    }
+                }
+            }
+        }
+    }
+
+    true
+}
+
 struct TyPosition<'tcx> {
     position: Position,
     ty: Option<Ty<'tcx>>,
@@ -1086,7 +1333,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
         },
         State::DerefedBorrow(state) => {
             let mut app = Applicability::MachineApplicable;
-            let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+            let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
+            let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
             span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
                 let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
                 let sugg = if !snip_is_macro
index a982990e4186c9cc6c2aef5bb9477d9a7b96651a..9ca443b7dff6cbe67a24c5008dee96d2d09be293 100644 (file)
@@ -516,7 +516,10 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
         tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
             params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
                 tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
-                    trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
+                    trait_ref: TraitRef::new(
+                        eq_trait_id,
+                        tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
+                    ),
                     constness: BoundConstness::NotConst,
                     polarity: ImplPolarity::Positive,
                 })))
index cb07f57e87006ff95368b8619b4bb559f983bc8a..0ff1d2755daf6284c5babcdc77a6989e05497f61 100644 (file)
@@ -21,7 +21,7 @@
     /// /// See also: [`foo`]
     /// fn bar() {}
     /// ```
-    #[clippy::version = "1.60.0"]
+    #[clippy::version = "1.63.0"]
     pub DOC_LINK_WITH_QUOTES,
     pedantic,
     "possible typo for an intra-doc link"
index e1eb3b6324c7820091e7b10dfa23f8c80d6e157a..7ff7068f0b05e56aec583ec5dd066ab280109193 100644 (file)
@@ -39,7 +39,7 @@
     /// // a.rs
     /// use crate::b;
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub DUPLICATE_MOD,
     suspicious,
     "file loaded as module multiple times"
index 1ac7bfba06ba217a38c4d23560be1ac841116126..327865e4c858ab36e8166c5773aa505804a44cce 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_hir;
-use clippy_utils::ty::contains_ty;
 use rustc_hir::intravisit;
 use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -30,18 +29,12 @@ pub struct BoxedLocal {
     ///
     /// ### Example
     /// ```rust
-    /// # fn foo(bar: usize) {}
-    /// let x = Box::new(1);
-    /// foo(*x);
-    /// println!("{}", *x);
+    /// fn foo(x: Box<u32>) {}
     /// ```
     ///
     /// Use instead:
     /// ```rust
-    /// # fn foo(bar: usize) {}
-    /// let x = 1;
-    /// foo(x);
-    /// println!("{}", x);
+    /// fn foo(x: u32) {}
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub BOXED_LOCAL,
@@ -172,7 +165,7 @@ fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
                 // skip if there is a `self` parameter binding to a type
                 // that contains `Self` (i.e.: `self: Box<Self>`), see #4804
                 if let Some(trait_self_ty) = self.trait_self_ty {
-                    if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
+                    if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
                         return;
                     }
                 }
index df9b41d2c98bef1fe10d2f1eca1fe10e41477aab..bb50e8fcabbb712c15c4dd0cdae52368fbb8ae23 100644 (file)
@@ -172,7 +172,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
             expr.span,
             "logarithm for bases 2, 10 and e can be computed more accurately",
             "consider using",
-            format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
+            format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method),
             Applicability::MachineApplicable,
         );
     }
@@ -263,13 +263,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
             (
                 SUBOPTIMAL_FLOPS,
                 "square-root of a number can be computed more efficiently and accurately",
-                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
+                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
             )
         } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
             (
                 IMPRECISE_FLOPS,
                 "cube-root of a number can be computed more accurately",
-                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
+                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
             )
         } else if let Some(exponent) = get_integer_from_float_constant(&value) {
             (
@@ -277,7 +277,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
                 "exponentiation with integer powers can be computed more efficiently",
                 format!(
                     "{}.powi({})",
-                    Sugg::hir(cx, &args[0], ".."),
+                    Sugg::hir(cx, &args[0], "..").maybe_par(),
                     numeric_literal::format(&exponent.to_string(), None, false)
                 ),
             )
@@ -327,7 +327,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
                         "consider using",
                         format!(
                             "{}.mul_add({}, {})",
-                            Sugg::hir(cx, &args[0], ".."),
+                            Sugg::hir(cx, &args[0], "..").maybe_par(),
                             Sugg::hir(cx, &args[0], ".."),
                             Sugg::hir(cx, other_addend, ".."),
                         ),
@@ -418,7 +418,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 "consider using",
                 format!(
                     "{}.exp_m1()",
-                    Sugg::hir(cx, self_arg, "..")
+                    Sugg::hir(cx, self_arg, "..").maybe_par()
                 ),
                 Applicability::MachineApplicable,
             );
@@ -550,11 +550,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
         then {
             let positive_abs_sugg = (
                 "manual implementation of `abs` method",
-                format!("{}.abs()", Sugg::hir(cx, body, "..")),
+                format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
             );
             let negative_abs_sugg = (
                 "manual implementation of negation of `abs` method",
-                format!("-{}.abs()", Sugg::hir(cx, body, "..")),
+                format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
             );
             let sugg = if is_testing_positive(cx, cond, body) {
                 if if_expr_positive {
@@ -621,7 +621,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 expr.span,
                 "log base can be expressed more clearly",
                 "consider using",
-                format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
+                format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
                 Applicability::MachineApplicable,
             );
         }
@@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
             if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
                (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
             {
-                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
+                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
                 if_chain! {
                     if let ExprKind::Lit(ref literal) = mul_lhs.kind;
                     if let ast::LitKind::Float(ref value, float_type) = literal.node;
@@ -677,7 +677,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
                 (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
             {
-                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
+                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
                 if_chain! {
                     if let ExprKind::Lit(ref literal) = mul_lhs.kind;
                     if let ast::LitKind::Float(ref value, float_type) = literal.node;
index 925a8cb8deed94ff792f3f4a7ce144ec5b2486c5..0c5851cdbed2a4241e0a3f2d35901f311dd732ec 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -56,29 +56,27 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         };
 
         let mut applicability = Applicability::MachineApplicable;
-        if format_args.value_args.is_empty() {
-            match *format_args.format_string_parts {
+        if format_args.args.is_empty() {
+            match *format_args.format_string.parts {
                 [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
                 [_] => {
-                    if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
-                        // Simulate macro expansion, converting {{ and }} to { and }.
-                        let s_expand = s_src.replace("{{", "{").replace("}}", "}");
-                        let sugg = format!("{}.to_string()", s_expand);
-                        span_useless_format(cx, call_site, sugg, applicability);
-                    }
+                    // Simulate macro expansion, converting {{ and }} to { and }.
+                    let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
+                    let sugg = format!("{}.to_string()", s_expand);
+                    span_useless_format(cx, call_site, sugg, applicability);
                 },
                 [..] => {},
             }
-        } else if let [value] = *format_args.value_args {
+        } else if let [arg] = &*format_args.args {
+            let value = arg.param.value;
             if_chain! {
-                if format_args.format_string_parts == [kw::Empty];
+                if format_args.format_string.parts == [kw::Empty];
                 if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
                     ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
                     ty::Str => true,
                     _ => false,
                 };
-                if let Some(args) = format_args.args();
-                if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
+                if !arg.format.has_string_formatting();
                 then {
                     let is_new_string = match value.kind {
                         ExprKind::Binary(..) => true,
index 1e6feaac26c3ab77e604572bd461efcc27803ca3..9fb9fd99748b4b63eabb571073dcba3306d938fb 100644 (file)
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::is_diag_trait_item;
-use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
+use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::implements_trait;
 use if_chain::if_chain;
+use itertools::Itertools;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
@@ -74,20 +75,16 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
             if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
             if is_format_macro(cx, macro_def_id);
             if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
-            if let Some(args) = format_args.args();
             then {
-                for (i, arg) in args.iter().enumerate() {
-                    if arg.format_trait != sym::Display {
+                for arg in &format_args.args {
+                    if arg.format.has_string_formatting() {
                         continue;
                     }
-                    if arg.has_string_formatting() {
+                    if is_aliased(&format_args, arg.param.value.hir_id) {
                         continue;
                     }
-                    if is_aliased(&args, i) {
-                        continue;
-                    }
-                    check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
-                    check_to_string_in_format_args(cx, name, arg.value);
+                    check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
+                    check_to_string_in_format_args(cx, name, arg.param.value);
                 }
             }
         }
@@ -134,45 +131,56 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
         if is_diag_trait_item(cx, method_def_id, sym::ToString);
         let receiver_ty = cx.typeck_results().expr_ty(receiver);
         if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
+        let (n_needed_derefs, target) =
+            count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
+        if implements_trait(cx, target, display_trait_id, &[]);
+        if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
-            let (n_needed_derefs, target) = count_needed_derefs(
-                receiver_ty,
-                cx.typeck_results().expr_adjustments(receiver).iter(),
-            );
-            if implements_trait(cx, target, display_trait_id, &[]) {
-                if n_needed_derefs == 0 {
-                    span_lint_and_sugg(
-                        cx,
-                        TO_STRING_IN_FORMAT_ARGS,
-                        value.span.with_lo(receiver.span.hi()),
-                        &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
-                        "remove this",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    );
-                } else {
-                    span_lint_and_sugg(
-                        cx,
-                        TO_STRING_IN_FORMAT_ARGS,
-                        value.span,
-                        &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
-                        "use this",
-                        format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs),
-                        Applicability::MachineApplicable,
-                    );
-                }
+            let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
+            if n_needed_derefs == 0 && !needs_ref {
+                span_lint_and_sugg(
+                    cx,
+                    TO_STRING_IN_FORMAT_ARGS,
+                    value.span.with_lo(receiver.span.hi()),
+                    &format!(
+                        "`to_string` applied to a type that implements `Display` in `{}!` args",
+                        name
+                    ),
+                    "remove this",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                span_lint_and_sugg(
+                    cx,
+                    TO_STRING_IN_FORMAT_ARGS,
+                    value.span,
+                    &format!(
+                        "`to_string` applied to a type that implements `Display` in `{}!` args",
+                        name
+                    ),
+                    "use this",
+                    format!(
+                        "{}{:*>width$}{}",
+                        if needs_ref { "&" } else { "" },
+                        "",
+                        receiver_snippet,
+                        width = n_needed_derefs
+                    ),
+                    Applicability::MachineApplicable,
+                );
             }
         }
     }
 }
 
-// Returns true if `args[i]` "refers to" or "is referred to by" another argument.
-fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
-    let value = args[i].value;
-    args.iter()
-        .enumerate()
-        .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
+// Returns true if `hir_id` is referred to by multiple format params
+fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
+    args.params()
+        .filter(|param| param.value.hir_id == hir_id)
+        .at_most_one()
+        .is_err()
 }
 
 fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
index 04b5be6c80ec6acf2727dfeaec769f0f71f43de0..d8bc0bf08f2b314b8513902d14f6da434e229d74 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
+use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
 use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -168,10 +168,9 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
         if let macro_def_id = outer_macro.def_id;
         if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
         if is_format_macro(cx, macro_def_id);
-        if let Some(args) = format_args.args();
         then {
-            for arg in args {
-                if arg.format_trait != impl_trait.name {
+            for arg in format_args.args {
+                if arg.format.r#trait != impl_trait.name {
                     continue;
                 }
                 check_format_arg_self(cx, expr, &arg, impl_trait);
@@ -180,11 +179,11 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
     }
 }
 
-fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
+fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
     // Handle multiple dereferencing of references e.g. &&self
     // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
     // Since the argument to fmt is itself a reference: &self
-    let reference = peel_ref_operators(cx, arg.value);
+    let reference = peel_ref_operators(cx, arg.param.value);
     let map = cx.tcx.hir();
     // Is the reference self?
     if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
index 73261fb8a44c7c9ed0891bdb971b3f40a8edcaa9..90911e0bf2595ca667b9b3f14b0a755c0e4290f6 100644 (file)
@@ -1,6 +1,6 @@
 mod must_use;
 mod not_unsafe_ptr_arg_deref;
-mod result_unit_err;
+mod result;
 mod too_many_arguments;
 mod too_many_lines;
 
     "public function returning `Result` with an `Err` type of `()`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for functions that return `Result` with an unusually large
+    /// `Err`-variant.
+    ///
+    /// ### Why is this bad?
+    /// A `Result` is at least as large as the `Err`-variant. While we
+    /// expect that variant to be seldomly used, the compiler needs to reserve
+    /// and move that much memory every single time.
+    ///
+    /// ### Known problems
+    /// The size determined by Clippy is platform-dependent.
+    ///
+    /// ### Examples
+    /// ```rust
+    /// pub enum ParseError {
+    ///     UnparsedBytes([u8; 512]),
+    ///     UnexpectedEof,
+    /// }
+    ///
+    /// // The `Result` has at least 512 bytes, even in the `Ok`-case
+    /// pub fn parse() -> Result<(), ParseError> {
+    ///     Ok(())
+    /// }
+    /// ```
+    /// should be
+    /// ```
+    /// pub enum ParseError {
+    ///     UnparsedBytes(Box<[u8; 512]>),
+    ///     UnexpectedEof,
+    /// }
+    ///
+    /// // The `Result` is slightly larger than a pointer
+    /// pub fn parse() -> Result<(), ParseError> {
+    ///     Ok(())
+    /// }
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub RESULT_LARGE_ERR,
+    perf,
+    "function returning `Result` with large `Err` type"
+}
+
 #[derive(Copy, Clone)]
 pub struct Functions {
     too_many_arguments_threshold: u64,
     too_many_lines_threshold: u64,
+    large_error_threshold: u64,
 }
 
 impl Functions {
-    pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self {
+    pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self {
         Self {
             too_many_arguments_threshold,
             too_many_lines_threshold,
+            large_error_threshold,
         }
     }
 }
@@ -240,6 +285,7 @@ pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) ->
     DOUBLE_MUST_USE,
     MUST_USE_CANDIDATE,
     RESULT_UNIT_ERR,
+    RESULT_LARGE_ERR,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -259,18 +305,18 @@ fn check_fn(
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         must_use::check_item(cx, item);
-        result_unit_err::check_item(cx, item);
+        result::check_item(cx, item, self.large_error_threshold);
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
         must_use::check_impl_item(cx, item);
-        result_unit_err::check_impl_item(cx, item);
+        result::check_impl_item(cx, item, self.large_error_threshold);
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
         too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
         not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
         must_use::check_trait_item(cx, item);
-        result_unit_err::check_trait_item(cx, item);
+        result::check_trait_item(cx, item, self.large_error_threshold);
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs
new file mode 100644 (file)
index 0000000..af520a4
--- /dev/null
@@ -0,0 +1,100 @@
+use rustc_errors::Diagnostic;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{sym, Span};
+use rustc_typeck::hir_ty_to_ty;
+
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
+use clippy_utils::trait_ref_of_method;
+use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item};
+
+use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
+
+/// The type of the `Err`-variant in a `std::result::Result` returned by the
+/// given `FnDecl`
+fn result_err_ty<'tcx>(
+    cx: &LateContext<'tcx>,
+    decl: &hir::FnDecl<'tcx>,
+    item_span: Span,
+) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
+    if !in_external_macro(cx.sess(), item_span)
+        && let hir::FnRetTy::Return(hir_ty) = decl.output
+        && let ty = hir_ty_to_ty(cx.tcx, hir_ty)
+        && is_type_diagnostic_item(cx, ty, sym::Result)
+        && let ty::Adt(_, substs) = ty.kind()
+    {
+        let err_ty = substs.type_at(1);
+        Some((hir_ty, err_ty))
+    } else {
+        None
+    }
+}
+
+pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
+    if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
+        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
+    {
+        if cx.access_levels.is_exported(item.def_id) {
+            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+            check_result_unit_err(cx, err_ty, fn_header_span);
+        }
+        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+    }
+}
+
+pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
+    // Don't lint if method is a trait's implementation, we can't do anything about those
+    if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
+        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
+        && trait_ref_of_method(cx, item.def_id).is_none()
+    {
+        if cx.access_levels.is_exported(item.def_id) {
+            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+            check_result_unit_err(cx, err_ty, fn_header_span);
+        }
+        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+    }
+}
+
+pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
+    if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
+        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+        if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) {
+            if cx.access_levels.is_exported(item.def_id) {
+                check_result_unit_err(cx, err_ty, fn_header_span);
+            }
+            check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+        }
+    }
+}
+
+fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) {
+    if err_ty.is_unit() {
+        span_lint_and_help(
+            cx,
+            RESULT_UNIT_ERR,
+            fn_header_span,
+            "this returns a `Result<_, ()>`",
+            None,
+            "use a custom `Error` type instead",
+        );
+    }
+}
+
+fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
+    let ty_size = approx_ty_size(cx, err_ty);
+    if ty_size >= large_err_threshold {
+        span_lint_and_then(
+            cx,
+            RESULT_LARGE_ERR,
+            hir_ty_span,
+            "the `Err`-variant returned from this function is very large",
+            |diag: &mut Diagnostic| {
+                diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
+                diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs
deleted file mode 100644 (file)
index 2e63a1f..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
-use rustc_span::{sym, Span};
-use rustc_typeck::hir_ty_to_ty;
-
-use if_chain::if_chain;
-
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::trait_ref_of_method;
-use clippy_utils::ty::is_type_diagnostic_item;
-
-use super::RESULT_UNIT_ERR;
-
-pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
-    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 {
-            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
-        }
-    }
-}
-
-pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
-    if let hir::ImplItemKind::Fn(ref sig, _) = 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 && trait_ref_of_method(cx, item.def_id).is_none() {
-            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
-        }
-    }
-}
-
-pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
-    if let hir::TraitItemKind::Fn(ref sig, _) = 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 {
-            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
-        }
-    }
-}
-
-fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) {
-    if_chain! {
-        if !in_external_macro(cx.sess(), item_span);
-        if let hir::FnRetTy::Return(ty) = decl.output;
-        let ty = hir_ty_to_ty(cx.tcx, ty);
-        if is_type_diagnostic_item(cx, ty, sym::Result);
-        if let ty::Adt(_, substs) = ty.kind();
-        let err_ty = substs.type_at(1);
-        if err_ty.is_unit();
-        then {
-            span_lint_and_help(
-                cx,
-                RESULT_UNIT_ERR,
-                fn_header_span,
-                "this returns a `Result<_, ()>`",
-                None,
-                "use a custom `Error` type instead",
-            );
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/get_first.rs b/src/tools/clippy/clippy_lints/src/get_first.rs
deleted file mode 100644 (file)
index 529f7ba..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for using `x.get(0)` instead of
-    /// `x.first()`.
-    ///
-    /// ### Why is this bad?
-    /// Using `x.first()` is easier to read and has the same
-    /// result.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = vec![2, 3, 5];
-    /// let first_element = x.get(0);
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// let x = vec![2, 3, 5];
-    /// let first_element = x.first();
-    /// ```
-    #[clippy::version = "1.63.0"]
-    pub GET_FIRST,
-    style,
-    "Using `x.get(0)` when `x.first()` is simpler"
-}
-declare_lint_pass!(GetFirst => [GET_FIRST]);
-
-impl<'tcx> LateLintPass<'tcx> for GetFirst {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if_chain! {
-            if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &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::SLICE_GET);
-
-            if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
-            if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
-
-            then {
-                let mut applicability = Applicability::MachineApplicable;
-                let slice_name = snippet_with_applicability(
-                    cx,
-                    struct_calling_on.span, "..",
-                    &mut applicability,
-                );
-                span_lint_and_sugg(
-                    cx,
-                    GET_FIRST,
-                    expr.span,
-                    &format!("accessing first element with `{0}.get(0)`", slice_name),
-                    "try",
-                    format!("{}.first()", slice_name),
-                    applicability,
-                );
-            }
-        }
-    }
-}
index e9501700784931c25ac97277fafa0b825ceac596..4d703d691acc2f81e57296256027072221c27214 100644 (file)
@@ -1,8 +1,9 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::SpanlessEq;
 use if_chain::if_chain;
+use rustc_errors::Diagnostic;
 use rustc_hir::intravisit::{self as visit, Visitor};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 
 impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        let mut arm_visit = ArmVisitor {
-            mutex_lock_called: false,
-            found_mutex: None,
-            cx,
-        };
-        let mut op_visit = OppVisitor {
-            mutex_lock_called: false,
-            found_mutex: None,
-            cx,
-        };
+        let mut arm_visit = ArmVisitor { found_mutex: None, cx };
+        let mut op_visit = OppVisitor { found_mutex: None, cx };
         if let Some(higher::IfLet {
             let_expr,
             if_then,
@@ -63,18 +56,28 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         }) = higher::IfLet::hir(cx, expr)
         {
             op_visit.visit_expr(let_expr);
-            if op_visit.mutex_lock_called {
+            if let Some(op_mutex) = op_visit.found_mutex {
                 arm_visit.visit_expr(if_then);
                 arm_visit.visit_expr(if_else);
 
-                if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
-                    span_lint_and_help(
+                if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) {
+                    let diag = |diag: &mut Diagnostic| {
+                        diag.span_label(
+                            op_mutex.span,
+                            "this Mutex will remain locked for the entire `if let`-block...",
+                        );
+                        diag.span_label(
+                            arm_mutex.span,
+                            "... and is tried to lock again here, which will always deadlock.",
+                        );
+                        diag.help("move the lock call outside of the `if let ...` expression");
+                    };
+                    span_lint_and_then(
                         cx,
                         IF_LET_MUTEX,
                         expr.span,
                         "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
-                        None,
-                        "move the lock call outside of the `if let ...` expression",
+                        diag,
                     );
                 }
             }
@@ -84,7 +87,6 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 
 /// Checks if `Mutex::lock` is called in the `if let` expr.
 pub struct OppVisitor<'a, 'tcx> {
-    mutex_lock_called: bool,
     found_mutex: Option<&'tcx Expr<'tcx>>,
     cx: &'a LateContext<'tcx>,
 }
@@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
             self.found_mutex = Some(mutex);
-            self.mutex_lock_called = true;
             return;
         }
         visit::walk_expr(self, expr);
@@ -102,7 +103,6 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 
 /// Checks if `Mutex::lock` is called in any of the branches.
 pub struct ArmVisitor<'a, 'tcx> {
-    mutex_lock_called: bool,
     found_mutex: Option<&'tcx Expr<'tcx>>,
     cx: &'a LateContext<'tcx>,
 }
@@ -111,7 +111,6 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
         if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
             self.found_mutex = Some(mutex);
-            self.mutex_lock_called = true;
             return;
         }
         visit::walk_expr(self, expr);
@@ -119,9 +118,12 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 }
 
 impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
-    fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
-        self.found_mutex
-            .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
+    fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> {
+        self.found_mutex.and_then(|arm_mutex| {
+            SpanlessEq::new(self.cx)
+                .eq_expr(op_mutex, arm_mutex)
+                .then_some(arm_mutex)
+        })
     }
 }
 
@@ -129,7 +131,7 @@ fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Opt
     if_chain! {
         if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
         if path.ident.as_str() == "lock";
-        let ty = cx.typeck_results().expr_ty(self_arg);
+        let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
         if is_type_diagnostic_item(cx, ty, sym::Mutex);
         then {
             Some(self_arg)
index b8d227855d97616c1f4171dcd4e0b621068e328f..11c43247868ca46b817e01f60b13efe562a531b3 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::eager_or_lazy::switch_to_eager_eval;
 use clippy_utils::source::snippet_with_macro_callsite;
 use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
-use if_chain::if_chain;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for if-else that could be written to `bool::then`.
+    /// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
     ///
     /// ### Why is this bad?
-    /// Looks a little redundant. Using `bool::then` helps it have less lines of code.
+    /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
+    /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
+    /// in comparison to `bool::then`.
     ///
     /// ### Example
     /// ```rust
@@ -39,7 +41,7 @@
     #[clippy::version = "1.53.0"]
     pub IF_THEN_SOME_ELSE_NONE,
     restriction,
-    "Finds if-else that could be written using `bool::then`"
+    "Finds if-else that could be written using either `bool::then` or `bool::then_some`"
 }
 
 pub struct IfThenSomeElseNone {
@@ -56,7 +58,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
 
 impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
             return;
         }
@@ -70,43 +72,47 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
             return;
         }
 
-        if_chain! {
-            if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
-            if let ExprKind::Block(then_block, _) = then.kind;
-            if let Some(then_expr) = then_block.expr;
-            if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
-            if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
-            if is_lang_ctor(cx, then_call_qpath, OptionSome);
-            if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
-            if is_lang_ctor(cx, qpath, OptionNone);
-            if !stmts_contains_early_return(then_block.stmts);
-            then {
-                let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
-                let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
-                    format!("({})", cond_snip)
-                } else {
-                    cond_snip.into_owned()
-                };
-                let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
-                let closure_body = if then_block.stmts.is_empty() {
-                    arg_snip.into_owned()
-                } else {
-                    format!("{{ /* snippet */ {} }}", arg_snip)
-                };
-                let help = format!(
-                    "consider using `bool::then` like: `{}.then(|| {})`",
-                    cond_snip,
-                    closure_body,
-                );
-                span_lint_and_help(
-                    cx,
-                    IF_THEN_SOME_ELSE_NONE,
-                    expr.span,
-                    "this could be simplified with `bool::then`",
-                    None,
-                    &help,
-                );
-            }
+        if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
+            && let ExprKind::Block(then_block, _) = then.kind
+            && let Some(then_expr) = then_block.expr
+            && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
+            && let ExprKind::Path(ref then_call_qpath) = then_call.kind
+            && is_lang_ctor(cx, then_call_qpath, OptionSome)
+            && let ExprKind::Path(ref qpath) = peel_blocks(els).kind
+            && is_lang_ctor(cx, qpath, OptionNone)
+            && !stmts_contains_early_return(then_block.stmts)
+        {
+            let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
+            let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
+                format!("({})", cond_snip)
+            } else {
+                cond_snip.into_owned()
+            };
+            let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
+            let mut method_body = if then_block.stmts.is_empty() {
+                arg_snip.into_owned()
+            } else {
+                format!("{{ /* snippet */ {} }}", arg_snip)
+            };
+            let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+                "then_some"
+            } else {
+                method_body.insert_str(0, "|| ");
+                "then"
+            };
+
+            let help = format!(
+                "consider using `bool::{}` like: `{}.{}({})`",
+                method_name, cond_snip, method_name, method_body,
+            );
+            span_lint_and_help(
+                cx,
+                IF_THEN_SOME_ELSE_NONE,
+                expr.span,
+                &format!("this could be simplified with `bool::{}`", method_name),
+                None,
+                &help,
+            );
         }
     }
 
index 01082cc8eeb64933304d54f3c2616d81a723f247..134cbbf7b5c66ad5f218173022533dc55b19c77b 100644 (file)
     LintId::of(booleans::NONMINIMAL_BOOL),
     LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
     LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
-    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),
     LintId::of(casts::CAST_REF_TO_MUT),
     LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
+    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
     LintId::of(casts::CHAR_LIT_AS_U8),
     LintId::of(casts::FN_TO_NUMERIC_CAST),
     LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
@@ -80,9 +80,9 @@
     LintId::of(functions::DOUBLE_MUST_USE),
     LintId::of(functions::MUST_USE_UNIT),
     LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
+    LintId::of(functions::RESULT_LARGE_ERR),
     LintId::of(functions::RESULT_UNIT_ERR),
     LintId::of(functions::TOO_MANY_ARGUMENTS),
-    LintId::of(get_first::GET_FIRST),
     LintId::of(if_let_mutex::IF_LET_MUTEX),
     LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
     LintId::of(infinite_iter::INFINITE_ITER),
     LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
     LintId::of(manual_retain::MANUAL_RETAIN),
     LintId::of(manual_strip::MANUAL_STRIP),
-    LintId::of(map_clone::MAP_CLONE),
     LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
     LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
     LintId::of(match_result_ok::MATCH_RESULT_OK),
     LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
     LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
     LintId::of(methods::BIND_INSTEAD_OF_MAP),
+    LintId::of(methods::BYTES_COUNT_TO_LEN),
     LintId::of(methods::BYTES_NTH),
     LintId::of(methods::CHARS_LAST_CMP),
     LintId::of(methods::CHARS_NEXT_CMP),
     LintId::of(methods::CLONE_DOUBLE_REF),
     LintId::of(methods::CLONE_ON_COPY),
+    LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
     LintId::of(methods::ERR_EXPECT),
     LintId::of(methods::EXPECT_FUN_CALL),
     LintId::of(methods::EXTEND_WITH_DRAIN),
     LintId::of(methods::FILTER_MAP_IDENTITY),
     LintId::of(methods::FILTER_NEXT),
     LintId::of(methods::FLAT_MAP_IDENTITY),
+    LintId::of(methods::GET_FIRST),
     LintId::of(methods::GET_LAST_WITH_LEN),
     LintId::of(methods::INSPECT_FOR_EACH),
     LintId::of(methods::INTO_ITER_ON_REF),
     LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
     LintId::of(methods::MANUAL_SPLIT_ONCE),
     LintId::of(methods::MANUAL_STR_REPEAT),
+    LintId::of(methods::MAP_CLONE),
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
     LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::MUT_MUTEX_LOCK),
     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::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(methods::NO_EFFECT_REPLACE),
     LintId::of(methods::OBFUSCATED_IF_ELSE),
     LintId::of(methods::OK_EXPECT),
     LintId::of(methods::OPTION_MAP_OR_NONE),
     LintId::of(methods::OR_FUN_CALL),
     LintId::of(methods::OR_THEN_UNWRAP),
+    LintId::of(methods::RANGE_ZIP_WITH_LEN),
+    LintId::of(methods::REPEAT_ONCE),
     LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
     LintId::of(methods::SEARCH_IS_SOME),
     LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
     LintId::of(methods::STRING_EXTEND_CHARS),
     LintId::of(methods::SUSPICIOUS_MAP),
     LintId::of(methods::SUSPICIOUS_SPLITN),
+    LintId::of(methods::SUSPICIOUS_TO_OWNED),
     LintId::of(methods::UNINIT_ASSUMED_INIT),
+    LintId::of(methods::UNIT_HASH),
     LintId::of(methods::UNNECESSARY_FILTER_MAP),
     LintId::of(methods::UNNECESSARY_FIND_MAP),
     LintId::of(methods::UNNECESSARY_FOLD),
     LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
+    LintId::of(methods::UNNECESSARY_SORT_BY),
     LintId::of(methods::UNNECESSARY_TO_OWNED),
     LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
     LintId::of(methods::USELESS_ASREF),
+    LintId::of(methods::VEC_RESIZE_TO_ZERO),
     LintId::of(methods::WRONG_SELF_CONVENTION),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
     LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
     LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
     LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
+    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
-    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
     LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
     LintId::of(needless_bool::BOOL_COMPARISON),
     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(open_options::NONSENSICAL_OPEN_OPTIONS),
+    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
     LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
     LintId::of(operators::ASSIGN_OP_PATTERN),
     LintId::of(operators::BAD_BIT_MASK),
     LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
     LintId::of(question_mark::QUESTION_MARK),
     LintId::of(ranges::MANUAL_RANGE_CONTAINS),
-    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
     LintId::of(ranges::REVERSED_EMPTY_RANGES),
     LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
     LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
     LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
     LintId::of(reference::DEREF_ADDROF),
     LintId::of(regex::INVALID_REGEX),
-    LintId::of(repeat_once::REPEAT_ONCE),
     LintId::of(returns::LET_AND_RETURN),
     LintId::of(returns::NEEDLESS_RETURN),
     LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
     LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
     LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
     LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
+    LintId::of(transmute::TRANSMUTING_NULL),
     LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
     LintId::of(transmute::USELESS_TRANSMUTE),
     LintId::of(transmute::WRONG_TRANSMUTE),
-    LintId::of(transmuting_null::TRANSMUTING_NULL),
     LintId::of(types::BORROWED_BOX),
     LintId::of(types::BOX_COLLECTION),
     LintId::of(types::REDUNDANT_ALLOCATION),
     LintId::of(types::VEC_BOX),
     LintId::of(unicode::INVISIBLE_CHARACTERS),
     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(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),
+    LintId::of(unused_peekable::UNUSED_PEEKABLE),
     LintId::of(unused_unit::UNUSED_UNIT),
     LintId::of(unwrap::PANICKING_UNWRAP),
     LintId::of(unwrap::UNNECESSARY_UNWRAP),
     LintId::of(useless_conversion::USELESS_CONVERSION),
     LintId::of(vec::USELESS_VEC),
     LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
-    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
+    LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
     LintId::of(write::PRINTLN_EMPTY_STRING),
     LintId::of(write::PRINT_LITERAL),
     LintId::of(write::PRINT_WITH_NEWLINE),
index 3784d3c68dceef2046f01b4f2a0393004867e35a..aa247352f88fb653ccbc6572c45bb74caedc63c7 100644 (file)
@@ -6,7 +6,6 @@
     LintId::of(attrs::DEPRECATED_CFG_ATTR),
     LintId::of(booleans::NONMINIMAL_BOOL),
     LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
-    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
     LintId::of(casts::CHAR_LIT_AS_U8),
     LintId::of(casts::UNNECESSARY_CAST),
     LintId::of(dereference::EXPLICIT_AUTO_DEREF),
@@ -33,6 +32,7 @@
     LintId::of(matches::NEEDLESS_MATCH),
     LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
     LintId::of(methods::BIND_INSTEAD_OF_MAP),
+    LintId::of(methods::BYTES_COUNT_TO_LEN),
     LintId::of(methods::CLONE_ON_COPY),
     LintId::of(methods::FILTER_MAP_IDENTITY),
     LintId::of(methods::FILTER_NEXT),
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(methods::OPTION_FILTER_MAP),
     LintId::of(methods::OR_THEN_UNWRAP),
+    LintId::of(methods::RANGE_ZIP_WITH_LEN),
+    LintId::of(methods::REPEAT_ONCE),
     LintId::of(methods::SEARCH_IS_SOME),
     LintId::of(methods::SKIP_WHILE_NEXT),
     LintId::of(methods::UNNECESSARY_FILTER_MAP),
     LintId::of(methods::UNNECESSARY_FIND_MAP),
+    LintId::of(methods::UNNECESSARY_SORT_BY),
     LintId::of(methods::USELESS_ASREF),
     LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
     LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
@@ -69,6 +72,7 @@
     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(operators::DOUBLE_COMPARISONS),
     LintId::of(operators::DURATION_SUBSEC),
     LintId::of(operators::IDENTITY_OP),
     LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
     LintId::of(precedence::PRECEDENCE),
     LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
-    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
     LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
     LintId::of(redundant_slicing::REDUNDANT_SLICING),
     LintId::of(reference::DEREF_ADDROF),
-    LintId::of(repeat_once::REPEAT_ONCE),
     LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
     LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
     LintId::of(swap::MANUAL_SWAP),
     LintId::of(types::TYPE_COMPLEXITY),
     LintId::of(types::VEC_BOX),
     LintId::of(unit_types::UNIT_ARG),
-    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
     LintId::of(unwrap::UNNECESSARY_UNWRAP),
     LintId::of(useless_conversion::USELESS_CONVERSION),
     LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
index 006275d1383ff0d313784fb9a5a486d88068bd3e..bb94037ec2e7949ba52a2b25ba0bd89ae6794e89 100644 (file)
     LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
     LintId::of(methods::CLONE_DOUBLE_REF),
     LintId::of(methods::ITERATOR_STEP_BY_ZERO),
+    LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(methods::SUSPICIOUS_SPLITN),
     LintId::of(methods::UNINIT_ASSUMED_INIT),
+    LintId::of(methods::UNIT_HASH),
+    LintId::of(methods::VEC_RESIZE_TO_ZERO),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
-    LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
     LintId::of(operators::BAD_BIT_MASK),
     LintId::of(operators::CMP_NAN),
     LintId::of(serde_api::SERDE_API_MISUSE),
     LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
     LintId::of(swap::ALMOST_SWAPPED),
+    LintId::of(transmute::TRANSMUTING_NULL),
     LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
     LintId::of(transmute::WRONG_TRANSMUTE),
-    LintId::of(transmuting_null::TRANSMUTING_NULL),
     LintId::of(unicode::INVISIBLE_CHARACTERS),
     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::UNIT_CMP),
     LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
     LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
     LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
     LintId::of(unwrap::PANICKING_UNWRAP),
-    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 ])
index c540573b80228e8f3786a4117ab3dbda503266f7..fd20e016578a1bad268088cfe5dac73f0182b881 100644 (file)
@@ -38,7 +38,6 @@
     almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
     approx_const::APPROX_CONSTANT,
     as_conversions::AS_CONVERSIONS,
-    as_underscore::AS_UNDERSCORE,
     asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
     asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
     assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
     bool_assert_comparison::BOOL_ASSERT_COMPARISON,
     booleans::NONMINIMAL_BOOL,
     booleans::OVERLY_COMPLEX_BOOL_EXPR,
-    borrow_as_ptr::BORROW_AS_PTR,
     borrow_deref_ref::BORROW_DEREF_REF,
-    bytecount::NAIVE_BYTECOUNT,
-    bytes_count_to_len::BYTES_COUNT_TO_LEN,
     cargo::CARGO_COMMON_METADATA,
     cargo::MULTIPLE_CRATE_VERSIONS,
     cargo::NEGATIVE_FEATURE_NAMES,
     cargo::REDUNDANT_FEATURE_NAMES,
     cargo::WILDCARD_DEPENDENCIES,
-    case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    casts::AS_UNDERSCORE,
+    casts::BORROW_AS_PTR,
     casts::CAST_ABS_TO_UNSIGNED,
     casts::CAST_ENUM_CONSTRUCTOR,
     casts::CAST_ENUM_TRUNCATION,
@@ -80,6 +77,7 @@
     casts::CAST_REF_TO_MUT,
     casts::CAST_SIGN_LOSS,
     casts::CAST_SLICE_DIFFERENT_SIZES,
+    casts::CAST_SLICE_FROM_RAW_PARTS,
     casts::CHAR_LIT_AS_U8,
     casts::FN_TO_NUMERIC_CAST,
     casts::FN_TO_NUMERIC_CAST_ANY,
     functions::MUST_USE_CANDIDATE,
     functions::MUST_USE_UNIT,
     functions::NOT_UNSAFE_PTR_ARG_DEREF,
+    functions::RESULT_LARGE_ERR,
     functions::RESULT_UNIT_ERR,
     functions::TOO_MANY_ARGUMENTS,
     functions::TOO_MANY_LINES,
     future_not_send::FUTURE_NOT_SEND,
-    get_first::GET_FIRST,
     if_let_mutex::IF_LET_MUTEX,
     if_not_else::IF_NOT_ELSE,
     if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
     manual_bits::MANUAL_BITS,
     manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
     manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
-    manual_ok_or::MANUAL_OK_OR,
     manual_rem_euclid::MANUAL_REM_EUCLID,
     manual_retain::MANUAL_RETAIN,
+    manual_string_new::MANUAL_STRING_NEW,
     manual_strip::MANUAL_STRIP,
-    map_clone::MAP_CLONE,
-    map_err_ignore::MAP_ERR_IGNORE,
     map_unit_fn::OPTION_MAP_UNIT_FN,
     map_unit_fn::RESULT_MAP_UNIT_FN,
     match_result_ok::MATCH_RESULT_OK,
     mem_replace::MEM_REPLACE_WITH_DEFAULT,
     mem_replace::MEM_REPLACE_WITH_UNINIT,
     methods::BIND_INSTEAD_OF_MAP,
+    methods::BYTES_COUNT_TO_LEN,
     methods::BYTES_NTH,
+    methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
     methods::CHARS_LAST_CMP,
     methods::CHARS_NEXT_CMP,
     methods::CLONED_INSTEAD_OF_COPIED,
     methods::CLONE_DOUBLE_REF,
     methods::CLONE_ON_COPY,
     methods::CLONE_ON_REF_PTR,
+    methods::COLLAPSIBLE_STR_REPLACE,
     methods::ERR_EXPECT,
     methods::EXPECT_FUN_CALL,
     methods::EXPECT_USED,
     methods::FLAT_MAP_IDENTITY,
     methods::FLAT_MAP_OPTION,
     methods::FROM_ITER_INSTEAD_OF_COLLECT,
+    methods::GET_FIRST,
     methods::GET_LAST_WITH_LEN,
     methods::GET_UNWRAP,
     methods::IMPLICIT_CLONE,
     methods::ITER_NEXT_SLICE,
     methods::ITER_NTH,
     methods::ITER_NTH_ZERO,
+    methods::ITER_ON_EMPTY_COLLECTIONS,
+    methods::ITER_ON_SINGLE_ITEMS,
     methods::ITER_OVEREAGER_CLONED,
     methods::ITER_SKIP_NEXT,
     methods::ITER_WITH_DRAIN,
     methods::MANUAL_FILTER_MAP,
     methods::MANUAL_FIND_MAP,
+    methods::MANUAL_OK_OR,
     methods::MANUAL_SATURATING_ARITHMETIC,
     methods::MANUAL_SPLIT_ONCE,
     methods::MANUAL_STR_REPEAT,
+    methods::MAP_CLONE,
     methods::MAP_COLLECT_RESULT_UNIT,
+    methods::MAP_ERR_IGNORE,
     methods::MAP_FLATTEN,
     methods::MAP_IDENTITY,
     methods::MAP_UNWRAP_OR,
+    methods::MUT_MUTEX_LOCK,
+    methods::NAIVE_BYTECOUNT,
     methods::NEEDLESS_OPTION_AS_DEREF,
     methods::NEEDLESS_OPTION_TAKE,
     methods::NEEDLESS_SPLITN,
     methods::NEW_RET_NO_SELF,
+    methods::NONSENSICAL_OPEN_OPTIONS,
     methods::NO_EFFECT_REPLACE,
     methods::OBFUSCATED_IF_ELSE,
     methods::OK_EXPECT,
     methods::OPTION_MAP_OR_NONE,
     methods::OR_FUN_CALL,
     methods::OR_THEN_UNWRAP,
+    methods::PATH_BUF_PUSH_OVERWRITE,
+    methods::RANGE_ZIP_WITH_LEN,
+    methods::REPEAT_ONCE,
     methods::RESULT_MAP_OR_INTO_OPTION,
     methods::SEARCH_IS_SOME,
     methods::SHOULD_IMPLEMENT_TRAIT,
     methods::SINGLE_CHAR_ADD_STR,
     methods::SINGLE_CHAR_PATTERN,
     methods::SKIP_WHILE_NEXT,
+    methods::STABLE_SORT_PRIMITIVE,
     methods::STRING_EXTEND_CHARS,
     methods::SUSPICIOUS_MAP,
     methods::SUSPICIOUS_SPLITN,
+    methods::SUSPICIOUS_TO_OWNED,
     methods::UNINIT_ASSUMED_INIT,
+    methods::UNIT_HASH,
     methods::UNNECESSARY_FILTER_MAP,
     methods::UNNECESSARY_FIND_MAP,
     methods::UNNECESSARY_FOLD,
     methods::UNNECESSARY_JOIN,
     methods::UNNECESSARY_LAZY_EVALUATIONS,
+    methods::UNNECESSARY_SORT_BY,
     methods::UNNECESSARY_TO_OWNED,
     methods::UNWRAP_OR_ELSE_DEFAULT,
     methods::UNWRAP_USED,
     methods::USELESS_ASREF,
+    methods::VEC_RESIZE_TO_ZERO,
+    methods::VERBOSE_FILE_READS,
     methods::WRONG_SELF_CONVENTION,
     methods::ZST_OFFSET,
     minmax::MIN_MAX,
     mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
     module_style::MOD_MODULE_FILES,
     module_style::SELF_NAMED_MODULE_FILES,
+    multi_assignments::MULTI_ASSIGNMENTS,
     mut_key::MUTABLE_KEY_TYPE,
     mut_mut::MUT_MUT,
-    mut_mutex_lock::MUT_MUTEX_LOCK,
     mut_reference::UNNECESSARY_MUT_PASSED,
     mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
     mutex_atomic::MUTEX_ATOMIC,
     nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
     octal_escapes::OCTAL_ESCAPES,
     only_used_in_recursion::ONLY_USED_IN_RECURSION,
-    open_options::NONSENSICAL_OPEN_OPTIONS,
     operators::ABSURD_EXTREME_COMPARISONS,
     operators::ARITHMETIC,
     operators::ASSIGN_OP_PATTERN,
     partialeq_to_none::PARTIALEQ_TO_NONE,
     pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
     pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
-    path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
     pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
     precedence::PRECEDENCE,
     ptr::CMP_NULL,
     ranges::MANUAL_RANGE_CONTAINS,
     ranges::RANGE_MINUS_ONE,
     ranges::RANGE_PLUS_ONE,
-    ranges::RANGE_ZIP_WITH_LEN,
     ranges::REVERSED_EMPTY_RANGES,
     rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
     read_zero_byte_vec::READ_ZERO_BYTE_VEC,
     reference::DEREF_ADDROF,
     regex::INVALID_REGEX,
     regex::TRIVIAL_REGEX,
-    repeat_once::REPEAT_ONCE,
     return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
     returns::LET_AND_RETURN,
     returns::NEEDLESS_RETURN,
     single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
     size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
     slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
-    stable_sort_primitive::STABLE_SORT_PRIMITIVE,
     std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
     std_instead_of_core::STD_INSTEAD_OF_ALLOC,
     std_instead_of_core::STD_INSTEAD_OF_CORE,
     transmute::TRANSMUTE_PTR_TO_PTR,
     transmute::TRANSMUTE_PTR_TO_REF,
     transmute::TRANSMUTE_UNDEFINED_REPR,
+    transmute::TRANSMUTING_NULL,
     transmute::UNSOUND_COLLECTION_TRANSMUTE,
     transmute::USELESS_TRANSMUTE,
     transmute::WRONG_TRANSMUTE,
-    transmuting_null::TRANSMUTING_NULL,
     types::BORROWED_BOX,
     types::BOX_COLLECTION,
     types::LINKEDLIST,
     unicode::NON_ASCII_LITERAL,
     unicode::UNICODE_NOT_NFC,
     uninit_vec::UNINIT_VEC,
-    unit_hash::UNIT_HASH,
     unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
     unit_types::LET_UNIT_VALUE,
     unit_types::UNIT_ARG,
     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,
     unnested_or_patterns::UNNESTED_OR_PATTERNS,
     unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
     unused_async::UNUSED_ASYNC,
     unused_io_amount::UNUSED_IO_AMOUNT,
+    unused_peekable::UNUSED_PEEKABLE,
     unused_rounding::UNUSED_ROUNDING,
     unused_self::UNUSED_SELF,
     unused_unit::UNUSED_UNIT,
     useless_conversion::USELESS_CONVERSION,
     vec::USELESS_VEC,
     vec_init_then_push::VEC_INIT_THEN_PUSH,
-    vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
-    verbose_file_reads::VERBOSE_FILE_READS,
     wildcard_imports::ENUM_GLOB_USE,
     wildcard_imports::WILDCARD_IMPORTS,
+    write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
     write::PRINTLN_EMPTY_STRING,
     write::PRINT_LITERAL,
     write::PRINT_STDERR,
index 91210b23afe30c46c31d796b7b4f1e1bdcae82b5..e319e7ee72c57ac656a77dbe35cd248795788d41 100644 (file)
     LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
     LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
     LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
+    LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
+    LintId::of(methods::ITER_ON_SINGLE_ITEMS),
     LintId::of(methods::ITER_WITH_DRAIN),
+    LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
     LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
     LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
     LintId::of(mutex_atomic::MUTEX_ATOMIC),
     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),
     LintId::of(regex::TRIVIAL_REGEX),
     LintId::of(strings::STRING_LIT_AS_BYTES),
index bd7d1a15ab4ea1dad05f3b905d28f3392107d38f..584ccf55e5114fc8f008eb57a4c14bb1de4b85be 100644 (file)
@@ -4,9 +4,7 @@
 
 store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(attrs::INLINE_ALWAYS),
-    LintId::of(borrow_as_ptr::BORROW_AS_PTR),
-    LintId::of(bytecount::NAIVE_BYTECOUNT),
-    LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
+    LintId::of(casts::BORROW_AS_PTR),
     LintId::of(casts::CAST_LOSSLESS),
     LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
     LintId::of(casts::CAST_POSSIBLE_WRAP),
     LintId::of(macro_use::MACRO_USE_IMPORTS),
     LintId::of(manual_assert::MANUAL_ASSERT),
     LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
-    LintId::of(manual_ok_or::MANUAL_OK_OR),
+    LintId::of(manual_string_new::MANUAL_STRING_NEW),
     LintId::of(matches::MATCH_BOOL),
     LintId::of(matches::MATCH_ON_VEC_ITEMS),
     LintId::of(matches::MATCH_SAME_ARMS),
     LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
     LintId::of(matches::MATCH_WILD_ERR_ARM),
     LintId::of(matches::SINGLE_MATCH_ELSE),
+    LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
     LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
     LintId::of(methods::FILTER_MAP_NEXT),
     LintId::of(methods::FLAT_MAP_OPTION),
     LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
     LintId::of(methods::IMPLICIT_CLONE),
     LintId::of(methods::INEFFICIENT_TO_STRING),
+    LintId::of(methods::MANUAL_OK_OR),
     LintId::of(methods::MAP_UNWRAP_OR),
+    LintId::of(methods::NAIVE_BYTECOUNT),
+    LintId::of(methods::STABLE_SORT_PRIMITIVE),
     LintId::of(methods::UNNECESSARY_JOIN),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
     LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
@@ -85,7 +87,6 @@
     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),
index e1b90acb93c2e9ae78baa4778790830996eb5065..195ce41e31e9f6b4687633830b35d974eca6ef3c 100644 (file)
@@ -7,12 +7,14 @@
     LintId::of(escape::BOXED_LOCAL),
     LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
     LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
+    LintId::of(functions::RESULT_LARGE_ERR),
     LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
     LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
     LintId::of(loops::MANUAL_MEMCPY),
     LintId::of(loops::MISSING_SPIN_LOOP),
     LintId::of(loops::NEEDLESS_COLLECT),
     LintId::of(manual_retain::MANUAL_RETAIN),
+    LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
     LintId::of(methods::EXPECT_FUN_CALL),
     LintId::of(methods::EXTEND_WITH_DRAIN),
     LintId::of(methods::ITER_NTH),
index a7339ef272174d4ee4955b60edd66778cd032461..dd1e1e1a8e33d82088578a75adcaa49cf7db7128 100644 (file)
@@ -4,11 +4,11 @@
 
 store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
     LintId::of(as_conversions::AS_CONVERSIONS),
-    LintId::of(as_underscore::AS_UNDERSCORE),
     LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
     LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
     LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
     LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
+    LintId::of(casts::AS_UNDERSCORE),
     LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
     LintId::of(create_dir::CREATE_DIR),
     LintId::of(dbg_macro::DBG_MACRO),
@@ -30,7 +30,6 @@
     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),
     LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
     LintId::of(matches::TRY_ERR),
     LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
@@ -39,7 +38,9 @@
     LintId::of(methods::EXPECT_USED),
     LintId::of(methods::FILETYPE_IS_FILE),
     LintId::of(methods::GET_UNWRAP),
+    LintId::of(methods::MAP_ERR_IGNORE),
     LintId::of(methods::UNWRAP_USED),
+    LintId::of(methods::VERBOSE_FILE_READS),
     LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
     LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
     LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
@@ -81,7 +82,6 @@
     LintId::of(unicode::NON_ASCII_LITERAL),
     LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
     LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
-    LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
     LintId::of(write::PRINT_STDERR),
     LintId::of(write::PRINT_STDOUT),
     LintId::of(write::USE_DEBUG),
index bfa654238f130386f1332b134d6a86eefc0fe544..b5cb078e7a3ccce2fc5a8f0b7ed93b68cca24585 100644 (file)
@@ -29,7 +29,6 @@
     LintId::of(functions::DOUBLE_MUST_USE),
     LintId::of(functions::MUST_USE_UNIT),
     LintId::of(functions::RESULT_UNIT_ERR),
-    LintId::of(get_first::GET_FIRST),
     LintId::of(inherent_to_string::INHERENT_TO_STRING),
     LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
     LintId::of(len_zero::COMPARISON_TO_EMPTY),
@@ -45,7 +44,6 @@
     LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
     LintId::of(manual_bits::MANUAL_BITS),
     LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
-    LintId::of(map_clone::MAP_CLONE),
     LintId::of(match_result_ok::MATCH_RESULT_OK),
     LintId::of(matches::COLLAPSIBLE_MATCH),
     LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
@@ -61,6 +59,7 @@
     LintId::of(methods::CHARS_LAST_CMP),
     LintId::of(methods::CHARS_NEXT_CMP),
     LintId::of(methods::ERR_EXPECT),
+    LintId::of(methods::GET_FIRST),
     LintId::of(methods::INTO_ITER_ON_REF),
     LintId::of(methods::IS_DIGIT_ASCII_RADIX),
     LintId::of(methods::ITER_CLONED_COLLECT),
@@ -68,7 +67,9 @@
     LintId::of(methods::ITER_NTH_ZERO),
     LintId::of(methods::ITER_SKIP_NEXT),
     LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
+    LintId::of(methods::MAP_CLONE),
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+    LintId::of(methods::MUT_MUTEX_LOCK),
     LintId::of(methods::NEW_RET_NO_SELF),
     LintId::of(methods::OBFUSCATED_IF_ELSE),
     LintId::of(methods::OK_EXPECT),
@@ -88,7 +89,6 @@
     LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
     LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
     LintId::of(misc_early::REDUNDANT_PATTERN),
-    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
     LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
     LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
index 964992bd94fe259f64aa4cd6a99a3e05621cca44..8f131bbf98be3fecab313b2ae904e982620fd88d 100644 (file)
@@ -11,6 +11,7 @@
     LintId::of(casts::CAST_ABS_TO_UNSIGNED),
     LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
     LintId::of(casts::CAST_ENUM_TRUNCATION),
+    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
     LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
     LintId::of(drop_forget_ref::DROP_NON_DROP),
     LintId::of(drop_forget_ref::FORGET_NON_DROP),
@@ -24,6 +25,8 @@
     LintId::of(loops::MUT_RANGE_BOUND),
     LintId::of(methods::NO_EFFECT_REPLACE),
     LintId::of(methods::SUSPICIOUS_MAP),
+    LintId::of(methods::SUSPICIOUS_TO_OWNED),
+    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
@@ -32,4 +35,6 @@
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
     LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
+    LintId::of(unused_peekable::UNUSED_PEEKABLE),
+    LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
 ])
index ec5c73c135764db66e1271342e7c16d277f3b91e..dfdaf90f09f48151eda7964dd3d2a02cba705271 100644 (file)
@@ -171,7 +171,6 @@ macro_rules! declare_clippy_lint {
 mod almost_complete_letter_range;
 mod approx_const;
 mod as_conversions;
-mod as_underscore;
 mod asm_syntax;
 mod assertions_on_constants;
 mod assertions_on_result_states;
@@ -181,12 +180,8 @@ macro_rules! declare_clippy_lint {
 mod blocks_in_if_conditions;
 mod bool_assert_comparison;
 mod booleans;
-mod borrow_as_ptr;
 mod borrow_deref_ref;
-mod bytecount;
-mod bytes_count_to_len;
 mod cargo;
-mod case_sensitive_file_extension_comparisons;
 mod casts;
 mod checked_conversions;
 mod cognitive_complexity;
@@ -239,7 +234,6 @@ macro_rules! declare_clippy_lint {
 mod from_str_radix_10;
 mod functions;
 mod future_not_send;
-mod get_first;
 mod if_let_mutex;
 mod if_not_else;
 mod if_then_some_else_none;
@@ -276,12 +270,10 @@ macro_rules! declare_clippy_lint {
 mod manual_bits;
 mod manual_instant_elapsed;
 mod manual_non_exhaustive;
-mod manual_ok_or;
 mod manual_rem_euclid;
 mod manual_retain;
+mod manual_string_new;
 mod manual_strip;
-mod map_clone;
-mod map_err_ignore;
 mod map_unit_fn;
 mod match_result_ok;
 mod matches;
@@ -298,9 +290,9 @@ macro_rules! declare_clippy_lint {
 mod missing_inline;
 mod mixed_read_write_in_expression;
 mod module_style;
+mod multi_assignments;
 mod mut_key;
 mod mut_mut;
-mod mut_mutex_lock;
 mod mut_reference;
 mod mutable_debug_assertion;
 mod mutex_atomic;
@@ -325,7 +317,6 @@ macro_rules! declare_clippy_lint {
 mod nonstandard_macro_braces;
 mod octal_escapes;
 mod only_used_in_recursion;
-mod open_options;
 mod operators;
 mod option_env_unwrap;
 mod option_if_let_else;
@@ -335,7 +326,6 @@ macro_rules! declare_clippy_lint {
 mod partialeq_ne_impl;
 mod partialeq_to_none;
 mod pass_by_ref_or_value;
-mod path_buf_push_overwrite;
 mod pattern_type_mismatch;
 mod precedence;
 mod ptr;
@@ -355,7 +345,6 @@ macro_rules! declare_clippy_lint {
 mod ref_option_ref;
 mod reference;
 mod regex;
-mod repeat_once;
 mod return_self_not_must_use;
 mod returns;
 mod same_name_method;
@@ -367,7 +356,6 @@ macro_rules! declare_clippy_lint {
 mod single_component_path_imports;
 mod size_of_in_element_count;
 mod slow_vector_initialization;
-mod stable_sort_primitive;
 mod std_instead_of_core;
 mod strings;
 mod strlen_on_c_strings;
@@ -381,23 +369,21 @@ macro_rules! declare_clippy_lint {
 mod trailing_empty_array;
 mod trait_bounds;
 mod transmute;
-mod transmuting_null;
 mod types;
 mod undocumented_unsafe_blocks;
 mod unicode;
 mod uninit_vec;
-mod unit_hash;
 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;
 mod unnested_or_patterns;
 mod unsafe_removed_from_name;
 mod unused_async;
 mod unused_io_amount;
+mod unused_peekable;
 mod unused_rounding;
 mod unused_self;
 mod unused_unit;
@@ -408,8 +394,6 @@ macro_rules! declare_clippy_lint {
 mod useless_conversion;
 mod vec;
 mod vec_init_then_push;
-mod vec_resize_to_zero;
-mod verbose_file_reads;
 mod wildcard_imports;
 mod write;
 mod zero_div_zero;
@@ -597,7 +581,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
     store.register_late_pass(|| Box::new(unicode::Unicode));
     store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
-    store.register_late_pass(|| Box::new(unit_hash::UnitHash));
     store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
     store.register_late_pass(|| Box::new(strings::StringAdd));
     store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
@@ -635,8 +618,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
     store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
     store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
-    store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
-
     store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
     store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
     let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
@@ -646,7 +627,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
             msrv,
         ))
     });
-    store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
     store.register_late_pass(|| Box::new(shadow::Shadow::default()));
     store.register_late_pass(|| Box::new(unit_types::UnitTypes));
     store.register_late_pass(|| Box::new(loops::Loops));
@@ -654,7 +634,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
     store.register_late_pass(|| Box::new(entry::HashMapPass));
     store.register_late_pass(|| Box::new(minmax::MinMaxPass));
-    store.register_late_pass(|| Box::new(open_options::OpenOptions));
     store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
     store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
     store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
@@ -690,10 +669,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
     let too_many_arguments_threshold = conf.too_many_arguments_threshold;
     let too_many_lines_threshold = conf.too_many_lines_threshold;
+    let large_error_threshold = conf.large_error_threshold;
     store.register_late_pass(move || {
         Box::new(functions::Functions::new(
             too_many_arguments_threshold,
             too_many_lines_threshold,
+            large_error_threshold,
         ))
     });
     let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
@@ -720,7 +701,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     );
     store.register_late_pass(move || Box::new(pass_by_ref_or_value));
     store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
-    store.register_late_pass(|| Box::new(bytecount::ByteCount));
     store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
     store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
     store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
@@ -738,12 +718,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
     store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
     store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
-    store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
     store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
     store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
     store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
-    store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
-    store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
     store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
     let max_trait_bounds = conf.max_trait_bounds;
     store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
@@ -819,18 +796,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
     let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
     store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
-    store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
     store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
     store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
-    store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
+    store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv)));
     store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
     store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
     store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
     store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
-    store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
     store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
-    store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
     store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
     let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
     store.register_early_pass(move || {
@@ -842,10 +816,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
     store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
     store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
-    store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
-    store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
     store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
-    store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
     store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
     let disallowed_methods = conf.disallowed_methods.clone();
@@ -857,9 +828,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(strings::StringToString));
     store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
     store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
-    store.register_late_pass(|| {
-        Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
-    });
     store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
     store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
     store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
@@ -894,11 +862,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
     store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
     store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
-    store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
     store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
     store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
     store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
-    store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
+    store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
     let allow_dbg_in_tests = conf.allow_dbg_in_tests;
     store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
     let cargo_ignore_publish = conf.cargo_ignore_publish;
@@ -912,18 +879,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     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));
     store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
     store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
-    store.register_late_pass(|| Box::new(get_first::GetFirst));
     store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
     store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
     store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
     store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
-    store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
     store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
     store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
     store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
@@ -934,6 +898,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
     store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
     store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
+    store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew));
+    store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable));
+    store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
index ddaffc751880db30251f01d8c246af8c9d8568d6..6d987f393fa5ce353c2d38dae517e5cdf18f1a6d 100644 (file)
@@ -1,5 +1,6 @@
 use super::NEEDLESS_COLLECT;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
+use clippy_utils::higher;
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
@@ -184,10 +185,19 @@ struct IterFunctionVisitor<'a, 'tcx> {
 impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
     fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
         for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
+            if check_loop_kind(expr).is_some() {
+                continue;
+            }
             self.visit_block_expr(expr, hir_id);
         }
         if let Some(expr) = block.expr {
-            self.visit_block_expr(expr, None);
+            if let Some(loop_kind) = check_loop_kind(expr) {
+                if let LoopKind::Conditional(block_expr) = loop_kind {
+                    self.visit_block_expr(block_expr, None);
+                }
+            } else {
+                self.visit_block_expr(expr, None);
+            }
         }
     }
 
@@ -264,6 +274,28 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
     }
 }
 
+enum LoopKind<'tcx> {
+    Conditional(&'tcx Expr<'tcx>),
+    Loop,
+}
+
+fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
+    if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
+        return Some(LoopKind::Conditional(let_expr));
+    }
+    if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
+        return Some(LoopKind::Conditional(condition));
+    }
+    if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
+        return Some(LoopKind::Conditional(arg));
+    }
+    if let ExprKind::Loop { .. } = expr.kind {
+        return Some(LoopKind::Loop);
+    }
+
+    None
+}
+
 impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
     fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
         self.current_statement_hir_id = hir_id;
index a0ca7e6ff1e22b78f2cd4c08a8ca29c6cea71e8c..2502c8f880ddce76aa1045cfcdc815aaa315209d 100644 (file)
@@ -192,7 +192,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
     match output.kind {
         TyKind::Tup(tys) if tys.is_empty() => {
             let sugg = "remove the return type";
-            Some((sugg, "".into()))
+            Some((sugg, String::new()))
         },
         _ => {
             let sugg = "return the output of the future directly";
diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
deleted file mode 100644 (file)
index cf50043..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_lang_ctor, path_to_local_id};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::LangItem::{ResultErr, ResultOk};
-use rustc_hir::{Closure, Expr, ExprKind, PatKind};
-use rustc_lint::LintContext;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    ///
-    /// Finds patterns that reimplement `Option::ok_or`.
-    ///
-    /// ### Why is this bad?
-    ///
-    /// Concise code helps focusing on behavior instead of boilerplate.
-    ///
-    /// ### Examples
-    /// ```rust
-    /// let foo: Option<i32> = None;
-    /// foo.map_or(Err("error"), |v| Ok(v));
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// let foo: Option<i32> = None;
-    /// foo.ok_or("error");
-    /// ```
-    #[clippy::version = "1.49.0"]
-    pub MANUAL_OK_OR,
-    pedantic,
-    "finds patterns that can be encoded more concisely with `Option::ok_or`"
-}
-
-declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
-
-impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
-        if in_external_macro(cx.sess(), scrutinee.span) {
-            return;
-        }
-
-        if_chain! {
-            if let ExprKind::MethodCall(method_segment, [receiver, or_expr, map_expr], _) = scrutinee.kind;
-            if method_segment.ident.name == sym!(map_or);
-            let ty = cx.typeck_results().expr_ty(receiver);
-            if is_type_diagnostic_item(cx, ty, sym::Option);
-            if is_ok_wrapping(cx, map_expr);
-            if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
-            if is_lang_ctor(cx, err_path, ResultErr);
-            if let Some(method_receiver_snippet) = snippet_opt(cx, receiver.span);
-            if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
-            if let Some(indent) = indent_of(cx, scrutinee.span);
-            then {
-                let reindented_err_arg_snippet =
-                    reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
-                span_lint_and_sugg(
-                    cx,
-                    MANUAL_OK_OR,
-                    scrutinee.span,
-                    "this pattern reimplements `Option::ok_or`",
-                    "replace with",
-                    format!(
-                        "{}.ok_or({})",
-                        method_receiver_snippet,
-                        reindented_err_arg_snippet
-                    ),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-    }
-}
-
-fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
-    if let ExprKind::Path(ref qpath) = map_expr.kind {
-        if is_lang_ctor(cx, qpath, ResultOk) {
-            return true;
-        }
-    }
-    if_chain! {
-        if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
-        let body = cx.tcx.hir().body(body);
-        if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
-        if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
-        if is_lang_ctor(cx, ok_path, ResultOk);
-        then { path_to_local_id(ok_arg, param_id) } else { false }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
new file mode 100644 (file)
index 0000000..a90eaa8
--- /dev/null
@@ -0,0 +1,140 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability::MachineApplicable;
+use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, symbol, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`,
+    /// `String::from("")` and others.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Different ways of creating an empty string makes your code less standardized, which can
+    /// be confusing.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a = "".to_string();
+    /// let b: String = "".into();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let a = String::new();
+    /// let b = String::new();
+    /// ```
+    #[clippy::version = "1.65.0"]
+    pub MANUAL_STRING_NEW,
+    pedantic,
+    "empty String is being created manually"
+}
+declare_lint_pass!(ManualStringNew => [MANUAL_STRING_NEW]);
+
+impl LateLintPass<'_> for ManualStringNew {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if expr.span.from_expansion() {
+            return;
+        }
+
+        let ty = cx.typeck_results().expr_ty(expr);
+        match ty.kind() {
+            ty::Adt(adt_def, _) if adt_def.is_struct() => {
+                if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) {
+                    return;
+                }
+            },
+            _ => return,
+        }
+
+        match expr.kind {
+            ExprKind::Call(func, args) => {
+                parse_call(cx, expr.span, func, args);
+            },
+            ExprKind::MethodCall(path_segment, args, _) => {
+                parse_method_call(cx, expr.span, path_segment, args);
+            },
+            _ => (),
+        }
+    }
+}
+
+/// Checks if an expression's kind corresponds to an empty &str.
+fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
+    if  let ExprKind::Lit(lit) = expr_kind &&
+        let LitKind::Str(value, _) = lit.node &&
+        value == symbol::kw::Empty
+    {
+        return true;
+    }
+
+    false
+}
+
+fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
+    span_lint_and_sugg(
+        cx,
+        MANUAL_STRING_NEW,
+        span,
+        "empty String is being created manually",
+        "consider using",
+        "String::new()".into(),
+        MachineApplicable,
+    );
+}
+
+/// Tries to parse an expression as a method call, emitting the warning if necessary.
+fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) {
+    if args.is_empty() {
+        // When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg.
+        return;
+    }
+
+    let ident = path_segment.ident.as_str();
+    let method_arg_kind = &args[0].kind;
+    if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
+        warn_then_suggest(cx, span);
+    } else if let ExprKind::Call(func, args) = method_arg_kind {
+        // If our first argument is a function call itself, it could be an `unwrap`-like function.
+        // E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
+        parse_call(cx, span, func, args);
+    }
+}
+
+/// Tries to parse an expression as a function call, emitting the warning if necessary.
+fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
+    if args.len() != 1 {
+        return;
+    }
+
+    let arg_kind = &args[0].kind;
+    if let ExprKind::Path(qpath) = &func.kind {
+        if let QPath::TypeRelative(_, _) = qpath {
+            // String::from(...) or String::try_from(...)
+            if  let QPath::TypeRelative(ty, path_seg) = qpath &&
+                [sym::from, sym::try_from].contains(&path_seg.ident.name) &&
+                let TyKind::Path(qpath) = &ty.kind &&
+                let QPath::Resolved(_, path) = qpath &&
+                let [path_seg] = path.segments &&
+                path_seg.ident.name == sym::String &&
+                is_expr_kind_empty_str(arg_kind)
+            {
+                warn_then_suggest(cx, span);
+            }
+        } else if let QPath::Resolved(_, path) = qpath {
+            // From::from(...) or TryFrom::try_from(...)
+            if  let [path_seg1, path_seg2] = path.segments &&
+                is_expr_kind_empty_str(arg_kind) && (
+                    (path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
+                    (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
+                )
+            {
+                warn_then_suggest(cx, span);
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs
deleted file mode 100644 (file)
index 95c312f..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
-use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::Mutability;
-use rustc_middle::ty;
-use rustc_middle::ty::adjustment::Adjust;
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for usage of `map(|x| x.clone())` or
-    /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
-    /// and suggests `cloned()` or `copied()` instead
-    ///
-    /// ### Why is this bad?
-    /// Readability, this can be written more concisely
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = vec![42, 43];
-    /// let y = x.iter();
-    /// let z = y.map(|i| *i);
-    /// ```
-    ///
-    /// The correct use would be:
-    ///
-    /// ```rust
-    /// let x = vec![42, 43];
-    /// let y = x.iter();
-    /// let z = y.cloned();
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub MAP_CLONE,
-    style,
-    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
-}
-
-pub struct MapClone {
-    msrv: Option<RustcVersion>,
-}
-
-impl_lint_pass!(MapClone => [MAP_CLONE]);
-
-impl MapClone {
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for MapClone {
-    fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
-        if e.span.from_expansion() {
-            return;
-        }
-
-        if_chain! {
-            if let hir::ExprKind::MethodCall(method, args, _) = e.kind;
-            if args.len() == 2;
-            if method.ident.name == sym::map;
-            let ty = cx.typeck_results().expr_ty(&args[0]);
-            if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator);
-            if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = args[1].kind;
-            then {
-                let closure_body = cx.tcx.hir().body(body);
-                let closure_expr = peel_blocks(&closure_body.value);
-                match closure_body.params[0].pat.kind {
-                    hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
-                        hir::BindingAnnotation::Unannotated, .., name, None
-                    ) = inner.kind {
-                        if ident_eq(name, closure_expr) {
-                            self.lint_explicit_closure(cx, e.span, args[0].span, true);
-                        }
-                    },
-                    hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
-                        match closure_expr.kind {
-                            hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
-                                if ident_eq(name, inner) {
-                                    if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
-                                        self.lint_explicit_closure(cx, e.span, args[0].span, true);
-                                    }
-                                }
-                            },
-                            hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
-                                if ident_eq(name, obj) && method.ident.name == sym::clone;
-                                if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
-                                if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
-                                if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
-                                // no autoderefs
-                                if !cx.typeck_results().expr_adjustments(obj).iter()
-                                    .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
-                                then {
-                                    let obj_ty = cx.typeck_results().expr_ty(obj);
-                                    if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
-                                        if matches!(mutability, Mutability::Not) {
-                                            let copy = is_copy(cx, *ty);
-                                            self.lint_explicit_closure(cx, e.span, args[0].span, copy);
-                                        }
-                                    } else {
-                                        lint_needless_cloning(cx, e.span, args[0].span);
-                                    }
-                                }
-                            },
-                            _ => {},
-                        }
-                    },
-                    _ => {},
-                }
-            }
-        }
-    }
-
-    extract_msrv_attr!(LateContext);
-}
-
-fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
-    if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
-        path.segments.len() == 1 && path.segments[0].ident == name
-    } else {
-        false
-    }
-}
-
-fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
-    span_lint_and_sugg(
-        cx,
-        MAP_CLONE,
-        root.trim_start(receiver).unwrap(),
-        "you are needlessly cloning iterator elements",
-        "remove the `map` call",
-        String::new(),
-        Applicability::MachineApplicable,
-    );
-}
-
-impl MapClone {
-    fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
-        let mut applicability = Applicability::MachineApplicable;
-
-        let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) {
-            ("you are using an explicit closure for copying elements", "copied")
-        } else {
-            ("you are using an explicit closure for cloning elements", "cloned")
-        };
-
-        span_lint_and_sugg(
-            cx,
-            MAP_CLONE,
-            replace,
-            message,
-            &format!("consider calling the dedicated `{}` method", sugg_method),
-            format!(
-                "{}.{}()",
-                snippet_with_applicability(cx, root, "..", &mut applicability),
-                sugg_method,
-            ),
-            applicability,
-        );
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs
deleted file mode 100644 (file)
index 1e54244..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for instances of `map_err(|_| Some::Enum)`
-    ///
-    /// ### Why is this bad?
-    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
-    ///
-    /// ### Example
-    /// Before:
-    /// ```rust
-    /// use std::fmt;
-    ///
-    /// #[derive(Debug)]
-    /// enum Error {
-    ///     Indivisible,
-    ///     Remainder(u8),
-    /// }
-    ///
-    /// impl fmt::Display for Error {
-    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    ///         match self {
-    ///             Error::Indivisible => write!(f, "could not divide input by three"),
-    ///             Error::Remainder(remainder) => write!(
-    ///                 f,
-    ///                 "input is not divisible by three, remainder = {}",
-    ///                 remainder
-    ///             ),
-    ///         }
-    ///     }
-    /// }
-    ///
-    /// impl std::error::Error for Error {}
-    ///
-    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
-    ///     input
-    ///         .parse::<i32>()
-    ///         .map_err(|_| Error::Indivisible)
-    ///         .map(|v| v % 3)
-    ///         .and_then(|remainder| {
-    ///             if remainder == 0 {
-    ///                 Ok(())
-    ///             } else {
-    ///                 Err(Error::Remainder(remainder as u8))
-    ///             }
-    ///         })
-    /// }
-    ///  ```
-    ///
-    ///  After:
-    ///  ```rust
-    /// use std::{fmt, num::ParseIntError};
-    ///
-    /// #[derive(Debug)]
-    /// enum Error {
-    ///     Indivisible(ParseIntError),
-    ///     Remainder(u8),
-    /// }
-    ///
-    /// impl fmt::Display for Error {
-    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    ///         match self {
-    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
-    ///             Error::Remainder(remainder) => write!(
-    ///                 f,
-    ///                 "input is not divisible by three, remainder = {}",
-    ///                 remainder
-    ///             ),
-    ///         }
-    ///     }
-    /// }
-    ///
-    /// impl std::error::Error for Error {
-    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-    ///         match self {
-    ///             Error::Indivisible(source) => Some(source),
-    ///             _ => None,
-    ///         }
-    ///     }
-    /// }
-    ///
-    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
-    ///     input
-    ///         .parse::<i32>()
-    ///         .map_err(Error::Indivisible)
-    ///         .map(|v| v % 3)
-    ///         .and_then(|remainder| {
-    ///             if remainder == 0 {
-    ///                 Ok(())
-    ///             } else {
-    ///                 Err(Error::Remainder(remainder as u8))
-    ///             }
-    ///         })
-    /// }
-    /// ```
-    #[clippy::version = "1.48.0"]
-    pub MAP_ERR_IGNORE,
-    restriction,
-    "`map_err` should not ignore the original error"
-}
-
-declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
-
-impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
-    // do not try to lint if this is from a macro or desugaring
-    fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
-        if e.span.from_expansion() {
-            return;
-        }
-
-        // check if this is a method call (e.g. x.foo())
-        if let ExprKind::MethodCall(method, [_, arg], _) = e.kind {
-            // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
-            // Enum::Variant[2]))
-            if method.ident.name == sym!(map_err) {
-                // make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span
-                // fields
-                if let ExprKind::Closure(&Closure {
-                    capture_clause,
-                    body,
-                    fn_decl_span,
-                    ..
-                }) = arg.kind
-                {
-                    // check if this is by Reference (meaning there's no move statement)
-                    if capture_clause == CaptureBy::Ref {
-                        // Get the closure body to check the parameters and values
-                        let closure_body = cx.tcx.hir().body(body);
-                        // make sure there's only one parameter (`|_|`)
-                        if closure_body.params.len() == 1 {
-                            // make sure that parameter is the wild token (`_`)
-                            if let PatKind::Wild = closure_body.params[0].pat.kind {
-                                // span the area of the closure capture and warn that the
-                                // original error will be thrown away
-                                span_lint_and_help(
-                                    cx,
-                                    MAP_ERR_IGNORE,
-                                    fn_decl_span,
-                                    "`map_err(|_|...` wildcard pattern discards the original error",
-                                    None,
-                                    "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
-                                );
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
index 0da4833f1dfe0c9dfc5694874ab0cad257c30487..34cc082687ec20426d6f36ae4c1aa11a4530458a 100644 (file)
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_wild;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::span_contains_comment;
 use rustc_ast::{Attribute, LitKind};
 use rustc_errors::Applicability;
 use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
-use rustc_lint::LateContext;
+use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty;
 use rustc_span::source_map::Spanned;
 
@@ -76,6 +77,7 @@ fn find_matches_sugg<'a, 'b, I>(
         >,
 {
     if_chain! {
+        if !span_contains_comment(cx.sess().source_map(), expr.span);
         if iter.len() >= 2;
         if cx.typeck_results().expr_ty(expr).is_bool();
         if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
index fa19cddd35ec7afff4258f68eb901967e01b411b..6f037339ec75870552a3d2867f447a012364d143 100644 (file)
@@ -8,7 +8,7 @@
 };
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
+use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath};
 use rustc_lint::LateContext;
 use rustc_span::sym;
 use rustc_typeck::hir_ty_to_ty;
@@ -65,8 +65,26 @@ pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let:
 fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
     for arm in arms {
         let arm_expr = peel_blocks_with_stmt(arm.body);
+
+        if let Some(guard_expr) = &arm.guard {
+            match guard_expr {
+                // gives up if `pat if expr` can have side effects
+                Guard::If(if_cond) => {
+                    if if_cond.can_have_side_effects() {
+                        return false;
+                    }
+                },
+                // gives up `pat if let ...` arm
+                Guard::IfLet(_) => {
+                    return false;
+                },
+            };
+        }
+
         if let PatKind::Wild = arm.pat.kind {
-            return eq_expr_value(cx, match_expr, strip_return(arm_expr));
+            if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) {
+                return false;
+            }
         } else if !pat_same_as_expr(arm.pat, arm_expr) {
             return false;
         }
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
new file mode 100644 (file)
index 0000000..6a7c63d
--- /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::match_type;
+use clippy_utils::visitors::is_local_used;
+use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, UintTy};
+use rustc_span::sym;
+
+use super::NAIVE_BYTECOUNT;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    filter_recv: &'tcx Expr<'_>,
+    filter_arg: &'tcx Expr<'_>,
+) {
+    if_chain! {
+        if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
+        let body = cx.tcx.hir().body(body);
+        if let [param] = body.params;
+        if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
+        if let ExprKind::Binary(ref op, l, r) = body.value.kind;
+        if op.node == BinOpKind::Eq;
+        if match_type(cx,
+                    cx.typeck_results().expr_ty(filter_recv).peel_refs(),
+                    &paths::SLICE_ITER);
+        let operand_is_arg = |expr| {
+            let expr = peel_ref_operators(cx, peel_blocks(expr));
+            path_to_local_id(expr, arg_id)
+        };
+        let needle = if operand_is_arg(l) {
+            r
+        } else if operand_is_arg(r) {
+            l
+        } else {
+            return;
+        };
+        if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
+        if !is_local_used(cx, needle, arg_id);
+        then {
+            let haystack = if let ExprKind::MethodCall(path, args, _) =
+                    filter_recv.kind {
+                let p = path.ident.name;
+                if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
+                    &args[0]
+                } else {
+                    filter_recv
+                }
+            } else {
+                filter_recv
+            };
+            let mut applicability = Applicability::MaybeIncorrect;
+            span_lint_and_sugg(
+                cx,
+                NAIVE_BYTECOUNT,
+                expr.span,
+                "you appear to be counting bytes the naive way",
+                "consider using the bytecount crate",
+                format!("bytecount::count({}, {})",
+                        snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
+                        snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
+                applicability,
+            );
+        }
+    };
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs
new file mode 100644 (file)
index 0000000..fcfc25b
--- /dev/null
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::BYTES_COUNT_TO_LEN;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    count_recv: &'tcx hir::Expr<'_>,
+    bytes_recv: &'tcx hir::Expr<'_>,
+) {
+    if_chain! {
+        if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
+        if cx.tcx.type_of(impl_id).is_str();
+        let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
+        if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
+        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, bytes_recv.span, "..", &mut applicability)),
+                applicability
+            );
+        }
+    };
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
new file mode 100644 (file)
index 0000000..b3c2c7c
--- /dev/null
@@ -0,0 +1,41 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::{source_map::Spanned, symbol::sym, Span};
+
+use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    call_span: Span,
+    recv: &'tcx Expr<'_>,
+    arg: &'tcx Expr<'_>,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if cx.tcx.type_of(impl_id).is_str();
+        if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
+        if (2..=6).contains(&ext_literal.as_str().len());
+        let ext_str = ext_literal.as_str();
+        if ext_str.starts_with('.');
+        if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
+            || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
+        let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
+        if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
+        then {
+            span_lint_and_help(
+                cx,
+                CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+                call_span,
+                "case-sensitive file extension comparison",
+                None,
+                "consider using a case-insensitive comparison instead",
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
new file mode 100644 (file)
index 0000000..561033b
--- /dev/null
@@ -0,0 +1,96 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{eq_expr_value, get_parent_expr};
+use core::ops::ControlFlow;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use std::collections::VecDeque;
+
+use super::method_call;
+use super::COLLAPSIBLE_STR_REPLACE;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    from: &'tcx hir::Expr<'tcx>,
+    to: &'tcx hir::Expr<'tcx>,
+) {
+    let replace_methods = collect_replace_calls(cx, expr, to);
+    if replace_methods.methods.len() > 1 {
+        let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind();
+        // If the parent node's `to` argument is the same as the `to` argument
+        // of the last replace call in the current chain, don't lint as it was already linted
+        if let Some(parent) = get_parent_expr(cx, expr)
+            && let Some(("replace", [_, current_from, current_to], _)) = method_call(parent)
+            && eq_expr_value(cx, to, current_to)
+            && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
+        {
+            return;
+        }
+
+        check_consecutive_replace_calls(cx, expr, &replace_methods, to);
+    }
+}
+
+struct ReplaceMethods<'tcx> {
+    methods: VecDeque<&'tcx hir::Expr<'tcx>>,
+    from_args: VecDeque<&'tcx hir::Expr<'tcx>>,
+}
+
+fn collect_replace_calls<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    to_arg: &'tcx hir::Expr<'tcx>,
+) -> ReplaceMethods<'tcx> {
+    let mut methods = VecDeque::new();
+    let mut from_args = VecDeque::new();
+
+    let _: Option<()> = for_each_expr(expr, |e| {
+        if let Some(("replace", [_, from, to], _)) = method_call(e) {
+            if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
+                methods.push_front(e);
+                from_args.push_front(from);
+                ControlFlow::Continue(())
+            } else {
+                ControlFlow::BREAK
+            }
+        } else {
+            ControlFlow::Continue(())
+        }
+    });
+
+    ReplaceMethods { methods, from_args }
+}
+
+/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint.
+fn check_consecutive_replace_calls<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    replace_methods: &ReplaceMethods<'tcx>,
+    to_arg: &'tcx hir::Expr<'tcx>,
+) {
+    let from_args = &replace_methods.from_args;
+    let from_arg_reprs: Vec<String> = from_args
+        .iter()
+        .map(|from_arg| snippet(cx, from_arg.span, "..").to_string())
+        .collect();
+    let app = Applicability::MachineApplicable;
+    let earliest_replace_call = replace_methods.methods.front().unwrap();
+    if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) {
+        span_lint_and_sugg(
+            cx,
+            COLLAPSIBLE_STR_REPLACE,
+            expr.span.with_lo(span_lo.lo()),
+            "used consecutive `str::replace` call",
+            "replace with",
+            format!(
+                "replace([{}], {})",
+                from_arg_reprs.join(", "),
+                snippet(cx, to_arg.span, ".."),
+            ),
+            app,
+        );
+    }
+}
index 5ef08ca6290bae6adbc15a6887edbd755c040711..d59fefa1ddc0ebf68104498532f8da1f1c8c0e26 100644 (file)
@@ -7,18 +7,26 @@
 
 use super::EXPECT_USED;
 
-/// lint use of `expect()` for `Option`s and `Result`s
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
+/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    recv: &hir::Expr<'_>,
+    is_err: bool,
+    allow_expect_in_tests: bool,
+) {
     let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 
-    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
+    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
         Some((EXPECT_USED, "an Option", "None", ""))
     } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
-        Some((EXPECT_USED, "a Result", "Err", "an "))
+        Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
     } else {
         None
     };
 
+    let method = if is_err { "expect_err" } else { "expect" };
+
     if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
         return;
     }
@@ -28,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
             cx,
             lint,
             expr.span,
-            &format!("used `expect()` on `{kind}` value"),
+            &format!("used `{method}()` on `{kind}` value"),
             None,
             &format!("if this value is {none_prefix}`{none_value}`, it will panic"),
         );
diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs
new file mode 100644 (file)
index 0000000..4de77de
--- /dev/null
@@ -0,0 +1,39 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_slice_of_primitives;
+use clippy_utils::source::snippet_with_applicability;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::source_map::Spanned;
+
+use super::GET_FIRST;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    recv: &'tcx hir::Expr<'_>,
+    arg: &'tcx hir::Expr<'_>,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if cx.tcx.type_of(impl_id).is_slice();
+        if let Some(_) = is_slice_of_primitives(cx, recv);
+        if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
+        then {
+            let mut app = Applicability::MachineApplicable;
+            let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
+            span_lint_and_sugg(
+                cx,
+                GET_FIRST,
+                expr.span,
+                &format!("accessing first element with `{0}.get(0)`", slice_name),
+                "try",
+                format!("{}.first()", slice_name),
+                app,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
new file mode 100644 (file)
index 0000000..cea7b0d
--- /dev/null
@@ -0,0 +1,107 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
+
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{Expr, ExprKind, Node};
+use rustc_lint::LateContext;
+
+use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
+
+enum IterType {
+    Iter,
+    IterMut,
+    IntoIter,
+}
+
+impl IterType {
+    fn ref_prefix(&self) -> &'static str {
+        match self {
+            Self::Iter => "&",
+            Self::IterMut => "&mut ",
+            Self::IntoIter => "",
+        }
+    }
+}
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
+    let item = match &recv.kind {
+        ExprKind::Array(v) if v.len() <= 1 => v.first(),
+        ExprKind::Path(p) => {
+            if is_lang_ctor(cx, p, OptionNone) {
+                None
+            } else {
+                return;
+            }
+        },
+        ExprKind::Call(f, some_args) if some_args.len() == 1 => {
+            if let ExprKind::Path(p) = &f.kind {
+                if is_lang_ctor(cx, p, OptionSome) {
+                    Some(&some_args[0])
+                } else {
+                    return;
+                }
+            } else {
+                return;
+            }
+        },
+        _ => return,
+    };
+    let iter_type = match method_name {
+        "iter" => IterType::Iter,
+        "iter_mut" => IterType::IterMut,
+        "into_iter" => IterType::IntoIter,
+        _ => return,
+    };
+
+    let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
+        Some((Node::Expr(parent), child_id)) => match parent.kind {
+            ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
+            ExprKind::If(_, _, _)
+            | ExprKind::Match(_, _, _)
+            | ExprKind::Closure(_)
+            | ExprKind::Ret(_)
+            | ExprKind::Break(_, _) => true,
+            _ => false,
+        },
+        Some((Node::Stmt(_) | Node::Local(_), _)) => false,
+        _ => true,
+    };
+
+    if is_unified {
+        return;
+    }
+
+    if let Some(i) = item {
+        let sugg = format!(
+            "{}::iter::once({}{})",
+            if is_no_std_crate(cx) { "core" } else { "std" },
+            iter_type.ref_prefix(),
+            snippet(cx, i.span, "...")
+        );
+        span_lint_and_sugg(
+            cx,
+            ITER_ON_SINGLE_ITEMS,
+            expr.span,
+            &format!("`{method_name}` call on a collection with only one item"),
+            "try",
+            sugg,
+            Applicability::MaybeIncorrect,
+        );
+    } else {
+        span_lint_and_sugg(
+            cx,
+            ITER_ON_EMPTY_COLLECTIONS,
+            expr.span,
+            &format!("`{method_name}` call on an empty collection"),
+            "try",
+            if is_no_std_crate(cx) {
+                "core::iter::empty()".to_string()
+            } else {
+                "std::iter::empty()".to_string()
+            },
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
new file mode 100644 (file)
index 0000000..ffd2f4a
--- /dev/null
@@ -0,0 +1,64 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_lang_ctor, path_to_local_id};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::{ResultErr, ResultOk};
+use rustc_hir::{Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::MANUAL_OK_OR;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    recv: &'tcx Expr<'_>,
+    or_expr: &'tcx Expr<'_>,
+    map_expr: &'tcx Expr<'_>,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
+        if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
+        if is_lang_ctor(cx, err_path, ResultErr);
+        if is_ok_wrapping(cx, map_expr);
+        if let Some(recv_snippet) = snippet_opt(cx, recv.span);
+        if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
+        if let Some(indent) = indent_of(cx, expr.span);
+        then {
+            let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
+            span_lint_and_sugg(
+                cx,
+                MANUAL_OK_OR,
+                expr.span,
+                "this pattern reimplements `Option::ok_or`",
+                "replace with",
+                format!(
+                    "{}.ok_or({})",
+                    recv_snippet,
+                    reindented_err_arg_snippet
+                ),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
+    if let ExprKind::Path(ref qpath) = map_expr.kind {
+        if is_lang_ctor(cx, qpath, ResultOk) {
+            return true;
+        }
+    }
+    if_chain! {
+        if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
+        let body = cx.tcx.hir().body(body);
+        if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
+        if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
+        if is_lang_ctor(cx, ok_path, ResultOk);
+        then { path_to_local_id(ok_arg, param_id) } else { false }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
new file mode 100644 (file)
index 0000000..ffedda9
--- /dev/null
@@ -0,0 +1,122 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
+use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::mir::Mutability;
+use rustc_middle::ty;
+use rustc_middle::ty::adjustment::Adjust;
+use rustc_semver::RustcVersion;
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Span};
+
+use super::MAP_CLONE;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'_>,
+    e: &hir::Expr<'_>,
+    recv: &hir::Expr<'_>,
+    arg: &'tcx hir::Expr<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
+        if cx.tcx.impl_of_method(method_id)
+            .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
+            || is_diag_trait_item(cx, method_id, sym::Iterator);
+        if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
+        then {
+            let closure_body = cx.tcx.hir().body(body);
+            let closure_expr = peel_blocks(&closure_body.value);
+            match closure_body.params[0].pat.kind {
+                hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
+                    hir::BindingAnnotation::Unannotated, .., name, None
+                ) = inner.kind {
+                    if ident_eq(name, closure_expr) {
+                        lint_explicit_closure(cx, e.span, recv.span, true, msrv);
+                    }
+                },
+                hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
+                    match closure_expr.kind {
+                        hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
+                            if ident_eq(name, inner) {
+                                if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
+                                    lint_explicit_closure(cx, e.span, recv.span, true, msrv);
+                                }
+                            }
+                        },
+                        hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
+                            if ident_eq(name, obj) && method.ident.name == sym::clone;
+                            if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
+                            if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
+                            if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
+                            // no autoderefs
+                            if !cx.typeck_results().expr_adjustments(obj).iter()
+                                .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
+                            then {
+                                let obj_ty = cx.typeck_results().expr_ty(obj);
+                                if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
+                                    if matches!(mutability, Mutability::Not) {
+                                        let copy = is_copy(cx, *ty);
+                                        lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
+                                    }
+                                } else {
+                                    lint_needless_cloning(cx, e.span, recv.span);
+                                }
+                            }
+                        },
+                        _ => {},
+                    }
+                },
+                _ => {},
+            }
+        }
+    }
+}
+
+fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
+    if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
+        path.segments.len() == 1 && path.segments[0].ident == name
+    } else {
+        false
+    }
+}
+
+fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
+    span_lint_and_sugg(
+        cx,
+        MAP_CLONE,
+        root.trim_start(receiver).unwrap(),
+        "you are needlessly cloning iterator elements",
+        "remove the `map` call",
+        String::new(),
+        Applicability::MachineApplicable,
+    );
+}
+
+fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
+    let mut applicability = Applicability::MachineApplicable;
+
+    let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+        ("you are using an explicit closure for copying elements", "copied")
+    } else {
+        ("you are using an explicit closure for cloning elements", "cloned")
+    };
+
+    span_lint_and_sugg(
+        cx,
+        MAP_CLONE,
+        replace,
+        message,
+        &format!("consider calling the dedicated `{}` method", sugg_method),
+        format!(
+            "{}.{}()",
+            snippet_with_applicability(cx, root, "..", &mut applicability),
+            sugg_method,
+        ),
+        applicability,
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
new file mode 100644 (file)
index 0000000..1fb6617
--- /dev/null
@@ -0,0 +1,34 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::MAP_ERR_IGNORE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
+    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+        && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
+        && let ExprKind::Closure(&Closure {
+            capture_clause: CaptureBy::Ref,
+            body,
+            fn_decl_span,
+            ..
+        }) = arg.kind
+        && let closure_body = cx.tcx.hir().body(body)
+        && let [param] = closure_body.params
+        && let PatKind::Wild = param.pat.kind
+    {
+        // span the area of the closure capture and warn that the
+        // original error will be thrown away
+        span_lint_and_help(
+            cx,
+            MAP_ERR_IGNORE,
+            fn_decl_span,
+            "`map_err(|_|...` wildcard pattern discards the original error",
+            None,
+            "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
+        );
+    }
+}
index 5ac6b09f0aa27bff22fa87c05292791f0a13f1b0..a0d190a58aff9a7f10478600f0d8ddab1b3b2840 100644 (file)
@@ -1,5 +1,8 @@
 mod bind_instead_of_map;
+mod bytecount;
+mod bytes_count_to_len;
 mod bytes_nth;
+mod case_sensitive_file_extension_comparisons;
 mod chars_cmp;
 mod chars_cmp_with_unwrap;
 mod chars_last_cmp;
@@ -9,6 +12,7 @@
 mod clone_on_copy;
 mod clone_on_ref_ptr;
 mod cloned_instead_of_copied;
+mod collapsible_str_replace;
 mod err_expect;
 mod expect_fun_call;
 mod expect_used;
@@ -21,6 +25,7 @@
 mod flat_map_identity;
 mod flat_map_option;
 mod from_iter_instead_of_collect;
+mod get_first;
 mod get_last_with_len;
 mod get_unwrap;
 mod implicit_clone;
 mod iter_next_slice;
 mod iter_nth;
 mod iter_nth_zero;
+mod iter_on_single_or_empty_collections;
 mod iter_overeager_cloned;
 mod iter_skip_next;
 mod iter_with_drain;
 mod iterator_step_by_zero;
+mod manual_ok_or;
 mod manual_saturating_arithmetic;
 mod manual_str_repeat;
+mod map_clone;
 mod map_collect_result_unit;
+mod map_err_ignore;
 mod map_flatten;
 mod map_identity;
 mod map_unwrap_or;
+mod mut_mutex_lock;
 mod needless_option_as_deref;
 mod needless_option_take;
 mod no_effect_replace;
 mod obfuscated_if_else;
 mod ok_expect;
+mod open_options;
 mod option_as_ref_deref;
 mod option_map_or_none;
 mod option_map_unwrap_or;
 mod or_fun_call;
 mod or_then_unwrap;
+mod path_buf_push_overwrite;
+mod range_zip_with_len;
+mod repeat_once;
 mod search_is_some;
 mod single_char_add_str;
 mod single_char_insert_string;
 mod single_char_pattern;
 mod single_char_push_string;
 mod skip_while_next;
+mod stable_sort_primitive;
 mod str_splitn;
 mod string_extend_chars;
 mod suspicious_map;
 mod suspicious_splitn;
+mod suspicious_to_owned;
 mod uninit_assumed_init;
+mod unit_hash;
 mod unnecessary_filter_map;
 mod unnecessary_fold;
 mod unnecessary_iter_cloned;
 mod unnecessary_join;
 mod unnecessary_lazy_eval;
+mod unnecessary_sort_by;
 mod unnecessary_to_owned;
 mod unwrap_or_else_default;
 mod unwrap_used;
 mod useless_asref;
 mod utils;
+mod vec_resize_to_zero;
+mod verbose_file_reads;
 mod wrong_self_convention;
 mod zst_offset;
 
 use bind_instead_of_map::BindInsteadOfMap;
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
-use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
+use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{
+    contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty,
+};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
     "used `cloned` where `copied` could be used instead"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for consecutive calls to `str::replace` (2 or more)
+    /// that can be collapsed into a single call.
+    ///
+    /// ### Why is this bad?
+    /// Consecutive `str::replace` calls scan the string multiple times
+    /// with repetitive code.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let hello = "hesuo worpd"
+    ///     .replace('s', "l")
+    ///     .replace("u", "l")
+    ///     .replace('p', "l");
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l");
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub COLLAPSIBLE_STR_REPLACE,
+    perf,
+    "collapse consecutive calls to str::replace (2 or more) into a single call"
+}
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `.unwrap()` calls on `Option`s and on `Result`s.
+    /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
     ///
     /// ### Why is this bad?
     /// It is better to handle the `None` or `Err` case,
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `.expect()` calls on `Option`s and `Result`s.
+    /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
     ///
     /// ### Why is this bad?
     /// Usually it is better to handle the `None` or `Err` case.
     "replace `.iter().count()` with `.len()`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
+    ///
+    /// ### Why is this bad?
+    /// Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
+    /// itself, without taking ownership of the `Cow` contents (i.e.
+    /// it's equivalent to calling `Cow::clone`).
+    /// The similarly named `into_owned` method, on the other hand,
+    /// clones the `Cow` contents, effectively turning any `Cow::Borrowed`
+    /// into a `Cow::Owned`.
+    ///
+    /// Given the potential ambiguity, consider replacing `to_owned`
+    /// with `clone` for better readability or, if getting a `Cow::Owned`
+    /// was the original intent, using `into_owned` instead.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::borrow::Cow;
+    /// let s = "Hello world!";
+    /// let cow = Cow::Borrowed(s);
+    ///
+    /// let data = cow.to_owned();
+    /// assert!(matches!(data, Cow::Borrowed(_)))
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::borrow::Cow;
+    /// let s = "Hello world!";
+    /// let cow = Cow::Borrowed(s);
+    ///
+    /// let data = cow.clone();
+    /// assert!(matches!(data, Cow::Borrowed(_)))
+    /// ```
+    /// or
+    /// ```rust
+    /// # use std::borrow::Cow;
+    /// let s = "Hello world!";
+    /// let cow = Cow::Borrowed(s);
+    ///
+    /// let data = cow.into_owned();
+    /// assert!(matches!(data, String))
+    /// ```
+    #[clippy::version = "1.65.0"]
+    pub SUSPICIOUS_TO_OWNED,
+    suspicious,
+    "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
+}
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for calls to [`splitn`]
     /// "1234".replace("12", "12");
     /// "1234".replacen("12", "12", 1);
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub NO_EFFECT_REPLACE,
     suspicious,
     "replace with no effect"
     more clearly with `if .. else ..`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
+    ///
+    /// ### Why is this bad?
+    ///
+    /// It is simpler to use the once function from the standard library:
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let a = [123].iter();
+    /// let b = Some(123).into_iter();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::iter;
+    /// let a = iter::once(&123);
+    /// let b = iter::once(123);
+    /// ```
+    ///
+    /// ### Known problems
+    ///
+    /// The type of the resulting iterator might become incompatible with its usage
+    #[clippy::version = "1.64.0"]
+    pub ITER_ON_SINGLE_ITEMS,
+    nursery,
+    "Iterator for array of length 1"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
+    ///
+    /// ### Why is this bad?
+    ///
+    /// It is simpler to use the empty function from the standard library:
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// use std::{slice, option};
+    /// let a: slice::Iter<i32> = [].iter();
+    /// let f: option::IntoIter<i32> = None.into_iter();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::iter;
+    /// let a: iter::Empty<i32> = iter::empty();
+    /// let b: iter::Empty<i32> = iter::empty();
+    /// ```
+    ///
+    /// ### Known problems
+    ///
+    /// The type of the resulting iterator might become incompatible with its usage
+    #[clippy::version = "1.64.0"]
+    pub ITER_ON_EMPTY_COLLECTIONS,
+    nursery,
+    "Iterator for empty array"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for naive byte counts
+    ///
+    /// ### Why is this bad?
+    /// The [`bytecount`](https://crates.io/crates/bytecount)
+    /// crate has methods to count your bytes faster, especially for large slices.
+    ///
+    /// ### Known problems
+    /// If you have predominantly small slices, the
+    /// `bytecount::count(..)` method may actually be slower. However, if you can
+    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
+    /// faster in those cases.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let vec = vec![1_u8];
+    /// let count = vec.iter().filter(|x| **x == 0u8).count();
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// # let vec = vec![1_u8];
+    /// let count = bytecount::count(&vec, 0u8);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub NAIVE_BYTECOUNT,
+    pedantic,
+    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
+}
+
+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_clippy_lint! {
+    /// ### What it does
+    /// Checks for calls to `ends_with` with possible file extensions
+    /// and suggests to use a case-insensitive approach instead.
+    ///
+    /// ### Why is this bad?
+    /// `ends_with` is case-sensitive and may not detect files with a valid extension.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn is_rust_file(filename: &str) -> bool {
+    ///     filename.ends_with(".rs")
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn is_rust_file(filename: &str) -> bool {
+    ///     let filename = std::path::Path::new(filename);
+    ///     filename.extension()
+    ///         .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
+    /// }
+    /// ```
+    #[clippy::version = "1.51.0"]
+    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    pedantic,
+    "Checks for calls to ends_with with case-sensitive file extensions"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for using `x.get(0)` instead of
+    /// `x.first()`.
+    ///
+    /// ### Why is this bad?
+    /// Using `x.first()` is easier to read and has the same
+    /// result.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = vec![2, 3, 5];
+    /// let first_element = x.get(0);
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let x = vec![2, 3, 5];
+    /// let first_element = x.first();
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub GET_FIRST,
+    style,
+    "Using `x.get(0)` when `x.first()` is simpler"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Finds patterns that reimplement `Option::ok_or`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Concise code helps focusing on behavior instead of boilerplate.
+    ///
+    /// ### Examples
+    /// ```rust
+    /// let foo: Option<i32> = None;
+    /// foo.map_or(Err("error"), |v| Ok(v));
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let foo: Option<i32> = None;
+    /// foo.ok_or("error");
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub MANUAL_OK_OR,
+    pedantic,
+    "finds patterns that can be encoded more concisely with `Option::ok_or`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `map(|x| x.clone())` or
+    /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
+    /// and suggests `cloned()` or `copied()` instead
+    ///
+    /// ### Why is this bad?
+    /// Readability, this can be written more concisely
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = vec![42, 43];
+    /// let y = x.iter();
+    /// let z = y.map(|i| *i);
+    /// ```
+    ///
+    /// The correct use would be:
+    ///
+    /// ```rust
+    /// let x = vec![42, 43];
+    /// let y = x.iter();
+    /// let z = y.cloned();
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub MAP_CLONE,
+    style,
+    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for instances of `map_err(|_| Some::Enum)`
+    ///
+    /// ### Why is this bad?
+    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
+    ///
+    /// ### Example
+    /// Before:
+    /// ```rust
+    /// use std::fmt;
+    ///
+    /// #[derive(Debug)]
+    /// enum Error {
+    ///     Indivisible,
+    ///     Remainder(u8),
+    /// }
+    ///
+    /// impl fmt::Display for Error {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         match self {
+    ///             Error::Indivisible => write!(f, "could not divide input by three"),
+    ///             Error::Remainder(remainder) => write!(
+    ///                 f,
+    ///                 "input is not divisible by three, remainder = {}",
+    ///                 remainder
+    ///             ),
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for Error {}
+    ///
+    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+    ///     input
+    ///         .parse::<i32>()
+    ///         .map_err(|_| Error::Indivisible)
+    ///         .map(|v| v % 3)
+    ///         .and_then(|remainder| {
+    ///             if remainder == 0 {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(Error::Remainder(remainder as u8))
+    ///             }
+    ///         })
+    /// }
+    ///  ```
+    ///
+    ///  After:
+    ///  ```rust
+    /// use std::{fmt, num::ParseIntError};
+    ///
+    /// #[derive(Debug)]
+    /// enum Error {
+    ///     Indivisible(ParseIntError),
+    ///     Remainder(u8),
+    /// }
+    ///
+    /// impl fmt::Display for Error {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         match self {
+    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
+    ///             Error::Remainder(remainder) => write!(
+    ///                 f,
+    ///                 "input is not divisible by three, remainder = {}",
+    ///                 remainder
+    ///             ),
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for Error {
+    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+    ///         match self {
+    ///             Error::Indivisible(source) => Some(source),
+    ///             _ => None,
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+    ///     input
+    ///         .parse::<i32>()
+    ///         .map_err(Error::Indivisible)
+    ///         .map(|v| v % 3)
+    ///         .and_then(|remainder| {
+    ///             if remainder == 0 {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(Error::Remainder(remainder as u8))
+    ///             }
+    ///         })
+    /// }
+    /// ```
+    #[clippy::version = "1.48.0"]
+    pub MAP_ERR_IGNORE,
+    restriction,
+    "`map_err` should not ignore the original error"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `&mut Mutex::lock` calls
+    ///
+    /// ### Why is this bad?
+    /// `Mutex::lock` is less efficient than
+    /// calling `Mutex::get_mut`. In addition you also have a statically
+    /// guarantee that the mutex isn't locked, instead of just a runtime
+    /// guarantee.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::sync::{Arc, Mutex};
+    ///
+    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
+    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+    ///
+    /// let mut value = value_mutex.lock().unwrap();
+    /// *value += 1;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::sync::{Arc, Mutex};
+    ///
+    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
+    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+    ///
+    /// let value = value_mutex.get_mut().unwrap();
+    /// *value += 1;
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub MUT_MUTEX_LOCK,
+    style,
+    "`&mut Mutex::lock` does unnecessary locking"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for duplicate open options as well as combinations
+    /// that make no sense.
+    ///
+    /// ### Why is this bad?
+    /// In the best case, the code will be harder to read than
+    /// necessary. I don't know the worst case.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::fs::OpenOptions;
+    ///
+    /// OpenOptions::new().read(true).truncate(true);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub NONSENSICAL_OPEN_OPTIONS,
+    correctness,
+    "nonsensical combination of options for opening a file"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
+    /// calls on `PathBuf` that can cause overwrites.
+    ///
+    /// ### Why is this bad?
+    /// Calling `push` with a root path at the start can overwrite the
+    /// previous defined path.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::path::PathBuf;
+    ///
+    /// let mut x = PathBuf::from("/foo");
+    /// x.push("/bar");
+    /// assert_eq!(x, PathBuf::from("/bar"));
+    /// ```
+    /// Could be written:
+    ///
+    /// ```rust
+    /// use std::path::PathBuf;
+    ///
+    /// let mut x = PathBuf::from("/foo");
+    /// x.push("bar");
+    /// assert_eq!(x, PathBuf::from("/foo/bar"));
+    /// ```
+    #[clippy::version = "1.36.0"]
+    pub PATH_BUF_PUSH_OVERWRITE,
+    nursery,
+    "calling `push` with file system root on `PathBuf` can overwrite it"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for zipping a collection with the range of
+    /// `0.._.len()`.
+    ///
+    /// ### Why is this bad?
+    /// The code is better expressed with `.enumerate()`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = vec![1];
+    /// let _ = x.iter().zip(0..x.len());
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x = vec![1];
+    /// let _ = x.iter().enumerate();
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub RANGE_ZIP_WITH_LEN,
+    complexity,
+    "zipping iterator with a range when `enumerate()` would do"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
+    /// - `.to_string()` for `str`
+    /// - `.clone()` for `String`
+    /// - `.to_vec()` for `slice`
+    ///
+    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
+    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
+    ///
+    /// ### Why is this bad?
+    /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
+    /// the string is the intention behind this, `clone()` should be used.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn main() {
+    ///     let x = String::from("hello world").repeat(1);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn main() {
+    ///     let x = String::from("hello world").clone();
+    /// }
+    /// ```
+    #[clippy::version = "1.47.0"]
+    pub REPEAT_ONCE,
+    complexity,
+    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
+}
+
+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 typically better to
+    /// use an unstable sort than a stable sort.
+    ///
+    /// ### Why is this bad?
+    /// 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];
+    /// vec.sort();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let mut vec = vec![2, 1, 3];
+    /// vec.sort_unstable();
+    /// ```
+    #[clippy::version = "1.47.0"]
+    pub STABLE_SORT_PRIMITIVE,
+    pedantic,
+    "use of sort() when sort_unstable() is equivalent"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects `().hash(_)`.
+    ///
+    /// ### Why is this bad?
+    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::hash::Hash;
+    /// # use std::collections::hash_map::DefaultHasher;
+    /// # enum Foo { Empty, WithValue(u8) }
+    /// # use Foo::*;
+    /// # let mut state = DefaultHasher::new();
+    /// # let my_enum = Foo::Empty;
+    /// match my_enum {
+    ///        Empty => ().hash(&mut state),
+    ///        WithValue(x) => x.hash(&mut state),
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::hash::Hash;
+    /// # use std::collections::hash_map::DefaultHasher;
+    /// # enum Foo { Empty, WithValue(u8) }
+    /// # use Foo::*;
+    /// # let mut state = DefaultHasher::new();
+    /// # let my_enum = Foo::Empty;
+    /// match my_enum {
+    ///        Empty => 0_u8.hash(&mut state),
+    ///        WithValue(x) => x.hash(&mut state),
+    /// }
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub UNIT_HASH,
+    correctness,
+    "hashing a unit value, which does nothing"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects uses of `Vec::sort_by` passing in a closure
+    /// which compares the two arguments, either directly or indirectly.
+    ///
+    /// ### Why is this bad?
+    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
+    /// possible) than to use `Vec::sort_by` and a more complicated
+    /// closure.
+    ///
+    /// ### Known problems
+    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
+    /// imported by a use statement, then it will need to be added manually.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # struct A;
+    /// # impl A { fn foo(&self) {} }
+    /// # let mut vec: Vec<A> = Vec::new();
+    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # struct A;
+    /// # impl A { fn foo(&self) {} }
+    /// # let mut vec: Vec<A> = Vec::new();
+    /// vec.sort_by_key(|a| a.foo());
+    /// ```
+    #[clippy::version = "1.46.0"]
+    pub UNNECESSARY_SORT_BY,
+    complexity,
+    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Finds occurrences of `Vec::resize(0, an_int)`
+    ///
+    /// ### Why is this bad?
+    /// This is probably an argument inversion mistake.
+    ///
+    /// ### Example
+    /// ```rust
+    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// vec!(1, 2, 3, 4, 5).clear()
+    /// ```
+    #[clippy::version = "1.46.0"]
+    pub VEC_RESIZE_TO_ZERO,
+    correctness,
+    "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for use of File::read_to_end and File::read_to_string.
+    ///
+    /// ### Why is this bad?
+    /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
+    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
+    ///
+    /// ### Example
+    /// ```rust,no_run
+    /// # use std::io::Read;
+    /// # use std::fs::File;
+    /// let mut f = File::open("foo.txt").unwrap();
+    /// let mut bytes = Vec::new();
+    /// f.read_to_end(&mut bytes).unwrap();
+    /// ```
+    /// Can be written more concisely as
+    /// ```rust,no_run
+    /// # use std::fs;
+    /// let mut bytes = fs::read("foo.txt").unwrap();
+    /// ```
+    #[clippy::version = "1.44.0"]
+    pub VERBOSE_FILE_READS,
+    restriction,
+    "use of `File::read_to_end` or `File::read_to_string`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -2347,6 +3078,7 @@ pub fn new(
     CLONE_ON_COPY,
     CLONE_ON_REF_PTR,
     CLONE_DOUBLE_REF,
+    COLLAPSIBLE_STR_REPLACE,
     ITER_OVEREAGER_CLONED,
     CLONED_INSTEAD_OF_COPIED,
     FLAT_MAP_OPTION,
@@ -2393,6 +3125,7 @@ pub fn new(
     FROM_ITER_INSTEAD_OF_COLLECT,
     INSPECT_FOR_EACH,
     IMPLICIT_CLONE,
+    SUSPICIOUS_TO_OWNED,
     SUSPICIOUS_SPLITN,
     MANUAL_STR_REPEAT,
     EXTEND_WITH_DRAIN,
@@ -2406,6 +3139,25 @@ pub fn new(
     NEEDLESS_OPTION_TAKE,
     NO_EFFECT_REPLACE,
     OBFUSCATED_IF_ELSE,
+    ITER_ON_SINGLE_ITEMS,
+    ITER_ON_EMPTY_COLLECTIONS,
+    NAIVE_BYTECOUNT,
+    BYTES_COUNT_TO_LEN,
+    CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    GET_FIRST,
+    MANUAL_OK_OR,
+    MAP_CLONE,
+    MAP_ERR_IGNORE,
+    MUT_MUTEX_LOCK,
+    NONSENSICAL_OPEN_OPTIONS,
+    PATH_BUF_PUSH_OVERWRITE,
+    RANGE_ZIP_WITH_LEN,
+    REPEAT_ONCE,
+    STABLE_SORT_PRIMITIVE,
+    UNIT_HASH,
+    UNNECESSARY_SORT_BY,
+    VEC_RESIZE_TO_ZERO,
+    VERBOSE_FILE_READS,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2541,7 +3293,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
                 if contains_adt_constructor(ret_ty, self_adt) {
                     return;
                 }
-            } else if contains_ty(ret_ty, self_ty) {
+            } else if ret_ty.contains(self_ty) {
                 return;
             }
 
@@ -2559,7 +3311,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
                             if contains_adt_constructor(assoc_ty, self_adt) {
                                 return;
                             }
-                        } else if contains_ty(assoc_ty, self_ty) {
+                        } else if assoc_ty.contains(self_ty) {
                             return;
                         }
                     }
@@ -2608,7 +3360,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
             if let TraitItemKind::Fn(_, _) = item.kind;
             let ret_ty = return_ty(cx, item.hir_id());
             let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
-            if !contains_ty(ret_ty, self_ty);
+            if !ret_ty.contains(self_ty);
 
             then {
                 span_lint(
@@ -2660,22 +3412,30 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     },
                     _ => {},
                 },
-                ("count", []) => match method_call(recv) {
+                ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
                     Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
                     Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
                         iter_count::check(cx, expr, recv2, name2);
                     },
                     Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
+                    Some(("filter", [recv2, arg], _)) => bytecount::check(cx, expr, recv2, arg),
+                    Some(("bytes", [recv2], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
                     _ => {},
                 },
                 ("drain", [arg]) => {
                     iter_with_drain::check(cx, expr, recv, span, arg);
                 },
+                ("ends_with", [arg]) => {
+                    if let ExprKind::MethodCall(_, _, span) = expr.kind {
+                        case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
+                    }
+                },
                 ("expect", [_]) => match method_call(recv) {
                     Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
                     Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
-                    _ => expect_used::check(cx, expr, recv, self.allow_expect_in_tests),
+                    _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
                 },
+                ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
                 ("extend", [arg]) => {
                     string_extend_chars::check(cx, expr, recv, arg);
                     extend_with_drain::check(cx, expr, recv, arg);
@@ -2702,12 +3462,21 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         inspect_for_each::check(cx, expr, span2);
                     }
                 },
-                ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg),
+                ("get", [arg]) => {
+                    get_first::check(cx, expr, recv, arg);
+                    get_last_with_len::check(cx, expr, recv, arg);
+                },
                 ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
+                ("hash", [arg]) => {
+                    unit_hash::check(cx, expr, recv, arg);
+                },
                 ("is_file", []) => filetype_is_file::check(cx, expr, recv),
                 ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
                 ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
                 ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+                ("iter" | "iter_mut" | "into_iter", []) => {
+                    iter_on_single_or_empty_collections::check(cx, expr, name, recv);
+                },
                 ("join", [join_arg]) => {
                     if let Some(("collect", _, span)) = method_call(recv) {
                         unnecessary_join::check(cx, expr, recv, join_arg, span);
@@ -2720,7 +3489,15 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         }
                     }
                 },
+                ("lock", []) => {
+                    mut_mutex_lock::check(cx, expr, recv, span);
+                },
                 (name @ ("map" | "map_err"), [m_arg]) => {
+                    if name == "map" {
+                        map_clone::check(cx, expr, recv, m_arg, self.msrv);
+                    } else {
+                        map_err_ignore::check(cx, expr, m_arg);
+                    }
                     if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
                         match (name, args) {
                             ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
@@ -2736,7 +3513,10 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     }
                     map_identity::check(cx, expr, recv, m_arg, name, span);
                 },
-                ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
+                ("map_or", [def, map]) => {
+                    option_map_or_none::check(cx, expr, recv, def, map);
+                    manual_ok_or::check(cx, expr, recv, def, map);
+                },
                 ("next", []) => {
                     if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
                         match (name2, args2) {
@@ -2758,11 +3538,46 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     _ => iter_nth_zero::check(cx, expr, recv, n_arg),
                 },
                 ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
+                ("open", [_]) => {
+                    open_options::check(cx, expr, recv);
+                },
                 ("or_else", [arg]) => {
                     if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
                         unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
                     }
                 },
+                ("push", [arg]) => {
+                    path_buf_push_overwrite::check(cx, expr, arg);
+                },
+                ("read_to_end", [_]) => {
+                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
+                },
+                ("read_to_string", [_]) => {
+                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
+                },
+                ("repeat", [arg]) => {
+                    repeat_once::check(cx, expr, recv, arg);
+                },
+                (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => {
+                    no_effect_replace::check(cx, expr, arg1, arg2);
+
+                    // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
+                    if name == "replace" && let Some(("replace", ..)) = method_call(recv) {
+                        collapsible_str_replace::check(cx, expr, arg1, arg2);
+                    }
+                },
+                ("resize", [count_arg, default_arg]) => {
+                    vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
+                },
+                ("sort", []) => {
+                    stable_sort_primitive::check(cx, expr, recv);
+                },
+                ("sort_by", [arg]) => {
+                    unnecessary_sort_by::check(cx, expr, recv, arg, false);
+                },
+                ("sort_unstable_by", [arg]) => {
+                    unnecessary_sort_by::check(cx, expr, recv, arg, true);
+                },
                 ("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);
@@ -2789,7 +3604,12 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     }
                     unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
                 },
-                ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
+                ("to_owned", []) => {
+                    if !suspicious_to_owned::check(cx, expr, recv) {
+                        implicit_clone::check(cx, name, expr, recv);
+                    }
+                },
+                ("to_os_string" | "to_path_buf" | "to_vec", []) => {
                     implicit_clone::check(cx, name, expr, recv);
                 },
                 ("unwrap", []) => {
@@ -2805,8 +3625,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         },
                         _ => {},
                     }
-                    unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests);
+                    unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
                 },
+                ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
                 ("unwrap_or", [u_arg]) => match method_call(recv) {
                     Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
                         manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
@@ -2827,8 +3648,12 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
                     },
                 },
-                ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
-                    no_effect_replace::check(cx, expr, arg1, arg2);
+                ("zip", [arg]) => {
+                    if let ExprKind::MethodCall(name, [iter_recv], _) = recv.kind
+                        && name.ident.name == sym::iter
+                    {
+                        range_zip_with_len::check(cx, expr, iter_recv, arg);
+                    }
                 },
                 _ => {},
             }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
new file mode 100644 (file)
index 0000000..bd8458a
--- /dev/null
@@ -0,0 +1,30 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, Mutability};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::{sym, Span};
+
+use super::MUT_MUTEX_LOCK;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
+    if_chain! {
+        if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
+        then {
+            span_lint_and_sugg(
+                cx,
+                MUT_MUTEX_LOCK,
+                name_span,
+                "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
+                "change this to",
+                "get_mut".to_owned(),
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
new file mode 100644 (file)
index 0000000..c311282
--- /dev/null
@@ -0,0 +1,178 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::paths;
+use clippy_utils::ty::match_type;
+use rustc_ast::ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::{Span, Spanned};
+
+use super::NONSENSICAL_OPEN_OPTIONS;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+        && match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS)
+    {
+        let mut options = Vec::new();
+        get_open_options(cx, recv, &mut options);
+        check_open_options(cx, &options, e.span);
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum Argument {
+    True,
+    False,
+    Unknown,
+}
+
+#[derive(Debug)]
+enum OpenOption {
+    Write,
+    Read,
+    Truncate,
+    Create,
+    Append,
+}
+
+fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
+    if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
+        let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
+
+        // Only proceed if this is a call on some object of type std::fs::OpenOptions
+        if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
+            let argument_option = match arguments[1].kind {
+                ExprKind::Lit(ref span) => {
+                    if let Spanned {
+                        node: LitKind::Bool(lit),
+                        ..
+                    } = *span
+                    {
+                        if lit { Argument::True } else { Argument::False }
+                    } else {
+                        // The function is called with a literal which is not a boolean literal.
+                        // This is theoretically possible, but not very likely.
+                        return;
+                    }
+                },
+                _ => Argument::Unknown,
+            };
+
+            match path.ident.as_str() {
+                "create" => {
+                    options.push((OpenOption::Create, argument_option));
+                },
+                "append" => {
+                    options.push((OpenOption::Append, argument_option));
+                },
+                "truncate" => {
+                    options.push((OpenOption::Truncate, argument_option));
+                },
+                "read" => {
+                    options.push((OpenOption::Read, argument_option));
+                },
+                "write" => {
+                    options.push((OpenOption::Write, argument_option));
+                },
+                _ => (),
+            }
+
+            get_open_options(cx, &arguments[0], options);
+        }
+    }
+}
+
+fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
+    let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
+    let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
+        (false, false, false, false, false);
+    // This code is almost duplicated (oh, the irony), but I haven't found a way to
+    // unify it.
+
+    for option in options {
+        match *option {
+            (OpenOption::Create, arg) => {
+                if create {
+                    span_lint(
+                        cx,
+                        NONSENSICAL_OPEN_OPTIONS,
+                        span,
+                        "the method `create` is called more than once",
+                    );
+                } else {
+                    create = true;
+                }
+                create_arg = create_arg || (arg == Argument::True);
+            },
+            (OpenOption::Append, arg) => {
+                if append {
+                    span_lint(
+                        cx,
+                        NONSENSICAL_OPEN_OPTIONS,
+                        span,
+                        "the method `append` is called more than once",
+                    );
+                } else {
+                    append = true;
+                }
+                append_arg = append_arg || (arg == Argument::True);
+            },
+            (OpenOption::Truncate, arg) => {
+                if truncate {
+                    span_lint(
+                        cx,
+                        NONSENSICAL_OPEN_OPTIONS,
+                        span,
+                        "the method `truncate` is called more than once",
+                    );
+                } else {
+                    truncate = true;
+                }
+                truncate_arg = truncate_arg || (arg == Argument::True);
+            },
+            (OpenOption::Read, arg) => {
+                if read {
+                    span_lint(
+                        cx,
+                        NONSENSICAL_OPEN_OPTIONS,
+                        span,
+                        "the method `read` is called more than once",
+                    );
+                } else {
+                    read = true;
+                }
+                read_arg = read_arg || (arg == Argument::True);
+            },
+            (OpenOption::Write, arg) => {
+                if write {
+                    span_lint(
+                        cx,
+                        NONSENSICAL_OPEN_OPTIONS,
+                        span,
+                        "the method `write` is called more than once",
+                    );
+                } else {
+                    write = true;
+                }
+                write_arg = write_arg || (arg == Argument::True);
+            },
+        }
+    }
+
+    if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
+        span_lint(
+            cx,
+            NONSENSICAL_OPEN_OPTIONS,
+            span,
+            "file opened with `truncate` and `read`",
+        );
+    }
+    if append && truncate && append_arg && truncate_arg {
+        span_lint(
+            cx,
+            NONSENSICAL_OPEN_OPTIONS,
+            span,
+            "file opened with `append` and `truncate`",
+        );
+    }
+}
index 6c641af59f92b9c6a3f1b6941c01305f8b8bd464..3c4002a3aef99b86fc175c86b343c9530cfac29e 100644 (file)
@@ -78,7 +78,7 @@ pub(super) fn check<'tcx>(
                     map_span,
                     String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
                 ),
-                (expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
+                (expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
             ];
 
             if !unwrap_snippet_none {
diff --git a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs
new file mode 100644 (file)
index 0000000..0cc28c0
--- /dev/null
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+use std::path::{Component, Path};
+
+use super::PATH_BUF_PUSH_OVERWRITE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::PathBuf);
+        if let ExprKind::Lit(ref lit) = arg.kind;
+        if let LitKind::Str(ref path_lit, _) = lit.node;
+        if let pushed_path = Path::new(path_lit.as_str());
+        if let Some(pushed_path_lit) = pushed_path.to_str();
+        if pushed_path.has_root();
+        if let Some(root) = pushed_path.components().next();
+        if root == Component::RootDir;
+        then {
+            span_lint_and_sugg(
+                cx,
+                PATH_BUF_PUSH_OVERWRITE,
+                lit.span,
+                "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
+                "try",
+                format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs
new file mode 100644 (file)
index 0000000..00a2a0d
--- /dev/null
@@ -0,0 +1,34 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::snippet;
+use clippy_utils::{higher, SpanlessEq};
+use clippy_utils::{is_integer_const, is_trait_method};
+use if_chain::if_chain;
+use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::RANGE_ZIP_WITH_LEN;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) {
+    if_chain! {
+        if is_trait_method(cx, expr, sym::Iterator);
+        // range expression in `.zip()` call: `0..x.len()`
+        if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
+        if is_integer_const(cx, start, 0);
+        // `.len()` call
+        if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind;
+        if len_path.ident.name == sym::len;
+        // `.iter()` and `.len()` called on same `Path`
+        if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind;
+        if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind;
+        if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
+        then {
+            span_lint(cx,
+                RANGE_ZIP_WITH_LEN,
+                expr.span,
+                &format!("it is more idiomatic to use `{}.iter().enumerate()`",
+                    snippet(cx, recv.span, "_"))
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs
new file mode 100644 (file)
index 0000000..0a14f92
--- /dev/null
@@ -0,0 +1,52 @@
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+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::REPEAT_ONCE;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    recv: &'tcx Expr<'_>,
+    repeat_arg: &'tcx Expr<'_>,
+) {
+    if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
+        let ty = cx.typeck_results().expr_ty(recv).peel_refs();
+        if ty.is_str() {
+            span_lint_and_sugg(
+                cx,
+                REPEAT_ONCE,
+                expr.span,
+                "calling `repeat(1)` on str",
+                "consider using `.to_string()` instead",
+                format!("{}.to_string()", snippet(cx, recv.span, r#""...""#)),
+                Applicability::MachineApplicable,
+            );
+        } else if ty.builtin_index().is_some() {
+            span_lint_and_sugg(
+                cx,
+                REPEAT_ONCE,
+                expr.span,
+                "calling `repeat(1)` on slice",
+                "consider using `.to_vec()` instead",
+                format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)),
+                Applicability::MachineApplicable,
+            );
+        } else if is_type_diagnostic_item(cx, ty, sym::String) {
+            span_lint_and_sugg(
+                cx,
+                REPEAT_ONCE,
+                expr.span,
+                "calling `repeat(1)` on a string literal",
+                "consider using `.clone()` instead",
+                format!("{}.clone()", snippet(cx, recv.span, r#""...""#)),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs
new file mode 100644 (file)
index 0000000..91951c6
--- /dev/null
@@ -0,0 +1,31 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_slice_of_primitives;
+use clippy_utils::source::snippet_with_context;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+
+use super::STABLE_SORT_PRIMITIVE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+        && cx.tcx.type_of(impl_id).is_slice()
+        && let Some(slice_type) = is_slice_of_primitives(cx, recv)
+    {
+        span_lint_and_then(
+            cx,
+            STABLE_SORT_PRIMITIVE,
+            e.span,
+            &format!("used `sort` on primitive type `{}`", slice_type),
+            |diag| {
+                let mut app = Applicability::MachineApplicable;
+                let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0;
+                diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app);
+                diag.note(
+                    "an unstable sort typically performs faster without any observable difference for this data type",
+                );
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs
new file mode 100644 (file)
index 0000000..6b306fb
--- /dev/null
@@ -0,0 +1,36 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_diag_trait_item;
+use clippy_utils::source::snippet_with_context;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+use super::SUSPICIOUS_TO_OWNED;
+
+pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool {
+    if_chain! {
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if is_diag_trait_item(cx, method_def_id, sym::ToOwned);
+        let input_type = cx.typeck_results().expr_ty(expr);
+        if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind();
+        if cx.tcx.is_diagnostic_item(sym::Cow, adt.did());
+        then {
+            let mut app = Applicability::MaybeIncorrect;
+            let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
+            span_lint_and_sugg(
+                cx,
+                SUSPICIOUS_TO_OWNED,
+                expr.span,
+                &format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type),
+                "consider using, depending on intent",
+                format!("{0}.clone()` or `{0}.into_owned()", recv_snip),
+                app,
+            );
+            return true;
+        }
+    }
+    false
+}
index 77d21f1d3730c66afeb7ac64f42ea735dd1e611e..a1c6294737cf87225631efca472c0ed5f03e93cf 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty};
+use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
     if_chain! {
         if let hir::ExprKind::Call(callee, args) = recv.kind;
         if args.is_empty();
-        if is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
+        if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
         if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr));
         then {
             span_lint(
diff --git a/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs b/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs
new file mode 100644 (file)
index 0000000..3c7955b
--- /dev/null
@@ -0,0 +1,29 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::UNIT_HASH;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
+    if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() {
+        span_lint_and_then(
+            cx,
+            UNIT_HASH,
+            expr.span,
+            "this call to `hash` on the unit type will do nothing",
+            |diag| {
+                diag.span_suggestion(
+                    expr.span,
+                    "remove the call to `hash` or consider using",
+                    format!("0_u8.hash({})", snippet(cx, arg.span, ".."),),
+                    Applicability::MaybeIncorrect,
+                );
+                diag.note("the implementation of `Hash` for `()` is a no-op");
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
new file mode 100644 (file)
index 0000000..1966990
--- /dev/null
@@ -0,0 +1,224 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::implements_trait;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, subst::GenericArgKind};
+use rustc_span::sym;
+use rustc_span::symbol::Ident;
+use std::iter;
+
+use super::UNNECESSARY_SORT_BY;
+
+enum LintTrigger {
+    Sort(SortDetection),
+    SortByKey(SortByKeyDetection),
+}
+
+struct SortDetection {
+    vec_name: String,
+}
+
+struct SortByKeyDetection {
+    vec_name: String,
+    closure_arg: String,
+    closure_body: String,
+    reverse: bool,
+}
+
+/// Detect if the two expressions are mirrored (identical, except one
+/// contains a and the other replaces it with b)
+fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
+    match (&a_expr.kind, &b_expr.kind) {
+        // Two boxes with mirrored contents
+        (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
+            mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+        },
+        // Two arrays with mirrored contents
+        (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
+            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+        },
+        // The two exprs are function calls.
+        // Check to see that the function itself and its arguments are mirrored
+        (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
+            mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+                && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+        },
+        // The two exprs are method calls.
+        // Check to see that the function is the same and the arguments are mirrored
+        // This is enough because the receiver of the method is listed in the arguments
+        (ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
+            left_segment.ident == right_segment.ident
+                && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+        },
+        // Two tuples with mirrored contents
+        (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
+            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+        },
+        // Two binary ops, which are the same operation and which have mirrored arguments
+        (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
+            left_op.node == right_op.node
+                && mirrored_exprs(left_left, a_ident, right_left, b_ident)
+                && mirrored_exprs(left_right, a_ident, right_right, b_ident)
+        },
+        // Two unary ops, which are the same operation and which have the same argument
+        (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
+            left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+        },
+        // The two exprs are literals of some kind
+        (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
+        (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
+        (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
+            mirrored_exprs(left_block, a_ident, right_block, b_ident)
+        },
+        (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
+            left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
+        },
+        // Two paths: either one is a and the other is b, or they're identical to each other
+        (
+            ExprKind::Path(QPath::Resolved(
+                _,
+                Path {
+                    segments: left_segments,
+                    ..
+                },
+            )),
+            ExprKind::Path(QPath::Resolved(
+                _,
+                Path {
+                    segments: right_segments,
+                    ..
+                },
+            )),
+        ) => {
+            (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
+                && left_segments
+                    .iter()
+                    .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
+                || (left_segments.len() == 1
+                    && &left_segments[0].ident == a_ident
+                    && right_segments.len() == 1
+                    && &right_segments[0].ident == b_ident)
+        },
+        // Matching expressions, but one or both is borrowed
+        (
+            ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
+            ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
+        ) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
+        (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
+        (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
+        _ => false,
+    }
+}
+
+fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if cx.tcx.type_of(impl_id).is_slice();
+        if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
+        if let closure_body = cx.tcx.hir().body(body);
+        if let &[
+            Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
+            Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
+        ] = &closure_body.params;
+        if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind;
+        if method_path.ident.name == sym::cmp;
+        if is_trait_method(cx, &closure_body.value, sym::Ord);
+        then {
+            let (closure_body, closure_arg, reverse) = if mirrored_exprs(
+                left_expr,
+                left_ident,
+                right_expr,
+                right_ident
+            ) {
+                (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
+            } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
+                (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
+            } else {
+                return None;
+            };
+            let vec_name = Sugg::hir(cx, recv, "..").to_string();
+
+            if_chain! {
+                if let ExprKind::Path(QPath::Resolved(_, Path {
+                    segments: [PathSegment { ident: left_name, .. }], ..
+                })) = &left_expr.kind;
+                if left_name == left_ident;
+                if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
+                    implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
+                });
+                then {
+                    return Some(LintTrigger::Sort(SortDetection { vec_name }));
+                }
+            }
+
+            if !expr_borrows(cx, left_expr) {
+                return Some(LintTrigger::SortByKey(SortByKeyDetection {
+                    vec_name,
+                    closure_arg,
+                    closure_body,
+                    reverse,
+                }));
+            }
+        }
+    }
+
+    None
+}
+
+fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let ty = cx.typeck_results().expr_ty(expr);
+    matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
+}
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    recv: &'tcx Expr<'_>,
+    arg: &'tcx Expr<'_>,
+    is_unstable: bool,
+) {
+    match detect_lint(cx, expr, recv, arg) {
+        Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
+            cx,
+            UNNECESSARY_SORT_BY,
+            expr.span,
+            "use Vec::sort_by_key here instead",
+            "try",
+            format!(
+                "{}.sort{}_by_key(|{}| {})",
+                trigger.vec_name,
+                if is_unstable { "_unstable" } else { "" },
+                trigger.closure_arg,
+                if trigger.reverse {
+                    format!("std::cmp::Reverse({})", trigger.closure_body)
+                } else {
+                    trigger.closure_body.to_string()
+                },
+            ),
+            if trigger.reverse {
+                Applicability::MaybeIncorrect
+            } else {
+                Applicability::MachineApplicable
+            },
+        ),
+        Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
+            cx,
+            UNNECESSARY_SORT_BY,
+            expr.span,
+            "use Vec::sort here instead",
+            "try",
+            format!(
+                "{}.sort{}()",
+                trigger.vec_name,
+                if is_unstable { "_unstable" } else { "" },
+            ),
+            Applicability::MachineApplicable,
+        ),
+        None => {},
+    }
+}
index b3276f1394ed2f7bcb0f4bcd3ad5bea8322717ce..44bf84352943853bddfa26628e7515617a93e14f 100644 (file)
@@ -3,11 +3,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{
-    contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
+    get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, 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 clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -279,7 +278,19 @@ fn check_other_call_arg<'tcx>(
                 &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
                 call_substs,
             );
-            implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
+            // if `expr` is a `String` and generic target is [u8], skip
+            // (https://github.com/rust-lang/rust-clippy/issues/9317).
+            if let [subst] = composed_substs[..]
+                && let GenericArgKind::Type(arg_ty) = subst.unpack()
+                && arg_ty.is_slice()
+                && let inner_ty = arg_ty.builtin_index().unwrap()
+                && let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
+                && let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
+                && is_type_diagnostic_item(cx, self_ty, sym::String) {
+                false
+            } else {
+                implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
+            }
         } else {
             false
         };
@@ -292,7 +303,7 @@ fn check_other_call_arg<'tcx>(
         // (https://github.com/rust-lang/rust-clippy/issues/8507).
         if (n_refs == 0 && !receiver_ty.is_ref())
             || trait_predicate.def_id() != as_ref_trait_id
-            || !contains_ty(fn_sig.output(), input);
+            || !fn_sig.output().contains(input);
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
             span_lint_and_sugg(
@@ -360,25 +371,15 @@ fn get_input_traits_and_projections<'tcx>(
 ) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
     let mut trait_predicates = Vec::new();
     let mut projection_predicates = Vec::new();
-    for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
-        // `substs` should have 1 + n elements. The first is the type on the left hand side of an
-        // `as`. The remaining n are trait parameters.
-        let is_input_substs = |substs: SubstsRef<'tcx>| {
-            if_chain! {
-                if let Some(arg) = substs.iter().next();
-                if let GenericArgKind::Type(arg_ty) = arg.unpack();
-                if arg_ty == input;
-                then { true } else { false }
-            }
-        };
+    for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
         match predicate.kind().skip_binder() {
             PredicateKind::Trait(trait_predicate) => {
-                if is_input_substs(trait_predicate.trait_ref.substs) {
+                if trait_predicate.trait_ref.self_ty() == input {
                     trait_predicates.push(trait_predicate);
                 }
             },
             PredicateKind::Projection(projection_predicate) => {
-                if is_input_substs(projection_predicate.projection_ty.substs) {
+                if projection_predicate.projection_ty.self_ty() == input {
                     projection_predicates.push(projection_predicate);
                 }
             },
index ce1a52e5480afb81226859153023754301935f84..ee17f2d7889ee9f93b6f619bf0100d982a62ec3e 100644 (file)
@@ -7,18 +7,26 @@
 
 use super::{EXPECT_USED, UNWRAP_USED};
 
-/// lint use of `unwrap()` for `Option`s and `Result`s
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
+/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    recv: &hir::Expr<'_>,
+    is_err: bool,
+    allow_unwrap_in_tests: bool,
+) {
     let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 
-    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
+    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
         Some((UNWRAP_USED, "an Option", "None", ""))
     } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
-        Some((UNWRAP_USED, "a Result", "Err", "an "))
+        Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
     } else {
         None
     };
 
+    let method_suffix = if is_err { "_err" } else { "" };
+
     if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
         return;
     }
@@ -27,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
         let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
             format!(
                 "if you don't want to handle the `{none_value}` case gracefully, consider \
-                using `expect()` to provide a better panic message"
+                using `expect{method_suffix}()` to provide a better panic message"
             )
         } else {
             format!("if this value is {none_prefix}`{none_value}`, it will panic")
@@ -37,7 +45,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
             cx,
             lint,
             expr.span,
-            &format!("used `unwrap()` on `{kind}` value"),
+            &format!("used `unwrap{method_suffix}()` on `{kind}` value"),
             None,
             &help,
         );
diff --git a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs
new file mode 100644 (file)
index 0000000..02d8364
--- /dev/null
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span};
+
+use super::VEC_RESIZE_TO_ZERO;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    count_arg: &'tcx Expr<'_>,
+    default_arg: &'tcx Expr<'_>,
+    name_span: Span,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Vec);
+        if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind;
+        if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind;
+        then {
+            let method_call_span = expr.span.with_lo(name_span.lo());
+            span_lint_and_then(
+                cx,
+                VEC_RESIZE_TO_ZERO,
+                expr.span,
+                "emptying a vector with `resize`",
+                |db| {
+                    db.help("the arguments may be inverted...");
+                    db.span_suggestion(
+                        method_call_span,
+                        "...or you can empty the vector with",
+                        "clear()".to_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                },
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs
new file mode 100644 (file)
index 0000000..2fe5ae9
--- /dev/null
@@ -0,0 +1,28 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::VERBOSE_FILE_READS;
+
+pub(super) const READ_TO_END_MSG: (&str, &str) = ("use of `File::read_to_end`", "consider using `fs::read` instead");
+pub(super) const READ_TO_STRING_MSG: (&str, &str) = (
+    "use of `File::read_to_string`",
+    "consider using `fs::read_to_string` instead",
+);
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    recv: &'tcx Expr<'_>,
+    (msg, help): (&str, &str),
+) {
+    if is_trait_method(cx, expr, sym::IoRead)
+        && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _)))
+        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File)
+    {
+        span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help);
+    }
+}
index df044538fe19d36b825bcd5d858c9833a6308961..7c4ae746e90e91894f9c604dadc53254cb48fb52 100644 (file)
@@ -46,7 +46,7 @@ fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
             "these patterns are unneeded as the `..` pattern can match those elements"
         },
         if only_one { "remove it" } else { "remove them" },
-        "".to_string(),
+        String::new(),
         Applicability::MachineApplicable,
     );
 }
index f763e0d24c9444206a8d14b599a411f3067d610e..020efeaebf02905dd46bb0b0a7ef0c5fa7f91853 100644 (file)
@@ -40,7 +40,7 @@
     /// }
     /// impl<A, B> Foo<A, B> {}
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub MISMATCHING_TYPE_PARAM_ORDER,
     pedantic,
     "type parameter positioned inconsistently between type def and impl block"
diff --git a/src/tools/clippy/clippy_lints/src/multi_assignments.rs b/src/tools/clippy/clippy_lints/src/multi_assignments.rs
new file mode 100644 (file)
index 0000000..81eb1a0
--- /dev/null
@@ -0,0 +1,65 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for nested assignments.
+    ///
+    /// ### Why is this bad?
+    /// While this is in most cases already a type mismatch,
+    /// the result of an assignment being `()` can throw off people coming from languages like python or C,
+    /// where such assignments return a copy of the assigned value.
+    ///
+    /// ### Example
+    /// ```rust
+    ///# let (a, b);
+    /// a = b = 42;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    ///# let (a, b);
+    /// b = 42;
+    /// a = b;
+    /// ```
+    #[clippy::version = "1.65.0"]
+    pub MULTI_ASSIGNMENTS,
+    suspicious,
+    "instead of using `a = b = c;` use `a = c; b = c;`"
+}
+
+declare_lint_pass!(MultiAssignments => [MULTI_ASSIGNMENTS]);
+
+fn strip_paren_blocks(expr: &Expr) -> &Expr {
+    match &expr.kind {
+        ExprKind::Paren(e) => strip_paren_blocks(e),
+        ExprKind::Block(b, _) => {
+            if let [
+                Stmt {
+                    kind: StmtKind::Expr(e),
+                    ..
+                },
+            ] = &b.stmts[..]
+            {
+                strip_paren_blocks(e)
+            } else {
+                expr
+            }
+        },
+        _ => expr,
+    }
+}
+
+impl EarlyLintPass for MultiAssignments {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if let ExprKind::Assign(target, source, _) = &expr.kind {
+            if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
+                span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
+            };
+            if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
+                span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
+            }
+        };
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
deleted file mode 100644 (file)
index b7f981f..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Mutability};
-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
-    /// Checks for `&mut Mutex::lock` calls
-    ///
-    /// ### Why is this bad?
-    /// `Mutex::lock` is less efficient than
-    /// calling `Mutex::get_mut`. In addition you also have a statically
-    /// guarantee that the mutex isn't locked, instead of just a runtime
-    /// guarantee.
-    ///
-    /// ### Example
-    /// ```rust
-    /// use std::sync::{Arc, Mutex};
-    ///
-    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
-    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
-    ///
-    /// let mut value = value_mutex.lock().unwrap();
-    /// *value += 1;
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// use std::sync::{Arc, Mutex};
-    ///
-    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
-    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
-    ///
-    /// let value = value_mutex.get_mut().unwrap();
-    /// *value += 1;
-    /// ```
-    #[clippy::version = "1.49.0"]
-    pub MUT_MUTEX_LOCK,
-    style,
-    "`&mut Mutex::lock` does unnecessary locking"
-}
-
-declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
-
-impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
-        if_chain! {
-            if let ExprKind::MethodCall(path, [self_arg, ..], _) = &ex.kind;
-            if path.ident.name == sym!(lock);
-            let ty = cx.typeck_results().expr_ty(self_arg);
-            if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
-            if is_type_diagnostic_item(cx, *inner_ty, sym::Mutex);
-            then {
-                span_lint_and_sugg(
-                    cx,
-                    MUT_MUTEX_LOCK,
-                    path.ident.span,
-                    "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
-                    "change this to",
-                    "get_mut".to_owned(),
-                    Applicability::MaybeIncorrect,
-                );
-            }
-        }
-    }
-}
index 413a740be25a529c0fe8fb64361dd38d567de634..774a3540d1e0c41bef19b00f44b955664de5c743 100644 (file)
@@ -1,25 +1,16 @@
-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};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id};
+use core::cell::Cell;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
-use rustc_hir::{
-    Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
-    PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
-};
+use rustc_hir::hir_id::HirIdMap;
+use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::kw;
-use rustc_span::symbol::Ident;
+use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::ty::{self, ConstKind};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::{kw, Ident};
 use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.61.0"]
     pub ONLY_USED_IN_RECURSION,
-    nursery,
+    complexity,
     "arguments that is only used in recursion can be removed"
 }
-declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
-
-impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
-    fn check_fn(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        kind: FnKind<'tcx>,
-        decl: &'tcx rustc_hir::FnDecl<'tcx>,
-        body: &'tcx Body<'tcx>,
-        _: 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;
-
-            if data.len() > 1 {
-                match data.get(data.len() - 2) {
-                    Some(DisambiguatedDefPathData {
-                        data: DefPathData::Impl,
-                        disambiguator,
-                    }) if *disambiguator != 0 => return,
-                    _ => {},
-                }
-            }
-
-            let has_self = !matches!(decl.implicit_self, ImplicitSelfKind::None);
-
-            let ty_res = cx.typeck_results();
-            let param_span = body
-                .params
-                .iter()
-                .flat_map(|param| {
-                    let mut v = Vec::new();
-                    param.pat.each_binding(|_, hir_id, span, ident| {
-                        v.push((hir_id, span, ident));
-                    });
-                    v
-                })
-                .skip(if has_self { 1 } else { 0 })
-                .filter(|(_, _, ident)| !ident.name.as_str().starts_with('_'))
-                .collect_vec();
-
-            let params = body.params.iter().map(|param| param.pat).collect();
-
-            let mut visitor = SideEffectVisit {
-                graph: FxHashMap::default(),
-                has_side_effect: FxHashSet::default(),
-                ret_vars: Vec::new(),
-                contains_side_effect: false,
-                break_vars: FxHashMap::default(),
-                params,
-                fn_ident: ident,
-                fn_def_id: def_id,
-                is_method: matches!(kind, FnKind::Method(..)),
-                has_self,
-                ty_res,
-                tcx: cx.tcx,
-                visited_exprs: FxHashSet::default(),
-            };
-
-            visitor.visit_expr(&body.value);
-            let vars = std::mem::take(&mut visitor.ret_vars);
-            // this would set the return variables to side effect
-            visitor.add_side_effect(vars);
-
-            let mut queue = visitor.has_side_effect.iter().copied().collect::<VecDeque<_>>();
-
-            // a simple BFS to check all the variables that have side effect
-            while let Some(id) = queue.pop_front() {
-                if let Some(next) = visitor.graph.get(&id) {
-                    for i in next {
-                        if !visitor.has_side_effect.contains(i) {
-                            visitor.has_side_effect.insert(*i);
-                            queue.push_back(*i);
-                        }
-                    }
-                }
-            }
-
-            for (id, span, ident) in param_span {
-                // if the variable is not used in recursion, it would be marked as unused
-                if !visitor.has_side_effect.contains(&id) {
-                    let mut queue = VecDeque::new();
-                    let mut visited = FxHashSet::default();
-
-                    queue.push_back(id);
-
-                    // a simple BFS to check the graph can reach to itself
-                    // if it can't, it means the variable is never used in recursion
-                    while let Some(id) = queue.pop_front() {
-                        if let Some(next) = visitor.graph.get(&id) {
-                            for i in next {
-                                if !visited.contains(i) {
-                                    visited.insert(id);
-                                    queue.push_back(*i);
-                                }
-                            }
-                        }
-                    }
+impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
+
+#[derive(Clone, Copy)]
+enum FnKind {
+    Fn,
+    TraitFn,
+    // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
+    // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
+    // equality.
+    ImplTraitFn(usize),
+}
 
-                    if visited.contains(&id) {
-                        span_lint_and_sugg(
-                            cx,
-                            ONLY_USED_IN_RECURSION,
-                            span,
-                            "parameter is only used in recursion",
-                            "if this is intentional, prefix with an underscore",
-                            format!("_{}", ident.name.as_str()),
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
-            }
+struct Param {
+    /// The function this is a parameter for.
+    fn_id: DefId,
+    fn_kind: FnKind,
+    /// The index of this parameter.
+    idx: usize,
+    ident: Ident,
+    /// Whether this parameter should be linted. Set by `Params::flag_for_linting`.
+    apply_lint: Cell<bool>,
+    /// All the uses of this parameter.
+    uses: Vec<Usage>,
+}
+impl Param {
+    fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self {
+        Self {
+            fn_id,
+            fn_kind,
+            idx,
+            ident,
+            apply_lint: Cell::new(true),
+            uses: Vec::new(),
         }
     }
 }
 
-pub fn is_primitive(ty: Ty<'_>) -> bool {
-    let ty = ty.peel_refs();
-    ty.is_primitive() || ty.is_str()
+#[derive(Debug)]
+struct Usage {
+    span: Span,
+    idx: usize,
 }
-
-pub fn is_array(ty: Ty<'_>) -> bool {
-    let ty = ty.peel_refs();
-    ty.is_array() || ty.is_array_slice()
+impl Usage {
+    fn new(span: Span, idx: usize) -> Self {
+        Self { span, idx }
+    }
 }
 
-/// This builds the graph of side effect.
-/// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
-///
-/// There are some example in following code:
-/// ```rust, ignore
-/// let b = 1;
-/// let a = b; // a -> b
-/// let (c, d) = (a, b); // c -> b, d -> b
-///
-/// let e = if a == 0 { // e -> a
-///     c // e -> c
-/// } else {
-///     d // e -> d
-/// };
-/// ```
-pub struct SideEffectVisit<'tcx> {
-    graph: FxHashMap<HirId, FxHashSet<HirId>>,
-    has_side_effect: FxHashSet<HirId>,
-    // bool for if the variable was dereferenced from mutable reference
-    ret_vars: Vec<(HirId, bool)>,
-    contains_side_effect: bool,
-    // break label
-    break_vars: FxHashMap<Ident, Vec<(HirId, bool)>>,
-    params: Vec<&'tcx Pat<'tcx>>,
-    fn_ident: Ident,
-    fn_def_id: DefId,
-    is_method: bool,
-    has_self: bool,
-    ty_res: &'tcx TypeckResults<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    visited_exprs: FxHashSet<HirId>,
+/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
+/// `DefId` of the function paired with the parameter's index.
+#[derive(Default)]
+struct Params {
+    params: Vec<Param>,
+    by_id: HirIdMap<usize>,
+    by_fn: FxHashMap<(DefId, usize), usize>,
 }
-
-impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
-    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);
-            },
-            StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
-                walk_stmt(self, s);
-            },
-            StmtKind::Local(_) => {},
-        }
-        self.ret_vars.clear();
+impl Params {
+    fn insert(&mut self, param: Param, id: HirId) {
+        let idx = self.params.len();
+        self.by_id.insert(id, idx);
+        self.by_fn.insert((param.fn_id, param.idx), idx);
+        self.params.push(param);
     }
 
-    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
-                    .iter()
-                    .flat_map(|expr| {
-                        self.visit_expr(expr);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-            },
-            ExprKind::Call(callee, args) => self.visit_fn(callee, args),
-            ExprKind::MethodCall(path, args, _) => self.visit_method_call(path, args),
-            ExprKind::Binary(_, lhs, rhs) => {
-                self.visit_bin_op(lhs, rhs);
-            },
-            ExprKind::Unary(op, expr) => self.visit_un_op(op, expr),
-            ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false),
-            ExprKind::If(bind, then_expr, else_expr) => {
-                self.visit_if(bind, then_expr, else_expr);
-            },
-            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(&Closure { body, .. }) => {
-                let body = self.tcx.hir().body(body);
-                self.visit_body(body);
-                let vars = std::mem::take(&mut self.ret_vars);
-                self.add_side_effect(vars);
-            },
-            ExprKind::Loop(block, label, _, _) | ExprKind::Block(block, label) => {
-                self.visit_block_label(block, label);
-            },
-            ExprKind::Assign(bind, expr, _) => {
-                self.visit_assign(bind, expr);
-            },
-            ExprKind::AssignOp(_, bind, expr) => {
-                self.visit_assign(bind, expr);
-                self.visit_bin_op(bind, expr);
-            },
-            ExprKind::Field(expr, _) => {
-                self.visit_expr(expr);
-                if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
-                    self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
-                }
-            },
-            ExprKind::Index(expr, index) => {
-                self.visit_expr(expr);
-                let mut vars = std::mem::take(&mut self.ret_vars);
-                self.visit_expr(index);
-                self.ret_vars.append(&mut vars);
-
-                if !is_array(self.ty_res.expr_ty(expr)) {
-                    self.add_side_effect(self.ret_vars.clone());
-                } else if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
-                    self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
-                }
-            },
-            ExprKind::Break(dest, Some(expr)) => {
-                self.visit_expr(expr);
-                if let Some(label) = dest.label {
-                    self.break_vars
-                        .entry(label.ident)
-                        .or_insert(Vec::new())
-                        .append(&mut self.ret_vars);
-                }
-                self.contains_side_effect = true;
-            },
-            ExprKind::Ret(Some(expr)) => {
-                self.visit_expr(expr);
-                let vars = std::mem::take(&mut self.ret_vars);
-                self.add_side_effect(vars);
-                self.contains_side_effect = true;
-            },
-            ExprKind::Break(_, None) | ExprKind::Continue(_) | ExprKind::Ret(None) => {
-                self.contains_side_effect = true;
-            },
-            ExprKind::Struct(_, exprs, expr) => {
-                let mut ret_vars = exprs
-                    .iter()
-                    .flat_map(|field| {
-                        self.visit_expr(field.expr);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-
-                walk_list!(self, visit_expr, expr);
-                self.ret_vars.append(&mut ret_vars);
-            },
-            _ => walk_expr(self, ex),
+    fn remove_by_id(&mut self, id: HirId) {
+        if let Some(param) = self.get_by_id_mut(id) {
+            param.uses = Vec::new();
+            let key = (param.fn_id, param.idx);
+            self.by_fn.remove(&key);
+            self.by_id.remove(&id);
         }
     }
 
-    fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
-        if let Res::Local(id) = path.res {
-            self.ret_vars.push((id, false));
-        }
+    fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> {
+        self.params.get_mut(*self.by_id.get(&id)?)
     }
-}
 
-impl<'tcx> SideEffectVisit<'tcx> {
-    fn visit_assign(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
-        // Just support array and tuple unwrapping for now.
-        //
-        // ex) `(a, b) = (c, d);`
-        // The graph would look like this:
-        //   a -> c
-        //   b -> d
-        //
-        // This would minimize the connection of the side-effect graph.
-        match (&lhs.kind, &rhs.kind) {
-            (ExprKind::Array(lhs), ExprKind::Array(rhs)) | (ExprKind::Tup(lhs), ExprKind::Tup(rhs)) => {
-                // if not, it is a compile error
-                debug_assert!(lhs.len() == rhs.len());
-                izip!(*lhs, *rhs).for_each(|(lhs, rhs)| self.visit_assign(lhs, rhs));
-            },
-            // in other assigns, we have to connect all each other
-            // because they can be connected somehow
-            _ => {
-                self.visit_expr(lhs);
-                let lhs_vars = std::mem::take(&mut self.ret_vars);
-                self.visit_expr(rhs);
-                let rhs_vars = std::mem::take(&mut self.ret_vars);
-                self.connect_assign(&lhs_vars, &rhs_vars, false);
-            },
-        }
+    fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> {
+        self.params.get(*self.by_fn.get(&(id, idx))?)
     }
 
-    fn visit_block_label(&mut self, block: &'tcx Block<'tcx>, label: Option<Label>) {
-        self.visit_block(block);
-        let _ = label.and_then(|label| {
-            self.break_vars
-                .remove(&label.ident)
-                .map(|mut break_vars| self.ret_vars.append(&mut break_vars))
-        });
-    }
-
-    fn visit_bin_op(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
-        self.visit_expr(lhs);
-        let mut ret_vars = std::mem::take(&mut self.ret_vars);
-        self.visit_expr(rhs);
-        self.ret_vars.append(&mut ret_vars);
-
-        // the binary operation between non primitive values are overloaded operators
-        // so they can have side-effects
-        if !is_primitive(self.ty_res.expr_ty(lhs)) || !is_primitive(self.ty_res.expr_ty(rhs)) {
-            self.ret_vars.iter().for_each(|id| {
-                self.has_side_effect.insert(id.0);
-            });
-            self.contains_side_effect = true;
-        }
+    fn clear(&mut self) {
+        self.params.clear();
+        self.by_id.clear();
+        self.by_fn.clear();
     }
 
-    fn visit_un_op(&mut self, op: UnOp, expr: &'tcx Expr<'tcx>) {
-        self.visit_expr(expr);
-        let ty = self.ty_res.expr_ty(expr);
-        // dereferencing a reference has no side-effect
-        if !is_primitive(ty) && !matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(..))) {
-            self.add_side_effect(self.ret_vars.clone());
-        }
-
-        if matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(_, _, Mutability::Mut))) {
-            self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
+    /// Sets the `apply_lint` flag on each parameter.
+    fn flag_for_linting(&mut self) {
+        // Stores the list of parameters currently being resolved. Needed to avoid cycles.
+        let mut eval_stack = Vec::new();
+        for param in &self.params {
+            self.try_disable_lint_for_param(param, &mut eval_stack);
         }
     }
 
-    fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) {
-        match (&pat.kind, &expr.kind) {
-            (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => {
-                self.ret_vars = izip!(*pats, *exprs)
-                    .flat_map(|(pat, expr)| {
-                        self.visit_pat_expr(pat, expr, connect_self);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-            },
-            (PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => {
-                let mut vars = izip!(*front_exprs, *exprs)
-                    .flat_map(|(pat, expr)| {
-                        self.visit_pat_expr(pat, expr, connect_self);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-                self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev())
-                    .flat_map(|(pat, expr)| {
-                        self.visit_pat_expr(pat, expr, connect_self);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-                self.ret_vars.append(&mut vars);
-            },
-            _ => {
-                let mut lhs_vars = Vec::new();
-                pat.each_binding(|_, id, _, _| lhs_vars.push((id, false)));
-                self.visit_expr(expr);
-                let rhs_vars = std::mem::take(&mut self.ret_vars);
-                self.connect_assign(&lhs_vars, &rhs_vars, connect_self);
-                self.ret_vars = rhs_vars;
-            },
+    // Use by calling `flag_for_linting`.
+    fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool {
+        if !param.apply_lint.get() {
+            true
+        } else if param.uses.is_empty() {
+            // Don't lint on unused parameters.
+            param.apply_lint.set(false);
+            true
+        } else if eval_stack.contains(&param.idx) {
+            // Already on the evaluation stack. Returning false will continue to evaluate other dependencies.
+            false
+        } else {
+            eval_stack.push(param.idx);
+            // Check all cases when used at a different parameter index.
+            // Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }`
+            for usage in param.uses.iter().filter(|u| u.idx != param.idx) {
+                if self
+                    .get_by_fn(param.fn_id, usage.idx)
+                    // If the parameter can't be found, then it's used for more than just recursion.
+                    .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
+                {
+                    param.apply_lint.set(false);
+                    eval_stack.pop();
+                    return true;
+                }
+            }
+            eval_stack.pop();
+            false
         }
     }
+}
 
-    fn visit_fn(&mut self, callee: &'tcx Expr<'tcx>, args: &'tcx [Expr<'tcx>]) {
-        self.visit_expr(callee);
-        let mut ret_vars = std::mem::take(&mut self.ret_vars);
-        self.add_side_effect(ret_vars.clone());
-
-        let mut is_recursive = false;
-
-        if_chain! {
-            if !self.has_self;
-            if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind;
-            if let Res::Def(DefKind::Fn, def_id) = path.res;
-            if self.fn_def_id == def_id;
-            then {
-                is_recursive = true;
-            }
-        }
+#[derive(Default)]
+pub struct OnlyUsedInRecursion {
+    /// Track the top-level body entered. Needed to delay reporting when entering nested bodies.
+    entered_body: Option<HirId>,
+    params: Params,
+}
 
-        if_chain! {
-            if !self.has_self && self.is_method;
-            if let ExprKind::Path(QPath::TypeRelative(ty, segment)) = callee.kind;
-            if segment.ident == self.fn_ident;
-            if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
-            if let Res::SelfTy{ .. } = path.res;
-            then {
-                is_recursive = true;
-            }
+impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
+    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+        if body.value.span.from_expansion() {
+            return;
         }
-
-        if is_recursive {
-            izip!(self.params.clone(), args).for_each(|(pat, expr)| {
-                self.visit_pat_expr(pat, expr, true);
-                self.ret_vars.clear();
-            });
-        } else {
-            // This would set arguments used in closure that does not have side-effect.
-            // Closure itself can be detected whether there is a side-effect, but the
-            // value of variable that is holding closure can change.
-            // So, we just check the variables.
-            self.ret_vars = args
-                .iter()
-                .flat_map(|expr| {
-                    self.visit_expr(expr);
-                    std::mem::take(&mut self.ret_vars)
-                })
-                .collect_vec()
-                .into_iter()
-                .map(|id| {
-                    self.has_side_effect.insert(id.0);
-                    id
-                })
-                .collect();
-            self.contains_side_effect = true;
+        // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions.
+        // It can't be renamed, and it can't be removed without removing it from multiple functions.
+        let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) {
+            Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0),
+            Some(Node::TraitItem(&TraitItem {
+                kind: TraitItemKind::Fn(ref sig, _),
+                def_id,
+                ..
+            })) => (
+                def_id.to_def_id(),
+                FnKind::TraitFn,
+                if sig.decl.implicit_self.has_implicit_self() {
+                    1
+                } else {
+                    0
+                },
+            ),
+            Some(Node::ImplItem(&ImplItem {
+                kind: ImplItemKind::Fn(ref sig, _),
+                def_id,
+                ..
+            })) => {
+                #[allow(trivial_casts)]
+                if let Some(Node::Item(item)) = get_parent_node(cx.tcx, cx.tcx.hir().local_def_id_to_hir_id(def_id))
+                    && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id)
+                    && let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id
+                {
+                    (
+                        trait_item_id,
+                        FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
+                        if sig.decl.implicit_self.has_implicit_self() {
+                            1
+                        } else {
+                            0
+                        },
+                    )
+                } else {
+                    (def_id.to_def_id(), FnKind::Fn, 0)
+                }
+            },
+            _ => return,
+        };
+        body.params
+            .iter()
+            .enumerate()
+            .skip(skip_params)
+            .filter_map(|(idx, p)| match p.pat.kind {
+                PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => {
+                    Some((id, Param::new(fn_id, fn_kind, idx, ident)))
+                },
+                _ => None,
+            })
+            .for_each(|(id, param)| self.params.insert(param, id));
+        if self.entered_body.is_none() {
+            self.entered_body = Some(body.value.hir_id);
         }
-
-        self.ret_vars.append(&mut ret_vars);
     }
 
-    fn visit_method_call(&mut self, path: &'tcx PathSegment<'tcx>, args: &'tcx [Expr<'tcx>]) {
-        if_chain! {
-            if self.is_method;
-            if path.ident == self.fn_ident;
-            if let ExprKind::Path(QPath::Resolved(_, path)) = args.first().unwrap().kind;
-            if let Res::Local(..) = path.res;
-            let ident = path.segments.last().unwrap().ident;
-            if ident.name == kw::SelfLower;
-            then {
-                izip!(self.params.clone(), args.iter())
-                    .for_each(|(pat, expr)| {
-                        self.visit_pat_expr(pat, expr, true);
-                        self.ret_vars.clear();
-                    });
-            } else {
-                self.ret_vars = args
-                    .iter()
-                    .flat_map(|expr| {
-                        self.visit_expr(expr);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect_vec()
-                    .into_iter()
-                    .map(|a| {
-                        self.has_side_effect.insert(a.0);
-                        a
-                    })
-                    .collect();
-                self.contains_side_effect = true;
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
+        if let Some(id) = path_to_local(e)
+            && let Some(param) = self.params.get_by_id_mut(id)
+        {
+            let typeck = cx.typeck_results();
+            let span = e.span;
+            let mut e = e;
+            loop {
+                match get_expr_use_or_unification_node(cx.tcx, e) {
+                    None | Some((Node::Stmt(_), _)) => return,
+                    Some((Node::Expr(parent), child_id)) => match parent.kind {
+                        // Recursive call. Track which index the parameter is used in.
+                        ExprKind::Call(callee, args)
+                            if path_def_id(cx, callee).map_or(false, |id| {
+                                id == param.fn_id
+                                    && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
+                            }) =>
+                        {
+                            if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+                                param.uses.push(Usage::new(span, idx));
+                            }
+                            return;
+                        },
+                        ExprKind::MethodCall(_, args, _)
+                            if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
+                                id == param.fn_id
+                                    && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
+                            }) =>
+                        {
+                            if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+                                param.uses.push(Usage::new(span, idx));
+                            }
+                            return;
+                        },
+                        // Assignment to a parameter is fine.
+                        ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
+                            return;
+                        },
+                        // Parameter update e.g. `x = x + 1`
+                        ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs)
+                            if rhs.hir_id == child_id && path_to_local_id(lhs, id) =>
+                        {
+                            return;
+                        },
+                        // Side-effect free expressions. Walk to the parent expression.
+                        ExprKind::Binary(_, lhs, rhs)
+                            if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() =>
+                        {
+                            e = parent;
+                            continue;
+                        },
+                        ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => {
+                            e = parent;
+                            continue;
+                        },
+                        ExprKind::AddrOf(..) | ExprKind::Cast(..) => {
+                            e = parent;
+                            continue;
+                        },
+                        // Only allow field accesses without auto-deref
+                        ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
+                            e = parent;
+                            continue
+                        }
+                        _ => (),
+                    },
+                    _ => (),
+                }
+                self.params.remove_by_id(id);
+                return;
             }
         }
     }
 
-    fn visit_if(&mut self, bind: &'tcx Expr<'tcx>, then_expr: &'tcx Expr<'tcx>, else_expr: Option<&'tcx Expr<'tcx>>) {
-        let contains_side_effect = self.contains_side_effect;
-        self.contains_side_effect = false;
-        self.visit_expr(bind);
-        let mut vars = std::mem::take(&mut self.ret_vars);
-        self.visit_expr(then_expr);
-        let mut then_vars = std::mem::take(&mut self.ret_vars);
-        walk_list!(self, visit_expr, else_expr);
-        if self.contains_side_effect {
-            self.add_side_effect(vars.clone());
-        }
-        self.contains_side_effect |= contains_side_effect;
-        self.ret_vars.append(&mut vars);
-        self.ret_vars.append(&mut then_vars);
-    }
-
-    fn visit_match(&mut self, expr: &'tcx Expr<'tcx>, arms: &'tcx [Arm<'tcx>]) {
-        self.visit_expr(expr);
-        let mut expr_vars = std::mem::take(&mut self.ret_vars);
-        self.ret_vars = arms
-            .iter()
-            .flat_map(|arm| {
-                let contains_side_effect = self.contains_side_effect;
-                self.contains_side_effect = false;
-                // this would visit `expr` multiple times
-                // but couldn't think of a better way
-                self.visit_pat_expr(arm.pat, expr, false);
-                let mut vars = std::mem::take(&mut self.ret_vars);
-                let _ = arm.guard.as_ref().map(|guard| {
-                    self.visit_expr(match guard {
-                        Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
-                    });
-                    vars.append(&mut self.ret_vars);
-                });
-                self.visit_expr(arm.body);
-                if self.contains_side_effect {
-                    self.add_side_effect(vars.clone());
-                    self.add_side_effect(expr_vars.clone());
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+        if self.entered_body == Some(body.value.hir_id) {
+            self.entered_body = None;
+            self.params.flag_for_linting();
+            for param in &self.params.params {
+                if param.apply_lint.get() {
+                    span_lint_and_then(
+                        cx,
+                        ONLY_USED_IN_RECURSION,
+                        param.ident.span,
+                        "parameter is only used in recursion",
+                        |diag| {
+                            if param.ident.name != kw::SelfLower {
+                                diag.span_suggestion(
+                                    param.ident.span,
+                                    "if this is intentional, prefix it with an underscore",
+                                    format!("_{}", param.ident.name),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                            diag.span_note(
+                                param.uses.iter().map(|x| x.span).collect::<Vec<_>>(),
+                                "parameter used here",
+                            );
+                        },
+                    );
                 }
-                self.contains_side_effect |= contains_side_effect;
-                vars.append(&mut self.ret_vars);
-                vars
-            })
-            .collect();
-        self.ret_vars.append(&mut expr_vars);
-    }
-
-    fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) {
-        // if mutable dereference is on assignment it can have side-effect
-        // (this can lead to parameter mutable dereference and change the original value)
-        // too hard to detect whether this value is from parameter, so this would all
-        // check mutable dereference assignment to side effect
-        lhs.iter().filter(|(_, b)| *b).for_each(|(id, _)| {
-            self.has_side_effect.insert(*id);
-            self.contains_side_effect = true;
-        });
-
-        // there is no connection
-        if lhs.is_empty() || rhs.is_empty() {
-            return;
-        }
-
-        // by connected rhs in cycle, the connections would decrease
-        // from `n * m` to `n + m`
-        // where `n` and `m` are length of `lhs` and `rhs`.
-
-        // unwrap is possible since rhs is not empty
-        let rhs_first = rhs.first().unwrap();
-        for (id, _) in lhs.iter() {
-            if connect_self || *id != rhs_first.0 {
-                self.graph
-                    .entry(*id)
-                    .or_insert_with(FxHashSet::default)
-                    .insert(rhs_first.0);
             }
+            self.params.clear();
         }
-
-        let rhs = rhs.iter();
-        izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| {
-            if connect_self || from.0 != to.0 {
-                self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
-            }
-        });
     }
+}
 
-    fn add_side_effect(&mut self, v: Vec<(HirId, bool)>) {
-        for (id, _) in v {
-            self.has_side_effect.insert(id);
-            self.contains_side_effect = true;
-        }
+fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
+    match kind {
+        FnKind::Fn => true,
+        FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
+            GenericArgKind::Lifetime(_) => true,
+            GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
+            GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
+        }),
+        #[allow(trivial_casts)]
+        FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/open_options.rs b/src/tools/clippy/clippy_lints/src/open_options.rs
deleted file mode 100644 (file)
index 5a0b504..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::paths;
-use clippy_utils::ty::match_type;
-use rustc_ast::ast::LitKind;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::{Span, Spanned};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for duplicate open options as well as combinations
-    /// that make no sense.
-    ///
-    /// ### Why is this bad?
-    /// In the best case, the code will be harder to read than
-    /// necessary. I don't know the worst case.
-    ///
-    /// ### Example
-    /// ```rust
-    /// use std::fs::OpenOptions;
-    ///
-    /// OpenOptions::new().read(true).truncate(true);
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub NONSENSICAL_OPEN_OPTIONS,
-    correctness,
-    "nonsensical combination of options for opening a file"
-}
-
-declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
-
-impl<'tcx> LateLintPass<'tcx> for OpenOptions {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::MethodCall(path, [self_arg, ..], _) = &e.kind {
-            let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
-            if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
-                let mut options = Vec::new();
-                get_open_options(cx, self_arg, &mut options);
-                check_open_options(cx, &options, e.span);
-            }
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-enum Argument {
-    True,
-    False,
-    Unknown,
-}
-
-#[derive(Debug)]
-enum OpenOption {
-    Write,
-    Read,
-    Truncate,
-    Create,
-    Append,
-}
-
-fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
-    if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
-        let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
-
-        // Only proceed if this is a call on some object of type std::fs::OpenOptions
-        if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
-            let argument_option = match arguments[1].kind {
-                ExprKind::Lit(ref span) => {
-                    if let Spanned {
-                        node: LitKind::Bool(lit),
-                        ..
-                    } = *span
-                    {
-                        if lit { Argument::True } else { Argument::False }
-                    } else {
-                        // The function is called with a literal which is not a boolean literal.
-                        // This is theoretically possible, but not very likely.
-                        return;
-                    }
-                },
-                _ => Argument::Unknown,
-            };
-
-            match path.ident.as_str() {
-                "create" => {
-                    options.push((OpenOption::Create, argument_option));
-                },
-                "append" => {
-                    options.push((OpenOption::Append, argument_option));
-                },
-                "truncate" => {
-                    options.push((OpenOption::Truncate, argument_option));
-                },
-                "read" => {
-                    options.push((OpenOption::Read, argument_option));
-                },
-                "write" => {
-                    options.push((OpenOption::Write, argument_option));
-                },
-                _ => (),
-            }
-
-            get_open_options(cx, &arguments[0], options);
-        }
-    }
-}
-
-fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
-    let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
-    let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
-        (false, false, false, false, false);
-    // This code is almost duplicated (oh, the irony), but I haven't found a way to
-    // unify it.
-
-    for option in options {
-        match *option {
-            (OpenOption::Create, arg) => {
-                if create {
-                    span_lint(
-                        cx,
-                        NONSENSICAL_OPEN_OPTIONS,
-                        span,
-                        "the method `create` is called more than once",
-                    );
-                } else {
-                    create = true;
-                }
-                create_arg = create_arg || (arg == Argument::True);
-            },
-            (OpenOption::Append, arg) => {
-                if append {
-                    span_lint(
-                        cx,
-                        NONSENSICAL_OPEN_OPTIONS,
-                        span,
-                        "the method `append` is called more than once",
-                    );
-                } else {
-                    append = true;
-                }
-                append_arg = append_arg || (arg == Argument::True);
-            },
-            (OpenOption::Truncate, arg) => {
-                if truncate {
-                    span_lint(
-                        cx,
-                        NONSENSICAL_OPEN_OPTIONS,
-                        span,
-                        "the method `truncate` is called more than once",
-                    );
-                } else {
-                    truncate = true;
-                }
-                truncate_arg = truncate_arg || (arg == Argument::True);
-            },
-            (OpenOption::Read, arg) => {
-                if read {
-                    span_lint(
-                        cx,
-                        NONSENSICAL_OPEN_OPTIONS,
-                        span,
-                        "the method `read` is called more than once",
-                    );
-                } else {
-                    read = true;
-                }
-                read_arg = read_arg || (arg == Argument::True);
-            },
-            (OpenOption::Write, arg) => {
-                if write {
-                    span_lint(
-                        cx,
-                        NONSENSICAL_OPEN_OPTIONS,
-                        span,
-                        "the method `write` is called more than once",
-                    );
-                } else {
-                    write = true;
-                }
-                write_arg = write_arg || (arg == Argument::True);
-            },
-        }
-    }
-
-    if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
-        span_lint(
-            cx,
-            NONSENSICAL_OPEN_OPTIONS,
-            span,
-            "file opened with `truncate` and `read`",
-        );
-    }
-    if append && truncate && append_arg && truncate_arg {
-        span_lint(
-            cx,
-            NONSENSICAL_OPEN_OPTIONS,
-            span,
-            "file opened with `append` and `truncate`",
-        );
-    }
-}
index 44f153cffac511401f43c871690590fd3d29da43..9602d0d1d2ea1a725963b38f8762a4744c2c4328 100644 (file)
@@ -1,21 +1,22 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{
     can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
     peel_hir_expr_while, CaptureKind,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::LangItem::OptionSome;
-use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::{
+    def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp,
+};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
+    /// Lints usage of `if let Some(v) = ... { y } else { x }` and
+    /// `match .. { Some(v) => y, None/_ => x }` which are more
     /// idiomatically done with `Option::map_or` (if the else bit is a pure
     /// expression) or `Option::map_or_else` (if the else bit is an impure
     /// expression).
     /// } else {
     ///     5
     /// };
+    /// let _ = match optional {
+    ///     Some(val) => val + 1,
+    ///     None => 5
+    /// };
     /// let _ = if let Some(foo) = optional {
     ///     foo
     /// } else {
     /// # let optional: Option<u32> = Some(0);
     /// # fn do_complicated_function() -> u32 { 5 };
     /// let _ = optional.map_or(5, |foo| foo);
+    /// let _ = optional.map_or(5, |val| val + 1);
     /// let _ = optional.map_or_else(||{
     ///     let y = do_complicated_function();
     ///     y*y
     /// }, |foo| foo);
     /// ```
+    // FIXME: Before moving this lint out of nursery, the lint name needs to be updated. It now also
+    // covers matches and `Result`.
     #[clippy::version = "1.47.0"]
     pub OPTION_IF_LET_ELSE,
     nursery,
 
 declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
 
-/// Returns true iff the given expression is the result of calling `Result::ok`
-fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
-    if let ExprKind::MethodCall(path, &[ref receiver], _) = &expr.kind {
-        path.ident.name.as_str() == "ok"
-            && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Result)
-    } else {
-        false
-    }
-}
-
-/// A struct containing information about occurrences of the
-/// `if let Some(..) = .. else` construct that this lint detects.
-struct OptionIfLetElseOccurrence {
+/// A struct containing information about occurrences of construct that this lint detects
+///
+/// Such as:
+///
+/// ```ignore
+/// if let Some(..) = {..} else {..}
+/// ```
+/// or
+/// ```ignore
+/// match x {
+///     Some(..) => {..},
+///     None/_ => {..}
+/// }
+/// ```
+struct OptionOccurence {
     option: String,
     method_sugg: String,
     some_expr: String,
@@ -99,43 +109,38 @@ 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 `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<OptionIfLetElseOccurrence> {
+fn try_get_option_occurence<'tcx>(
+    cx: &LateContext<'tcx>,
+    pat: &Pat<'tcx>,
+    expr: &Expr<'_>,
+    if_then: &'tcx Expr<'_>,
+    if_else: &'tcx Expr<'_>,
+) -> Option<OptionOccurence> {
+    let cond_expr = match expr.kind {
+        ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr,
+        _ => expr,
+    };
+    let inner_pat = try_get_inner_pat(cx, pat)?;
     if_chain! {
-        if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
-        if !in_constant(cx, expr.hir_id);
-        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
-            = higher::IfLet::hir(cx, expr);
-        if !is_else_clause(cx.tcx, expr);
-        if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
-        if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
-        if is_lang_ctor(cx, struct_qpath, OptionSome);
-        if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind;
+        if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind;
         if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
         if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
         if some_captures
             .iter()
             .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
             .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
-
         then {
-            let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
+            let capture_mut = if bind_annotation == BindingAnnotation::Mutable { "mut " } else { "" };
             let some_body = peel_blocks(if_then);
             let none_body = peel_blocks(if_else);
             let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
             let capture_name = id.name.to_ident_string();
-            let (as_ref, as_mut) = match &let_expr.kind {
+            let (as_ref, as_mut) = match &expr.kind {
                 ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
                 ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
-                _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
-            };
-            let cond_expr = match let_expr.kind {
-                // Pointer dereferencing happens automatically, so we can omit it in the suggestion
-                ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
-                _ => let_expr,
+                _ => (bind_annotation == BindingAnnotation::Ref, bind_annotation == BindingAnnotation::RefMut),
             };
+
             // Check if captures the closure will need conflict with borrows made in the scrutinee.
             // TODO: check all the references made in the scrutinee expression. This will require interacting
             // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
@@ -154,30 +159,100 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
                     }
                 }
             }
-            Some(OptionIfLetElseOccurrence {
+
+            return Some(OptionOccurence {
                 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, "..")),
                 none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
-            })
+            });
+        }
+    }
+
+    None
+}
+
+fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> {
+    if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
+        if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) {
+            return Some(inner_pat);
+        }
+    }
+    None
+}
+
+/// If this expression is the option if let/else construct we're detecting, then
+/// this function returns an `OptionOccurence` 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<OptionOccurence> {
+    if let Some(higher::IfLet {
+        let_pat,
+        let_expr,
+        if_then,
+        if_else: Some(if_else),
+    }) = higher::IfLet::hir(cx, expr)
+    {
+        if !is_else_clause(cx.tcx, expr) {
+            return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else);
+        }
+    }
+    None
+}
+
+fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
+    if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
+        if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
+            return try_get_option_occurence(cx, let_pat, ex, if_then, if_else);
+        }
+    }
+    None
+}
+
+fn try_convert_match<'tcx>(
+    cx: &LateContext<'tcx>,
+    arms: &[Arm<'tcx>],
+) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
+    if arms.len() == 2 {
+        return if is_none_or_err_arm(cx, &arms[1]) {
+            Some((arms[0].pat, arms[0].body, arms[1].body))
+        } else if is_none_or_err_arm(cx, &arms[0]) {
+            Some((arms[1].pat, arms[1].body, arms[0].body))
         } else {
             None
-        }
+        };
+    }
+    None
+}
+
+fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+    match arm.pat.kind {
+        PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
+        PatKind::TupleStruct(ref qpath, [first_pat], _) => {
+            is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild)
+        },
+        PatKind::Wild => true,
+        _ => false,
     }
 }
 
 impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
-        if let Some(detection) = detect_option_if_let_else(cx, expr) {
+        // Don't lint macros and constants
+        if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
+            return;
+        }
+
+        let detection = detect_option_if_let_else(cx, expr).or_else(|| detect_option_match(cx, expr));
+        if let Some(det) = detection {
             span_lint_and_sugg(
                 cx,
                 OPTION_IF_LET_ELSE,
                 expr.span,
-                format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
+                format!("use Option::{} instead of an if let/else", det.method_sugg).as_str(),
                 "try",
                 format!(
                     "{}.{}({}, {})",
-                    detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
+                    det.option, det.method_sugg, det.none_expr, det.some_expr
                 ),
                 Applicability::MaybeIncorrect,
             );
index eee7642068d6237bebe5fdcae8b91624fe40518b..000b0ba7a148e75e503dd9f73ca2061ead430189 100644 (file)
@@ -53,7 +53,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 
         // If the expression is a literal `Option::None`
         let is_none_ctor = |expr: &Expr<'_>| {
-            matches!(&peel_hir_expr_refs(expr).0.kind,
+            !expr.span.from_expansion()
+                && matches!(&peel_hir_expr_refs(expr).0.kind,
             ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone))
         };
 
diff --git a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs
deleted file mode 100644 (file)
index bc6a918..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-use std::path::{Component, Path};
-
-declare_clippy_lint! {
-    /// ### What it does
-    ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
-    /// calls on `PathBuf` that can cause overwrites.
-    ///
-    /// ### Why is this bad?
-    /// Calling `push` with a root path at the start can overwrite the
-    /// previous defined path.
-    ///
-    /// ### Example
-    /// ```rust
-    /// use std::path::PathBuf;
-    ///
-    /// let mut x = PathBuf::from("/foo");
-    /// x.push("/bar");
-    /// assert_eq!(x, PathBuf::from("/bar"));
-    /// ```
-    /// Could be written:
-    ///
-    /// ```rust
-    /// use std::path::PathBuf;
-    ///
-    /// let mut x = PathBuf::from("/foo");
-    /// x.push("bar");
-    /// assert_eq!(x, PathBuf::from("/foo/bar"));
-    /// ```
-    #[clippy::version = "1.36.0"]
-    pub PATH_BUF_PUSH_OVERWRITE,
-    nursery,
-    "calling `push` with file system root on `PathBuf` can overwrite it"
-}
-
-declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
-
-impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::MethodCall(path, [recv, get_index_arg], _) = expr.kind;
-            if path.ident.name == sym!(push);
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::PathBuf);
-            if let ExprKind::Lit(ref lit) = get_index_arg.kind;
-            if let LitKind::Str(ref path_lit, _) = lit.node;
-            if let pushed_path = Path::new(path_lit.as_str());
-            if let Some(pushed_path_lit) = pushed_path.to_str();
-            if pushed_path.has_root();
-            if let Some(root) = pushed_path.components().next();
-            if root == Component::RootDir;
-            then {
-                span_lint_and_sugg(
-                    cx,
-                    PATH_BUF_PUSH_OVERWRITE,
-                    lit.span,
-                    "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
-                    "try",
-                    format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-    }
-}
index 964a057f00d32e5c1da41c7a4d1b7a3525710f0c..b432ccb1ee32dd675d054de109fd3ec690fe80f8 100644 (file)
@@ -123,7 +123,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
         if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
         if !is_else_clause(cx.tcx, expr);
         if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind;
-        if let PatKind::Binding(annot, bind_id, ident, _) = field.kind;
+        if let PatKind::Binding(annot, bind_id, ident, None) = field.kind;
         let caller_ty = cx.typeck_results().expr_ty(let_expr);
         let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
         if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
index fbf842c339e4906b3fff22a5e3a2d608bc40e047..490f345d2970777634c6554ec54cbb5fd5c040cb 100644 (file)
@@ -1,46 +1,20 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::higher;
 use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
-use clippy_utils::{higher, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
+use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
-use rustc_span::sym;
 use std::cmp::Ordering;
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for zipping a collection with the range of
-    /// `0.._.len()`.
-    ///
-    /// ### Why is this bad?
-    /// The code is better expressed with `.enumerate()`.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = vec![1];
-    /// let _ = x.iter().zip(0..x.len());
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = vec![1];
-    /// let _ = x.iter().enumerate();
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub RANGE_ZIP_WITH_LEN,
-    complexity,
-    "zipping iterator with a range when `enumerate()` would do"
-}
-
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for exclusive ranges where 1 is added to the
@@ -198,7 +172,6 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 }
 
 impl_lint_pass!(Ranges => [
-    RANGE_ZIP_WITH_LEN,
     RANGE_PLUS_ONE,
     RANGE_MINUS_ONE,
     REVERSED_EMPTY_RANGES,
@@ -207,16 +180,10 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl<'tcx> LateLintPass<'tcx> for Ranges {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        match expr.kind {
-            ExprKind::MethodCall(path, args, _) => {
-                check_range_zip_with_len(cx, path, args, expr.span);
-            },
-            ExprKind::Binary(ref op, l, r) => {
-                if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
-                    check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
-                }
-            },
-            _ => {},
+        if let ExprKind::Binary(ref op, l, r) = expr.kind {
+            if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
+                check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
+            }
         }
 
         check_exclusive_range_plus_one(cx, expr);
@@ -380,34 +347,6 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R
     None
 }
 
-fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
-    if_chain! {
-        if path.ident.as_str() == "zip";
-        if let [iter, zip_arg] = args;
-        // `.iter()` call
-        if let ExprKind::MethodCall(iter_path, [iter_caller, ..], _) = iter.kind;
-        if iter_path.ident.name == sym::iter;
-        // range expression in `.zip()` call: `0..x.len()`
-        if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
-        if is_integer_const(cx, start, 0);
-        // `.len()` call
-        if let ExprKind::MethodCall(len_path, [len_caller], _) = end.kind;
-        if len_path.ident.name == sym::len;
-        // `.iter()` and `.len()` called on same `Path`
-        if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_caller.kind;
-        if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_caller.kind;
-        if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
-        then {
-            span_lint(cx,
-                RANGE_ZIP_WITH_LEN,
-                span,
-                &format!("it is more idiomatic to use `{}.iter().enumerate()`",
-                    snippet(cx, iter_caller.span, "_"))
-            );
-        }
-    }
-}
-
 // exclusive range plus one: `x..(y+1)`
 fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if_chain! {
index 8db8c4e9b7870117d71022f7baa10809ab1c6bbf..e82aa3a7b9897dd751a40aa5c8a4f59e4de247b6 100644 (file)
@@ -41,7 +41,7 @@
     /// let data = std::rc::Rc::new("some data".to_string());
     /// let v = vec![data; 100];
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub RC_CLONE_IN_VEC_INIT,
     suspicious,
     "initializing reference-counted pointer in `vec![elem; len]`"
index db6c97f3739c74068a74e4b26ca4e9f80aae964c..8693ca9af83003f2492158e481fae972649e5276 100644 (file)
@@ -11,6 +11,8 @@
 use rustc_middle::ty::subst::GenericArg;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
+use std::iter;
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for redundant slicing expressions which use the full range, and
@@ -134,7 +136,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 } else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
                     if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
                         cx.param_env,
-                        cx.tcx.mk_projection(target_id, cx.tcx.mk_substs([GenericArg::from(indexed_ty)].into_iter())),
+                        cx.tcx.mk_projection(target_id, cx.tcx.mk_substs(iter::once(GenericArg::from(indexed_ty)))),
                     ) {
                         if deref_ty == expr_ty {
                             let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
index f8801f769e83d6cc2dddd94abac1f4bbc2f10da8..2d751c274679f9b64eef70d1b8af54d45bd7aad6 100644 (file)
@@ -48,15 +48,15 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl RedundantStaticLifetimes {
     // Recursively visit types
-    fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
+    fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
         match ty.kind {
             // Be careful of nested structures (arrays and tuples)
             TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
-                self.visit_type(ty, cx, reason);
+                Self::visit_type(ty, cx, reason);
             },
             TyKind::Tup(ref tup) => {
                 for tup_ty in tup {
-                    self.visit_type(tup_ty, cx, reason);
+                    Self::visit_type(tup_ty, cx, reason);
                 }
             },
             // This is what we are looking for !
@@ -87,7 +87,7 @@ fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
                         _ => {},
                     }
                 }
-                self.visit_type(&borrow_type.ty, cx, reason);
+                Self::visit_type(&borrow_type.ty, cx, reason);
             },
             _ => {},
         }
@@ -102,13 +102,13 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 
         if !item.span.from_expansion() {
             if let ItemKind::Const(_, ref var_type, _) = item.kind {
-                self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
+                Self::visit_type(var_type, cx, "constants have by default a `'static` lifetime");
                 // Don't check associated consts because `'static` cannot be elided on those (issue
                 // #2438)
             }
 
             if let ItemKind::Static(ref var_type, _, _) = item.kind {
-                self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
+                Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/repeat_once.rs b/src/tools/clippy/clippy_lints/src/repeat_once.rs
deleted file mode 100644 (file)
index 898c70a..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-use clippy_utils::consts::{constant_context, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
-    /// - `.to_string()` for `str`
-    /// - `.clone()` for `String`
-    /// - `.to_vec()` for `slice`
-    ///
-    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
-    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
-    ///
-    /// ### Why is this bad?
-    /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
-    /// the string is the intention behind this, `clone()` should be used.
-    ///
-    /// ### Example
-    /// ```rust
-    /// fn main() {
-    ///     let x = String::from("hello world").repeat(1);
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// fn main() {
-    ///     let x = String::from("hello world").clone();
-    /// }
-    /// ```
-    #[clippy::version = "1.47.0"]
-    pub REPEAT_ONCE,
-    complexity,
-    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
-}
-
-declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
-
-impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::MethodCall(path, [receiver, count], _) = &expr.kind;
-            if path.ident.name == sym!(repeat);
-            if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
-            if !receiver.span.from_expansion();
-            then {
-                let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
-                if ty.is_str() {
-                    span_lint_and_sugg(
-                        cx,
-                        REPEAT_ONCE,
-                        expr.span,
-                        "calling `repeat(1)` on str",
-                        "consider using `.to_string()` instead",
-                        format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
-                        Applicability::MachineApplicable,
-                    );
-                } else if ty.builtin_index().is_some() {
-                    span_lint_and_sugg(
-                        cx,
-                        REPEAT_ONCE,
-                        expr.span,
-                        "calling `repeat(1)` on slice",
-                        "consider using `.to_vec()` instead",
-                        format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
-                        Applicability::MachineApplicable,
-                    );
-                } else if is_type_diagnostic_item(cx, ty, sym::String) {
-                    span_lint_and_sugg(
-                        cx,
-                        REPEAT_ONCE,
-                        expr.span,
-                        "calling `repeat(1)` on a string literal",
-                        "consider using `.clone()` instead",
-                        format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
-                        Applicability::MachineApplicable,
-                    );
-                }
-            }
-        }
-    }
-}
index 1d9a2abf7066c8819b61a42faacde644a72eb94a..1926661c59603a886b5bfe369532d42df86a497e 100644 (file)
@@ -2,7 +2,6 @@
 use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::{fn_def_id, path_to_local_id};
 use if_chain::if_chain;
-use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
@@ -11,7 +10,6 @@
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -152,10 +150,6 @@ fn check_fn(
     }
 }
 
-fn attr_is_cfg(attr: &Attribute) -> bool {
-    attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
-}
-
 fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
     if let Some(expr) = block.expr {
         check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
@@ -178,9 +172,7 @@ fn check_final_expr<'tcx>(
     match expr.kind {
         // simple return is always "bad"
         ExprKind::Ret(ref inner) => {
-            // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
-            let attrs = cx.tcx.hir().attrs(expr.hir_id);
-            if !attrs.iter().any(attr_is_cfg) {
+            if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
                 let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
                 if !borrows {
                     emit_return_lint(
index d07c26d7c8975da63108f5ec5e9af7efd2ab8ebd..9cea4d8806710a84b9f1980361f0c2e407a64321 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::return_ty;
-use clippy_utils::ty::{contains_adt_constructor, contains_ty};
+use clippy_utils::ty::contains_adt_constructor;
 use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -66,7 +66,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<
             if !contains_adt_constructor(ret_ty, self_adt) {
                 return;
             }
-        } else if !contains_ty(ret_ty, self_ty) {
+        } else if !ret_ty.contains(self_ty) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
deleted file mode 100644 (file)
index 6d54935..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{is_slice_of_primitives, sugg::Sugg};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-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 typically better to
-    /// use an unstable sort than a stable sort.
-    ///
-    /// ### Why is this bad?
-    /// 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];
-    /// vec.sort();
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let mut vec = vec![2, 1, 3];
-    /// vec.sort_unstable();
-    /// ```
-    #[clippy::version = "1.47.0"]
-    pub STABLE_SORT_PRIMITIVE,
-    pedantic,
-    "use of sort() when sort_unstable() is equivalent"
-}
-
-declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
-
-/// The three "kinds" of sorts
-enum SortingKind {
-    Vanilla,
-    /* The other kinds of lint are currently commented out because they
-     * can map distinct values to equal ones. If the key function is
-     * provably one-to-one, or if the Cmp function conserves equality,
-     * then they could be linted on, but I don't know if we can check
-     * for that. */
-
-    /* ByKey,
-     * ByCmp, */
-}
-impl SortingKind {
-    /// The name of the stable version of this kind of sort
-    fn stable_name(&self) -> &str {
-        match self {
-            SortingKind::Vanilla => "sort",
-            /* SortingKind::ByKey => "sort_by_key",
-             * SortingKind::ByCmp => "sort_by", */
-        }
-    }
-    /// The name of the unstable version of this kind of sort
-    fn unstable_name(&self) -> &str {
-        match self {
-            SortingKind::Vanilla => "sort_unstable",
-            /* SortingKind::ByKey => "sort_unstable_by_key",
-             * SortingKind::ByCmp => "sort_unstable_by", */
-        }
-    }
-    /// Takes the name of a function call and returns the kind of sort
-    /// that corresponds to that function name (or None if it isn't)
-    fn from_stable_name(name: &str) -> Option<SortingKind> {
-        match name {
-            "sort" => Some(SortingKind::Vanilla),
-            // "sort_by" => Some(SortingKind::ByCmp),
-            // "sort_by_key" => Some(SortingKind::ByKey),
-            _ => None,
-        }
-    }
-}
-
-/// A detected instance of this lint
-struct LintDetection {
-    slice_name: String,
-    method: SortingKind,
-    method_args: String,
-    slice_type: String,
-}
-
-fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, [slice, args @ ..], _) = &expr.kind;
-        if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str());
-        if let Some(slice_type) = is_slice_of_primitives(cx, slice);
-        then {
-            let args_str = args.iter().map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
-            Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
-        } else {
-            None
-        }
-    }
-}
-
-impl LateLintPass<'_> for StableSortPrimitive {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
-            span_lint_and_then(
-                cx,
-                STABLE_SORT_PRIMITIVE,
-                expr.span,
-                format!(
-                    "used `{}` on primitive type `{}`",
-                    detection.method.stable_name(),
-                    detection.slice_type,
-                )
-                .as_str(),
-                |diag| {
-                    diag.span_suggestion(
-                        expr.span,
-                        "try",
-                        format!(
-                            "{}.{}({})",
-                            detection.slice_name,
-                            detection.method.unstable_name(),
-                            detection.method_args,
-                        ),
-                        Applicability::MachineApplicable,
-                    );
-                    diag.note(
-                        "an unstable sort typically performs faster without any observable difference for this data type",
-                    );
-                },
-            );
-        }
-    }
-}
index 0a42a31fb8cf9e0a96dac08ede5a215e3e736880..2ffa022b04f7a4b870e32717c5cd45aa189d23cf 100644 (file)
@@ -4,7 +4,7 @@
 use core::hash::{Hash, Hasher};
 use if_chain::if_chain;
 use itertools::Itertools;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -15,6 +15,7 @@
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{BytePos, Span};
+use std::collections::hash_map::Entry;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -103,7 +104,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
     fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
         self.check_type_repetition(cx, gen);
         check_trait_bound_duplication(cx, gen);
-        check_bounds_or_where_duplication(cx, gen);
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
@@ -234,35 +234,61 @@ impl Eq for SpanlessTy<'_, '_> {}
 }
 
 fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-    if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
+    if gen.span.from_expansion() {
         return;
     }
 
-    let mut map = FxHashMap::<_, Vec<_>>::default();
-    for predicate in gen.predicates {
+    // Explanation:
+    // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+    // where T: Clone + Default, { unimplemented!(); }
+    //       ^^^^^^^^^^^^^^^^^^
+    //       |
+    // collects each of these where clauses into a set keyed by generic name and comparable trait
+    // eg. (T, Clone)
+    let where_predicates = gen
+        .predicates
+        .iter()
+        .filter_map(|pred| {
+            if_chain! {
+                if pred.in_where_clause();
+                if let WherePredicate::BoundPredicate(bound_predicate) = pred;
+                if let TyKind::Path(QPath::Resolved(_, path)) =  bound_predicate.bounded_ty.kind;
+                then {
+                    return Some(
+                        rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements")
+                        .into_iter().map(|(trait_ref, _)| (path.res, trait_ref)))
+                }
+            }
+            None
+        })
+        .flatten()
+        .collect::<FxHashSet<_>>();
+
+    // Explanation:
+    // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
+    //            ^^^^^^^^^^^^^^^^^^  ^^^^^^^
+    //            |
+    // compare trait bounds keyed by generic name and comparable trait to collected where
+    // predicates eg. (T, Clone)
+    for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) {
         if_chain! {
-            if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
+            if let WherePredicate::BoundPredicate(bound_predicate) = predicate;
             if bound_predicate.origin != PredicateOrigin::ImplTrait;
             if !bound_predicate.span.from_expansion();
-            if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
-            if let Some(segment) = segments.first();
+            if let TyKind::Path(QPath::Resolved(_, path)) =  bound_predicate.bounded_ty.kind;
             then {
-                for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
-                    let trait_resolutions_direct = map.entry(segment.ident).or_default();
-                    if let Some((_, span_direct)) = trait_resolutions_direct
-                                                .iter()
-                                                .find(|(res_direct, _)| *res_direct == res_where) {
+                let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements");
+                for (trait_ref, span) in traits {
+                    let key = (path.res, trait_ref);
+                    if where_predicates.contains(&key) {
                         span_lint_and_help(
                             cx,
                             TRAIT_DUPLICATION_IN_BOUNDS,
-                            *span_direct,
+                            span,
                             "this trait bound is already specified in the where clause",
                             None,
                             "consider removing this trait bound",
-                        );
-                    }
-                    else {
-                        trait_resolutions_direct.push((res_where, span_where));
+                            );
                     }
                 }
             }
@@ -270,23 +296,11 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
     }
 }
 
-#[derive(PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 struct ComparableTraitRef(Res, Vec<Res>);
-
-fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-    if gen.span.from_expansion() {
-        return;
-    }
-
-    for predicate in gen.predicates {
-        if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
-            let msg = if predicate.in_where_clause() {
-                "these where clauses contain repeated elements"
-            } else {
-                "these bounds contain repeated elements"
-            };
-            rollup_traits(cx, bound_predicate.bounds, msg);
-        }
+impl Default for ComparableTraitRef {
+    fn default() -> Self {
+        Self(Res::Err, Vec::new())
     }
 }
 
@@ -331,7 +345,7 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
     )
 }
 
-fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
+fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> Vec<(ComparableTraitRef, Span)> {
     let mut map = FxHashMap::default();
     let mut repeated_res = false;
 
@@ -343,23 +357,33 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
         }
     };
 
+    let mut i = 0usize;
     for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
         let (comparable_bound, span_direct) = bound;
-        if map.insert(comparable_bound, span_direct).is_some() {
-            repeated_res = true;
+        match map.entry(comparable_bound) {
+            Entry::Occupied(_) => repeated_res = true,
+            Entry::Vacant(e) => {
+                e.insert((span_direct, i));
+                i += 1;
+            },
         }
     }
 
+    // Put bounds in source order
+    let mut comparable_bounds = vec![Default::default(); map.len()];
+    for (k, (v, i)) in map {
+        comparable_bounds[i] = (k, v);
+    }
+
     if_chain! {
         if repeated_res;
         if let [first_trait, .., last_trait] = bounds;
         then {
             let all_trait_span = first_trait.span().to(last_trait.span());
 
-            let mut traits = map.values()
-                .filter_map(|span| snippet_opt(cx, *span))
+            let traits = comparable_bounds.iter()
+                .filter_map(|&(_, span)| snippet_opt(cx, span))
                 .collect::<Vec<_>>();
-            traits.sort_unstable();
             let traits = traits.join(" + ");
 
             span_lint_and_sugg(
@@ -373,4 +397,6 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
             );
         }
     }
+
+    comparable_bounds
 }
index 5f3e98144f42ddfb5ccf9ce5021e652e0369a08d..424a6e9264e4b96680b6e4a09cfab81e2a36083f 100644 (file)
@@ -9,6 +9,7 @@
 mod transmute_ref_to_ref;
 mod transmute_undefined_repr;
 mod transmutes_expressible_as_ptr_casts;
+mod transmuting_null;
 mod unsound_collection_transmute;
 mod useless_transmute;
 mod utils;
     "transmute to or from a type with an undefined representation"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for transmute calls which would receive a null pointer.
+    ///
+    /// ### Why is this bad?
+    /// Transmuting a null pointer is undefined behavior.
+    ///
+    /// ### Known problems
+    /// Not all cases can be detected at the moment of this writing.
+    /// For example, variables which hold a null pointer and are then fed to a `transmute`
+    /// call, aren't detectable yet.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
+    /// ```
+    #[clippy::version = "1.35.0"]
+    pub TRANSMUTING_NULL,
+    correctness,
+    "transmutes from a null pointer to a reference, which is undefined behavior"
+}
+
 pub struct Transmute {
     msrv: Option<RustcVersion>,
 }
@@ -404,6 +427,7 @@ pub struct Transmute {
     UNSOUND_COLLECTION_TRANSMUTE,
     TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     TRANSMUTE_UNDEFINED_REPR,
+    TRANSMUTING_NULL,
 ]);
 impl Transmute {
     #[must_use]
@@ -436,6 +460,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 
                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
+                    | transmuting_null::check(cx, e, arg, to_ty)
                     | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
                     | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
index 20b348fc14f7b12f71823d410cd9f5ae2913c250..b6d7d9f5b42ec2a7c5138163a738ced1b4cb8271 100644 (file)
@@ -5,9 +5,9 @@
 use rustc_lint::LateContext;
 use rustc_middle::ty::subst::{Subst, SubstsRef};
 use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
-use rustc_span::Span;
+use rustc_span::DUMMY_SP;
 
-#[allow(clippy::too_many_lines)]
+#[expect(clippy::too_many_lines)]
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     e: &'tcx Expr<'_>,
@@ -18,116 +18,89 @@ pub(super) fn check<'tcx>(
     let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
 
     while from_ty != to_ty {
-        match reduce_refs(cx, e.span, from_ty, to_ty) {
-            ReducedTys::FromFatPtr {
-                unsized_ty,
-                to_ty: to_sub_ty,
-            } => match reduce_ty(cx, to_sub_ty) {
-                ReducedTy::TypeErasure => break,
-                ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
-                ReducedTy::Ref(to_sub_ty) => {
-                    from_ty = unsized_ty;
-                    to_ty = to_sub_ty;
-                    continue;
-                },
-                _ => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                        |diag| {
-                            if from_ty_orig.peel_refs() != unsized_ty {
-                                diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
-                            }
-                        },
-                    );
-                    return true;
-                },
+        let reduced_tys = reduce_refs(cx, from_ty, to_ty);
+        match (reduce_ty(cx, reduced_tys.from_ty), reduce_ty(cx, reduced_tys.to_ty)) {
+            // Various forms of type erasure.
+            (ReducedTy::TypeErasure { raw_ptr_only: false }, _)
+            | (_, ReducedTy::TypeErasure { raw_ptr_only: false }) => return false,
+            (ReducedTy::TypeErasure { .. }, _) if reduced_tys.from_raw_ptr => return false,
+            (_, ReducedTy::TypeErasure { .. }) if reduced_tys.to_raw_ptr => return false,
+
+            // `Repr(C)` <-> unordered type.
+            // If the first field of the `Repr(C)` type matches then the transmute is ok
+            (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty))
+            | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => {
+                from_ty = from_sub_ty;
+                to_ty = to_sub_ty;
+                continue;
             },
-            ReducedTys::ToFatPtr {
-                unsized_ty,
-                from_ty: from_sub_ty,
-            } => match reduce_ty(cx, from_sub_ty) {
-                ReducedTy::TypeErasure => break,
-                ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
-                ReducedTy::Ref(from_sub_ty) => {
-                    from_ty = from_sub_ty;
-                    to_ty = unsized_ty;
-                    continue;
-                },
-                _ => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
-                        |diag| {
-                            if to_ty_orig.peel_refs() != unsized_ty {
-                                diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
-                            }
-                        },
-                    );
-                    return true;
-                },
+            (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => {
+                from_ty = from_sub_ty;
+                to_ty = to_sub_ty;
+                continue;
             },
-            ReducedTys::ToPtr {
-                from_ty: from_sub_ty,
-                to_ty: to_sub_ty,
-            } => match reduce_ty(cx, from_sub_ty) {
-                ReducedTy::UnorderedFields(from_ty) => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                        |diag| {
-                            if from_ty_orig.peel_refs() != from_ty {
-                                diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
-                            }
-                        },
-                    );
-                    return true;
-                },
-                ReducedTy::Ref(from_sub_ty) => {
-                    from_ty = from_sub_ty;
-                    to_ty = to_sub_ty;
-                    continue;
-                },
-                _ => break,
+            (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty)))
+                if reduced_tys.from_fat_ptr =>
+            {
+                from_ty = from_sub_ty;
+                to_ty = to_sub_ty;
+                continue;
             },
-            ReducedTys::FromPtr {
-                from_ty: from_sub_ty,
-                to_ty: to_sub_ty,
-            } => match reduce_ty(cx, to_sub_ty) {
-                ReducedTy::UnorderedFields(to_ty) => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
-                        |diag| {
-                            if to_ty_orig.peel_refs() != to_ty {
-                                diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
-                            }
-                        },
-                    );
-                    return true;
-                },
-                ReducedTy::Ref(to_sub_ty) => {
-                    from_ty = from_sub_ty;
-                    to_ty = to_sub_ty;
-                    continue;
-                },
-                _ => break,
+
+            // ptr <-> ptr
+            (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty))
+                if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_))
+                    && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) =>
+            {
+                from_ty = from_sub_ty;
+                to_ty = to_sub_ty;
+                continue;
             },
-            ReducedTys::Other {
-                from_ty: from_sub_ty,
-                to_ty: to_sub_ty,
-            } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
-                (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false,
-                (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
-                    let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
+
+            // fat ptr <-> (*size, *size)
+            (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty))
+                if reduced_tys.from_fat_ptr && is_size_pair(to_ty) =>
+            {
+                return false;
+            },
+            (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_))
+                if reduced_tys.to_fat_ptr && is_size_pair(from_ty) =>
+            {
+                return false;
+            },
+
+            // fat ptr -> some struct | some struct -> fat ptr
+            (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+                    |diag| {
+                        if from_ty_orig.peel_refs() != from_ty.peel_refs() {
+                            diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+                        }
+                    },
+                );
+                return true;
+            },
+            (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
+                    |diag| {
+                        if to_ty_orig.peel_refs() != to_ty.peel_refs() {
+                            diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+                        }
+                    },
+                );
+                return true;
+            },
+
+            (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
+                let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
                         = (from_ty.kind(), to_ty.kind())
                         && from_def == to_def
                     {
@@ -138,79 +111,72 @@ pub(super) fn check<'tcx>(
                     } else {
                         None
                     };
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!(
-                            "transmute from `{}` to `{}`, both of which have an undefined layout",
-                            from_ty_orig, to_ty_orig
-                        ),
-                        |diag| {
-                            if let Some(same_adt_did) = same_adt_did {
-                                diag.note(&format!(
-                                    "two instances of the same generic type (`{}`) may have different layouts",
-                                    cx.tcx.item_name(same_adt_did)
-                                ));
-                            } else {
-                                if from_ty_orig.peel_refs() != from_ty {
-                                    diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
-                                }
-                                if to_ty_orig.peel_refs() != to_ty {
-                                    diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
-                                }
-                            }
-                        },
-                    );
-                    return true;
-                },
-                (
-                    ReducedTy::UnorderedFields(from_ty),
-                    ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
-                ) => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                        |diag| {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!(
+                        "transmute from `{}` to `{}`, both of which have an undefined layout",
+                        from_ty_orig, to_ty_orig
+                    ),
+                    |diag| {
+                        if let Some(same_adt_did) = same_adt_did {
+                            diag.note(&format!(
+                                "two instances of the same generic type (`{}`) may have different layouts",
+                                cx.tcx.item_name(same_adt_did)
+                            ));
+                        } else {
                             if from_ty_orig.peel_refs() != from_ty {
                                 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
                             }
-                        },
-                    );
-                    return true;
-                },
-                (
-                    ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
-                    ReducedTy::UnorderedFields(to_ty),
-                ) => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
-                        |diag| {
                             if to_ty_orig.peel_refs() != to_ty {
                                 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
                             }
-                        },
-                    );
-                    return true;
-                },
-                (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
-                    from_ty = from_sub_ty;
-                    to_ty = to_sub_ty;
-                    continue;
-                },
-                (
-                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
-                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
-                )
-                | (
-                    ReducedTy::UnorderedFields(_) | ReducedTy::Param,
-                    ReducedTy::UnorderedFields(_) | ReducedTy::Param,
-                ) => break,
+                        }
+                    },
+                );
+                return true;
+            },
+            (
+                ReducedTy::UnorderedFields(from_ty),
+                ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
+            ) => {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+                    |diag| {
+                        if from_ty_orig.peel_refs() != from_ty {
+                            diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+                        }
+                    },
+                );
+                return true;
+            },
+            (
+                ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
+                ReducedTy::UnorderedFields(to_ty),
+            ) => {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
+                    |diag| {
+                        if to_ty_orig.peel_refs() != to_ty {
+                            diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+                        }
+                    },
+                );
+                return true;
+            },
+            (
+                ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
+                ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
+            )
+            | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => {
+                break;
             },
         }
     }
@@ -218,64 +184,64 @@ pub(super) fn check<'tcx>(
     false
 }
 
-enum ReducedTys<'tcx> {
-    FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-    ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
-    ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-    FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-    Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
+#[expect(clippy::struct_excessive_bools)]
+struct ReducedTys<'tcx> {
+    from_ty: Ty<'tcx>,
+    to_ty: Ty<'tcx>,
+    from_raw_ptr: bool,
+    to_raw_ptr: bool,
+    from_fat_ptr: bool,
+    to_fat_ptr: bool,
 }
 
 /// Remove references so long as both types are references.
-fn reduce_refs<'tcx>(
-    cx: &LateContext<'tcx>,
-    span: Span,
-    mut from_ty: Ty<'tcx>,
-    mut to_ty: Ty<'tcx>,
-) -> ReducedTys<'tcx> {
-    loop {
-        return match (from_ty.kind(), to_ty.kind()) {
+fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> {
+    let mut from_raw_ptr = false;
+    let mut to_raw_ptr = false;
+    let (from_fat_ptr, to_fat_ptr) = loop {
+        break match (from_ty.kind(), to_ty.kind()) {
             (
                 &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
                 &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
             ) => {
+                from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_));
                 from_ty = from_sub_ty;
+                to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_));
                 to_ty = to_sub_ty;
                 continue;
             },
             (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _)
-                if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
+                if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
             {
-                ReducedTys::FromFatPtr { unsized_ty, to_ty }
+                (true, false)
             },
             (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })))
-                if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
+                if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
             {
-                ReducedTys::ToFatPtr { unsized_ty, from_ty }
+                (false, true)
             },
-            (&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => {
-                ReducedTys::FromPtr { from_ty, to_ty }
-            },
-            (_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => {
-                ReducedTys::ToPtr { from_ty, to_ty }
-            },
-            _ => ReducedTys::Other { from_ty, to_ty },
+            _ => (false, false),
         };
+    };
+    ReducedTys {
+        from_ty,
+        to_ty,
+        from_raw_ptr,
+        to_raw_ptr,
+        from_fat_ptr,
+        to_fat_ptr,
     }
 }
 
 enum ReducedTy<'tcx> {
     /// The type can be used for type erasure.
-    TypeErasure,
+    TypeErasure { raw_ptr_only: bool },
     /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
     /// sized fields with a defined order.
-    OrderedFields(Ty<'tcx>),
+    /// The second value is the first non-zero sized type.
+    OrderedFields(Ty<'tcx>, Option<Ty<'tcx>>),
     /// The type is a struct containing multiple non-zero sized fields with no defined order.
     UnorderedFields(Ty<'tcx>),
-    /// The type is a reference to the contained type.
-    Ref(Ty<'tcx>),
-    /// The type is a generic parameter.
-    Param,
     /// Any other type.
     Other(Ty<'tcx>),
 }
@@ -285,16 +251,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
     loop {
         ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
         return match *ty.kind() {
-            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure,
+            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
+                ReducedTy::TypeErasure { raw_ptr_only: false }
+            },
             ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
                 ty = sub_ty;
                 continue;
             },
-            ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
+            ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false },
             ty::Tuple(args) => {
                 let mut iter = args.iter();
                 let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
-                    return ReducedTy::OrderedFields(ty);
+                    return ReducedTy::OrderedFields(ty, None);
                 };
                 if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
                     ty = sized_ty;
@@ -309,27 +277,25 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
                     .iter()
                     .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs));
                 let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
-                    return ReducedTy::TypeErasure;
+                    return ReducedTy::TypeErasure { raw_ptr_only: false };
                 };
                 if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
                     ty = sized_ty;
                     continue;
                 }
                 if def.repr().inhibit_struct_field_reordering_opt() {
-                    ReducedTy::OrderedFields(ty)
+                    ReducedTy::OrderedFields(ty, Some(sized_ty))
                 } else {
                     ReducedTy::UnorderedFields(ty)
                 }
             },
             ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
-                ReducedTy::TypeErasure
+                ReducedTy::TypeErasure { raw_ptr_only: false }
             },
             // TODO: Check if the conversion to or from at least one of a union's fields is valid.
-            ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure,
-            ty::Foreign(_) => ReducedTy::TypeErasure,
-            ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
-            ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
-            ty::Param(_) => ReducedTy::Param,
+            ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false },
+            ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false },
+            ty::Int(_) | ty::Uint(_) => ReducedTy::TypeErasure { raw_ptr_only: true },
             _ => ReducedTy::Other(ty),
         };
     }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs
new file mode 100644 (file)
index 0000000..d8e349a
--- /dev/null
@@ -0,0 +1,61 @@
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_path_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::symbol::sym;
+
+use super::TRANSMUTING_NULL;
+
+const LINT_MSG: &str = "transmuting a known null pointer into a reference";
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
+    if !to_ty.is_ref() {
+        return false;
+    }
+
+    // Catching transmute over constants that resolve to `null`.
+    let mut const_eval_context = constant_context(cx, cx.typeck_results());
+    if_chain! {
+        if let ExprKind::Path(ref _qpath) = arg.kind;
+        if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
+        if x == 0;
+        then {
+            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+            return true;
+        }
+    }
+
+    // Catching:
+    // `std::mem::transmute(0 as *const i32)`
+    if_chain! {
+        if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
+        if let ExprKind::Lit(ref lit) = inner_expr.kind;
+        if let LitKind::Int(0, _) = lit.node;
+        then {
+            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+            return true;
+        }
+    }
+
+    // Catching:
+    // `std::mem::transmute(std::ptr::null::<i32>())`
+    if_chain! {
+        if let ExprKind::Call(func1, []) = arg.kind;
+        if is_path_diagnostic_item(cx, func1, sym::ptr_null);
+        then {
+            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+            return true;
+        }
+    }
+
+    // FIXME:
+    // Also catch transmutations of variables which are known nulls.
+    // To do this, MIR const propagation seems to be the better tool.
+    // Whenever MIR const prop routines are more developed, this will
+    // become available. As of this writing (25/03/19) it is not yet.
+    false
+}
diff --git a/src/tools/clippy/clippy_lints/src/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmuting_null.rs
deleted file mode 100644 (file)
index 7939dfe..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-use clippy_utils::consts::{constant_context, Constant};
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_expr_diagnostic_item;
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for transmute calls which would receive a null pointer.
-    ///
-    /// ### Why is this bad?
-    /// Transmuting a null pointer is undefined behavior.
-    ///
-    /// ### Known problems
-    /// Not all cases can be detected at the moment of this writing.
-    /// For example, variables which hold a null pointer and are then fed to a `transmute`
-    /// call, aren't detectable yet.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
-    /// ```
-    #[clippy::version = "1.35.0"]
-    pub TRANSMUTING_NULL,
-    correctness,
-    "transmutes from a null pointer to a reference, which is undefined behavior"
-}
-
-declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
-
-const LINT_MSG: &str = "transmuting a known null pointer into a reference";
-
-impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-
-        if_chain! {
-            if let ExprKind::Call(func, [arg]) = expr.kind;
-            if is_expr_diagnostic_item(cx, func, sym::transmute);
-
-            then {
-                // Catching transmute over constants that resolve to `null`.
-                let mut const_eval_context = constant_context(cx, cx.typeck_results());
-                if_chain! {
-                    if let ExprKind::Path(ref _qpath) = arg.kind;
-                    if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
-                    if x == 0;
-                    then {
-                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
-                    }
-                }
-
-                // Catching:
-                // `std::mem::transmute(0 as *const i32)`
-                if_chain! {
-                    if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
-                    if let ExprKind::Lit(ref lit) = inner_expr.kind;
-                    if let LitKind::Int(0, _) = lit.node;
-                    then {
-                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
-                    }
-                }
-
-                // Catching:
-                // `std::mem::transmute(std::ptr::null::<i32>())`
-                if_chain! {
-                    if let ExprKind::Call(func1, []) = arg.kind;
-                    if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
-                    then {
-                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
-                    }
-                }
-
-                // FIXME:
-                // Also catch transmutations of variables which are known nulls.
-                // To do this, MIR const propagation seems to be the better tool.
-                // Whenever MIR const prop routines are more developed, this will
-                // become available. As of this writing (25/03/19) it is not yet.
-            }
-        }
-    }
-}
index cc64d17be05520feefc3a13d3563e149a8d344de..8980283e5c82634ca1ded1a0a0be6db8672b3094 100644 (file)
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_lint_allowed;
+use clippy_utils::macros::span_is_local;
 use clippy_utils::source::snippet;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -98,6 +99,10 @@ fn escape<T: Iterator<Item = char>>(s: T) -> String {
 }
 
 fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
+    if !span_is_local(span) {
+        return;
+    }
+
     let string = snippet(cx, span, "");
     if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
         span_lint_and_sugg(
@@ -113,6 +118,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             Applicability::MachineApplicable,
         );
     }
+
     if string.chars().any(|c| c as u32 > 0x7F) {
         span_lint_and_sugg(
             cx,
@@ -128,6 +134,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             Applicability::MachineApplicable,
         );
     }
+
     if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
         span_lint_and_sugg(
             cx,
index 9f4c5555f11b7c20489ef384432a6e2e0ebc958b..9a41603f2f4ce1068d6dccbc2e61de037b7d3653 100644 (file)
@@ -45,7 +45,7 @@
     ///    let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
     ///    vec.set_len(1000);  // `MaybeUninit` can be uninitialized
     ///    ```
-    /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available:
+    /// 3. If you are on 1.60.0 or later, `Vec::spare_capacity_mut()` is available:
     ///    ```rust,ignore
     ///    let mut vec: Vec<u8> = Vec::with_capacity(1000);
     ///    let remaining = vec.spare_capacity_mut();  // `&mut [MaybeUninit<u8>]`
diff --git a/src/tools/clippy/clippy_lints/src/unit_hash.rs b/src/tools/clippy/clippy_lints/src/unit_hash.rs
deleted file mode 100644 (file)
index 88ca0cb..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Detects `().hash(_)`.
-    ///
-    /// ### Why is this bad?
-    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # use std::hash::Hash;
-    /// # use std::collections::hash_map::DefaultHasher;
-    /// # enum Foo { Empty, WithValue(u8) }
-    /// # use Foo::*;
-    /// # let mut state = DefaultHasher::new();
-    /// # let my_enum = Foo::Empty;
-    /// match my_enum {
-    ///        Empty => ().hash(&mut state),
-    ///        WithValue(x) => x.hash(&mut state),
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// # use std::hash::Hash;
-    /// # use std::collections::hash_map::DefaultHasher;
-    /// # enum Foo { Empty, WithValue(u8) }
-    /// # use Foo::*;
-    /// # let mut state = DefaultHasher::new();
-    /// # let my_enum = Foo::Empty;
-    /// match my_enum {
-    ///        Empty => 0_u8.hash(&mut state),
-    ///        WithValue(x) => x.hash(&mut state),
-    /// }
-    /// ```
-    #[clippy::version = "1.58.0"]
-    pub UNIT_HASH,
-    correctness,
-    "hashing a unit value, which does nothing"
-}
-declare_lint_pass!(UnitHash => [UNIT_HASH]);
-
-impl<'tcx> LateLintPass<'tcx> for UnitHash {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if_chain! {
-            if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
-            if name_ident.ident.name == sym::hash;
-            if let [recv, state_param] = args;
-            if cx.typeck_results().expr_ty(recv).is_unit();
-            then {
-                span_lint_and_then(
-                    cx,
-                    UNIT_HASH,
-                    expr.span,
-                    "this call to `hash` on the unit type will do nothing",
-                    |diag| {
-                        diag.span_suggestion(
-                            expr.span,
-                            "remove the call to `hash` or consider using",
-                            format!(
-                                "0_u8.hash({})",
-                                snippet(cx, state_param.span, ".."),
-                            ),
-                            Applicability::MaybeIncorrect,
-                        );
-                        diag.note("the implementation of `Hash` for `()` is a no-op");
-                    }
-                );
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
deleted file mode 100644 (file)
index ea5aadb..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, subst::GenericArgKind};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-use rustc_span::symbol::Ident;
-use std::iter;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Detects uses of `Vec::sort_by` passing in a closure
-    /// which compares the two arguments, either directly or indirectly.
-    ///
-    /// ### Why is this bad?
-    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
-    /// possible) than to use `Vec::sort_by` and a more complicated
-    /// closure.
-    ///
-    /// ### Known problems
-    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
-    /// imported by a use statement, then it will need to be added manually.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # struct A;
-    /// # impl A { fn foo(&self) {} }
-    /// # let mut vec: Vec<A> = Vec::new();
-    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// # struct A;
-    /// # impl A { fn foo(&self) {} }
-    /// # let mut vec: Vec<A> = Vec::new();
-    /// vec.sort_by_key(|a| a.foo());
-    /// ```
-    #[clippy::version = "1.46.0"]
-    pub UNNECESSARY_SORT_BY,
-    complexity,
-    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
-}
-
-declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
-
-enum LintTrigger {
-    Sort(SortDetection),
-    SortByKey(SortByKeyDetection),
-}
-
-struct SortDetection {
-    vec_name: String,
-    unstable: bool,
-}
-
-struct SortByKeyDetection {
-    vec_name: String,
-    closure_arg: String,
-    closure_body: String,
-    reverse: bool,
-    unstable: bool,
-}
-
-/// Detect if the two expressions are mirrored (identical, except one
-/// contains a and the other replaces it with b)
-fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
-    match (&a_expr.kind, &b_expr.kind) {
-        // Two boxes with mirrored contents
-        (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
-            mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
-        },
-        // Two arrays with mirrored contents
-        (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
-            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
-        },
-        // The two exprs are function calls.
-        // Check to see that the function itself and its arguments are mirrored
-        (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
-            mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
-                && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
-        },
-        // The two exprs are method calls.
-        // Check to see that the function is the same and the arguments are mirrored
-        // This is enough because the receiver of the method is listed in the arguments
-        (ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
-            left_segment.ident == right_segment.ident
-                && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
-        },
-        // Two tuples with mirrored contents
-        (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
-            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
-        },
-        // Two binary ops, which are the same operation and which have mirrored arguments
-        (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
-            left_op.node == right_op.node
-                && mirrored_exprs(left_left, a_ident, right_left, b_ident)
-                && mirrored_exprs(left_right, a_ident, right_right, b_ident)
-        },
-        // Two unary ops, which are the same operation and which have the same argument
-        (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
-            left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
-        },
-        // The two exprs are literals of some kind
-        (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
-        (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
-        (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
-            mirrored_exprs(left_block, a_ident, right_block, b_ident)
-        },
-        (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
-            left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
-        },
-        // Two paths: either one is a and the other is b, or they're identical to each other
-        (
-            ExprKind::Path(QPath::Resolved(
-                _,
-                Path {
-                    segments: left_segments,
-                    ..
-                },
-            )),
-            ExprKind::Path(QPath::Resolved(
-                _,
-                Path {
-                    segments: right_segments,
-                    ..
-                },
-            )),
-        ) => {
-            (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
-                && left_segments
-                    .iter()
-                    .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
-                || (left_segments.len() == 1
-                    && &left_segments[0].ident == a_ident
-                    && right_segments.len() == 1
-                    && &right_segments[0].ident == b_ident)
-        },
-        // Matching expressions, but one or both is borrowed
-        (
-            ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
-            ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
-        ) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
-        (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
-        (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
-        _ => false,
-    }
-}
-
-fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
-    if_chain! {
-        if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
-        if let name = name_ident.ident.name.to_ident_string();
-        if name == "sort_by" || name == "sort_unstable_by";
-        if let [vec, Expr { kind: ExprKind::Closure(Closure { body: closure_body_id, .. }), .. }] = args;
-        if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec);
-        if let closure_body = cx.tcx.hir().body(*closure_body_id);
-        if let &[
-            Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
-            Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
-        ] = &closure_body.params;
-        if let ExprKind::MethodCall(method_path, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
-        if method_path.ident.name == sym::cmp;
-        then {
-            let (closure_body, closure_arg, reverse) = if mirrored_exprs(
-                left_expr,
-                left_ident,
-                right_expr,
-                right_ident
-            ) {
-                (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
-            } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
-                (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
-            } else {
-                return None;
-            };
-            let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
-            let unstable = name == "sort_unstable_by";
-
-            if_chain! {
-            if let ExprKind::Path(QPath::Resolved(_, Path {
-                segments: [PathSegment { ident: left_name, .. }], ..
-            })) = &left_expr.kind;
-            if left_name == left_ident;
-            if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
-                implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
-            });
-                then {
-                    return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
-                }
-            }
-
-            if !expr_borrows(cx, left_expr) {
-                return Some(LintTrigger::SortByKey(SortByKeyDetection {
-                    vec_name,
-                    closure_arg,
-                    closure_body,
-                    reverse,
-                    unstable,
-                }));
-            }
-        }
-    }
-
-    None
-}
-
-fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let ty = cx.typeck_results().expr_ty(expr);
-    matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
-}
-
-impl LateLintPass<'_> for UnnecessarySortBy {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        match detect_lint(cx, expr) {
-            Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
-                cx,
-                UNNECESSARY_SORT_BY,
-                expr.span,
-                "use Vec::sort_by_key here instead",
-                "try",
-                format!(
-                    "{}.sort{}_by_key(|{}| {})",
-                    trigger.vec_name,
-                    if trigger.unstable { "_unstable" } else { "" },
-                    trigger.closure_arg,
-                    if trigger.reverse {
-                        format!("std::cmp::Reverse({})", trigger.closure_body)
-                    } else {
-                        trigger.closure_body.to_string()
-                    },
-                ),
-                if trigger.reverse {
-                    Applicability::MaybeIncorrect
-                } else {
-                    Applicability::MachineApplicable
-                },
-            ),
-            Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
-                cx,
-                UNNECESSARY_SORT_BY,
-                expr.span,
-                "use Vec::sort here instead",
-                "try",
-                format!(
-                    "{}.sort{}()",
-                    trigger.vec_name,
-                    if trigger.unstable { "_unstable" } else { "" },
-                ),
-                Applicability::MachineApplicable,
-            ),
-            None => {},
-        }
-    }
-}
index f4f5a4336a39ec7ad4ae0800207e429f986f1f4d..a5afbb8ff9da49272efd9887b20732ed221c0049 100644 (file)
@@ -130,7 +130,7 @@ fn check_fn(
                         (
                             ret_expr.span,
                             if inner_type.is_unit() {
-                                "".to_string()
+                                String::new()
                             } else {
                                 snippet(cx, arg.span.source_callsite(), "..").to_string()
                             }
diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
new file mode 100644 (file)
index 0000000..ac73173
--- /dev/null
@@ -0,0 +1,225 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators};
+use rustc_ast::Mutability;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for the creation of a `peekable` iterator that is never `.peek()`ed
+    ///
+    /// ### Why is this bad?
+    /// Creating a peekable iterator without using any of its methods is likely a mistake,
+    /// or just a leftover after a refactor.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let collection = vec![1, 2, 3];
+    /// let iter = collection.iter().peekable();
+    ///
+    /// for item in iter {
+    ///     // ...
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let collection = vec![1, 2, 3];
+    /// let iter = collection.iter();
+    ///
+    /// for item in iter {
+    ///     // ...
+    /// }
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub UNUSED_PEEKABLE,
+    suspicious,
+    "creating a peekable iterator without using any of its methods"
+}
+
+declare_lint_pass!(UnusedPeekable => [UNUSED_PEEKABLE]);
+
+impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
+        // Don't lint `Peekable`s returned from a block
+        if let Some(expr) = block.expr
+            && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr))
+            && match_type(cx, ty, &paths::PEEKABLE)
+        {
+            return;
+        }
+
+        for (idx, stmt) in block.stmts.iter().enumerate() {
+            if !stmt.span.from_expansion()
+                && let StmtKind::Local(local) = stmt.kind
+                && let PatKind::Binding(_, binding, ident, _) = local.pat.kind
+                && let Some(init) = local.init
+                && !init.span.from_expansion()
+                && let Some(ty) = cx.typeck_results().expr_ty_opt(init)
+                && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
+                && match_type(cx, ty, &paths::PEEKABLE)
+            {
+                let mut vis = PeekableVisitor::new(cx, binding);
+
+                if idx + 1 == block.stmts.len() && block.expr.is_none() {
+                    return;
+                }
+
+                for stmt in &block.stmts[idx..] {
+                    vis.visit_stmt(stmt);
+                }
+
+                if let Some(expr) = block.expr {
+                    vis.visit_expr(expr);
+                }
+
+                if !vis.found_peek_call {
+                    span_lint_and_help(
+                        cx,
+                        UNUSED_PEEKABLE,
+                        ident.span,
+                        "`peek` never called on `Peekable` iterator",
+                        None,
+                        "consider removing the call to `peekable`"
+                   );
+                }
+            }
+        }
+    }
+}
+
+struct PeekableVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    expected_hir_id: HirId,
+    found_peek_call: bool,
+}
+
+impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self {
+        Self {
+            cx,
+            expected_hir_id,
+            found_peek_call: false,
+        }
+    }
+}
+
+impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
+    fn visit_expr(&mut self, ex: &'_ Expr<'_>) {
+        if self.found_peek_call {
+            return;
+        }
+
+        if path_to_local_id(ex, self.expected_hir_id) {
+            for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) {
+                match node {
+                    Node::Expr(expr) => {
+                        match expr.kind {
+                            // some_function(peekable)
+                            //
+                            // If the Peekable is passed to a function, stop
+                            ExprKind::Call(_, args) => {
+                                if let Some(func_did) = fn_def_id(self.cx, expr)
+                                    && let Ok(into_iter_did) = self
+                                        .cx
+                                        .tcx
+                                        .lang_items()
+                                        .require(LangItem::IntoIterIntoIter)
+                                    && func_did == into_iter_did
+                                {
+                                    // Probably a for loop desugar, stop searching
+                                    return;
+                                }
+
+                                if args.iter().any(|arg| {
+                                    matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg)
+                                }) {
+                                    self.found_peek_call = true;
+                                    return;
+                                }
+                            },
+                            // Catch anything taking a Peekable mutably
+                            ExprKind::MethodCall(
+                                PathSegment {
+                                    ident: method_name_ident,
+                                    ..
+                                },
+                                [self_arg, remaining_args @ ..],
+                                _,
+                            ) => {
+                                let method_name = method_name_ident.name.as_str();
+
+                                // `Peekable` methods
+                                if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq")
+                                    && arg_is_mut_peekable(self.cx, self_arg)
+                                {
+                                    self.found_peek_call = true;
+                                    return;
+                                }
+
+                                // foo.some_method() excluding Iterator methods
+                                if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg))
+                                    && !is_trait_method(self.cx, expr, sym::Iterator)
+                                {
+                                    self.found_peek_call = true;
+                                    return;
+                                }
+
+                                // foo.by_ref(), keep checking for `peek`
+                                if method_name == "by_ref" {
+                                    continue;
+                                }
+
+                                return;
+                            },
+                            ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => {
+                            },
+                            ExprKind::AddrOf(_, Mutability::Not, _) => return,
+                            _ => {
+                                self.found_peek_call = true;
+                                return;
+                            },
+                        }
+                    },
+                    Node::Local(Local { init: Some(init), .. }) => {
+                        if arg_is_mut_peekable(self.cx, init) {
+                            self.found_peek_call = true;
+                            return;
+                        }
+
+                        break;
+                    },
+                    Node::Stmt(stmt) => match stmt.kind {
+                        StmtKind::Expr(_) | StmtKind::Semi(_) => {},
+                        _ => {
+                            self.found_peek_call = true;
+                            return;
+                        },
+                    },
+                    Node::Block(_) | Node::ExprField(_) => {},
+                    _ => {
+                        break;
+                    },
+                }
+            }
+        }
+
+        walk_expr(self, ex);
+    }
+}
+
+fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
+    if let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
+        && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
+        && match_type(cx, ty, &paths::PEEKABLE)
+    {
+        true
+    } else {
+        false
+    }
+}
index e1ec357838dbd88846e97ef43c602d33500ebe85..b8a5d4ea8c9fbe177429781c403313046f752194 100644 (file)
@@ -22,7 +22,7 @@
     /// ```rust
     /// let x = 1f32;
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub UNUSED_ROUNDING,
     nursery,
     "Uselessly rounding a whole number floating-point literal"
index 3faae9ac0d2b2af6232568015f6874955174c811..84e65d5fa0b719044d3d43705141e5656aa9f63e 100644 (file)
@@ -350,7 +350,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
     /// Lint: DISALLOWED_SCRIPT_IDENTS.
     ///
     /// The list of unicode scripts allowed to be used in the scope.
-    (allowed_scripts: Vec<String> = ["Latin"].iter().map(ToString::to_string).collect()),
+    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
     /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
     ///
     /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
@@ -379,6 +379,10 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
     ///
     /// Whether `dbg!` should be allowed in test functions
     (allow_dbg_in_tests: bool = false),
+    /// Lint: RESULT_LARGE_ERR
+    ///
+    /// The maximum size of the `Err`-variant in a `Result` returned from a function
+    (large_error_threshold: u64 = 128),
 }
 
 /// Search for the configuration file.
index 5dcacd604be45f6b0a4c4844e05ece2e13287198..eb34085a2abf3a5e5631bf182173a33cb14abb9c 100644 (file)
@@ -593,8 +593,8 @@ fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Opt
     attrs.iter().find_map(|attr| {
         if_chain! {
             // Identify attribute
-            if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
-            if let [tool_name, attr_name] = &attr_kind.path.segments[..];
+            if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind;
+            if let [tool_name, attr_name] = &attr_kind.item.path.segments[..];
             if tool_name.ident.name == sym::clippy;
             if attr_name.ident.name == sym::version;
             if let Some(version) = attr.value_str();
index 92cf42c7ad43f0459fe10051fd2ea4baac6109eb..b1148bccc2a283d025cb530f18197437bfbe9079 100644 (file)
@@ -797,7 +797,7 @@ fn get_lint_group_and_level_or_lint(
     let result = cx.lint_store.check_lint_name(
         lint_name,
         Some(sym::clippy),
-        &[Ident::with_dummy_span(sym::clippy)].into_iter().collect(),
+        &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
     );
     if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
         if let Some(group) = get_lint_group(cx, lint_lst[0]) {
diff --git a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
deleted file mode 100644 (file)
index 0fee3e8..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{match_def_path, paths};
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Finds occurrences of `Vec::resize(0, an_int)`
-    ///
-    /// ### Why is this bad?
-    /// This is probably an argument inversion mistake.
-    ///
-    /// ### Example
-    /// ```rust
-    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// vec!(1, 2, 3, 4, 5).clear()
-    /// ```
-    #[clippy::version = "1.46.0"]
-    pub VEC_RESIZE_TO_ZERO,
-    correctness,
-    "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
-}
-
-declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]);
-
-impl<'tcx> LateLintPass<'tcx> for VecResizeToZero {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let hir::ExprKind::MethodCall(path_segment, args, _) = expr.kind;
-            if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-            if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3;
-            if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind;
-            if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind;
-            then {
-                let method_call_span = expr.span.with_lo(path_segment.ident.span.lo());
-                span_lint_and_then(
-                    cx,
-                    VEC_RESIZE_TO_ZERO,
-                    expr.span,
-                    "emptying a vector with `resize`",
-                    |db| {
-                        db.help("the arguments may be inverted...");
-                        db.span_suggestion(
-                            method_call_span,
-                            "...or you can empty the vector with",
-                            "clear()".to_string(),
-                            Applicability::MaybeIncorrect,
-                        );
-                    },
-                );
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs
deleted file mode 100644 (file)
index afd0077..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::paths;
-use clippy_utils::ty::match_type;
-use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for use of File::read_to_end and File::read_to_string.
-    ///
-    /// ### Why is this bad?
-    /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
-    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
-    ///
-    /// ### Example
-    /// ```rust,no_run
-    /// # use std::io::Read;
-    /// # use std::fs::File;
-    /// let mut f = File::open("foo.txt").unwrap();
-    /// let mut bytes = Vec::new();
-    /// f.read_to_end(&mut bytes).unwrap();
-    /// ```
-    /// Can be written more concisely as
-    /// ```rust,no_run
-    /// # use std::fs;
-    /// let mut bytes = fs::read("foo.txt").unwrap();
-    /// ```
-    #[clippy::version = "1.44.0"]
-    pub VERBOSE_FILE_READS,
-    restriction,
-    "use of `File::read_to_end` or `File::read_to_string`"
-}
-
-declare_lint_pass!(VerboseFileReads => [VERBOSE_FILE_READS]);
-
-impl<'tcx> LateLintPass<'tcx> for VerboseFileReads {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if is_file_read_to_end(cx, expr) {
-            span_lint_and_help(
-                cx,
-                VERBOSE_FILE_READS,
-                expr.span,
-                "use of `File::read_to_end`",
-                None,
-                "consider using `fs::read` instead",
-            );
-        } else if is_file_read_to_string(cx, expr) {
-            span_lint_and_help(
-                cx,
-                VERBOSE_FILE_READS,
-                expr.span,
-                "use of `File::read_to_string`",
-                None,
-                "consider using `fs::read_to_string` instead",
-            );
-        }
-    }
-}
-
-fn is_file_read_to_end<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, [recv, ..], _) = expr.kind;
-        if method_name.ident.as_str() == "read_to_end";
-        if let ExprKind::Path(QPath::Resolved(None, _)) = &recv.kind;
-        let ty = cx.typeck_results().expr_ty(recv);
-        if match_type(cx, ty, &paths::FILE);
-        then {
-            return true
-        }
-    }
-    false
-}
-
-fn is_file_read_to_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, exprs, _) = expr.kind;
-        if method_name.ident.as_str() == "read_to_string";
-        if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
-        let ty = cx.typeck_results().expr_ty(&exprs[0]);
-        if match_type(cx, ty, &paths::FILE);
-        then {
-            return true
-        }
-    }
-    false
-}
index fa2383066f3f622af5353b45d5431fe33d7df445..347165d9704a100717d0e61bffe3cee3ab571269 100644 (file)
@@ -3,8 +3,9 @@
 use std::ops::{Deref, Range};
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
+use rustc_ast::ptr::P;
 use rustc_ast::token::{self, LitKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_errors::{Applicability, DiagnosticBuilder};
     "writing a literal with a format string"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// This lint warns when a named parameter in a format string is used as a positional one.
+    ///
+    /// ### Why is this bad?
+    /// It may be confused for an assignment and obfuscates which parameter is being used.
+    ///
+    /// ### Example
+    /// ```rust
+    /// println!("{}", x = 10);
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// println!("{x}", x = 10);
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub POSITIONAL_NAMED_FORMAT_PARAMETERS,
+    suspicious,
+    "named parameter in a format string is used positionally"
+}
+
 #[derive(Default)]
 pub struct Write {
     in_debug_impl: bool,
@@ -270,7 +293,8 @@ pub struct Write {
     PRINT_LITERAL,
     WRITE_WITH_NEWLINE,
     WRITELN_EMPTY_STRING,
-    WRITE_LITERAL
+    WRITE_LITERAL,
+    POSITIONAL_NAMED_FORMAT_PARAMETERS,
 ]);
 
 impl EarlyLintPass for Write {
@@ -408,6 +432,7 @@ fn newline_span(fmtstr: &StrLit) -> (Span, bool) {
 #[derive(Default)]
 struct SimpleFormatArgs {
     unnamed: Vec<Vec<Span>>,
+    complex_unnamed: Vec<Vec<Span>>,
     named: Vec<(Symbol, Vec<Span>)>,
 }
 impl SimpleFormatArgs {
@@ -419,6 +444,10 @@ fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
         })
     }
 
+    fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> {
+        self.complex_unnamed.iter().map(Vec::as_slice)
+    }
+
     fn get_named(&self, n: &Path) -> &[Span] {
         self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
     }
@@ -479,6 +508,61 @@ fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
             },
         };
     }
+
+    fn push_to_complex(&mut self, span: Span, position: usize) {
+        if self.complex_unnamed.len() <= position {
+            self.complex_unnamed.resize_with(position, Vec::new);
+            self.complex_unnamed.push(vec![span]);
+        } else {
+            let args: &mut Vec<Span> = &mut self.complex_unnamed[position];
+            args.push(span);
+        }
+    }
+
+    fn push_complex(
+        &mut self,
+        cx: &EarlyContext<'_>,
+        arg: rustc_parse_format::Argument<'_>,
+        str_lit_span: Span,
+        fmt_span: Span,
+    ) {
+        use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam, CountIsStar};
+
+        let snippet = snippet_opt(cx, fmt_span);
+
+        let end = snippet
+            .as_ref()
+            .and_then(|s| s.find(':'))
+            .or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize));
+
+        if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) {
+            let span = fmt_span.from_inner(InnerSpan::new(1, end));
+            self.push_to_complex(span, n);
+        };
+
+        if let (CountIsParam(n) | CountIsStar(n), Some(span)) = (arg.format.precision, arg.format.precision_span) {
+            // We need to do this hack as precision spans should be converted from .* to .foo$
+            let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() {
+                0
+            } else {
+                1
+            };
+
+            let span = str_lit_span.from_inner(InnerSpan {
+                start: span.start + 1,
+                end: span.end - hack,
+            });
+            self.push_to_complex(span, n);
+        };
+
+        if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) {
+            let span = str_lit_span.from_inner(InnerSpan {
+                start: span.start,
+                end: span.end - 1,
+            });
+            self.push_to_complex(span, n);
+        };
+    }
 }
 
 impl Write {
@@ -511,8 +595,8 @@ fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<Si
                 // FIXME: modify rustc's fmt string parser to give us the current span
                 span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
             }
-
             args.push(arg, span);
+            args.push_complex(cx, arg, str_lit.span, span);
         }
 
         parser.errors.is_empty().then_some(args)
@@ -566,6 +650,7 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool)
 
         let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
         let mut unnamed_args = args.get_unnamed();
+        let mut complex_unnamed_args = args.get_complex_unnamed();
         loop {
             if !parser.eat(&token::Comma) {
                 return (Some(fmtstr), expr);
@@ -577,11 +662,20 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool)
             } else {
                 return (Some(fmtstr), None);
             };
+            let complex_unnamed_arg = complex_unnamed_args.next();
+
             let (fmt_spans, lit) = match &token_expr.kind {
                 ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
-                ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) {
-                    (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
-                    _ => continue,
+                ExprKind::Assign(lhs, rhs, _) => {
+                    if let Some(span) = complex_unnamed_arg {
+                        for x in span {
+                            Self::report_positional_named_param(cx, *x, lhs, rhs);
+                        }
+                    }
+                    match (&lhs.kind, &rhs.kind) {
+                        (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
+                        _ => continue,
+                    }
                 },
                 _ => {
                     unnamed_args.next();
@@ -637,6 +731,29 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool)
         }
     }
 
+    fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) {
+        if let ExprKind::Path(_, _p) = &lhs.kind {
+            let mut applicability = Applicability::MachineApplicable;
+            let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability);
+            // We need to do this hack as precision spans should be converted from .* to .foo$
+            let hack = snippet(cx, span, "").contains('*');
+
+            span_lint_and_sugg(
+                cx,
+                POSITIONAL_NAMED_FORMAT_PARAMETERS,
+                span,
+                &format!("named parameter {} is used as a positional parameter", name),
+                "replace it with",
+                if hack {
+                    format!("{}$", name)
+                } else {
+                    format!("{}", name)
+                },
+                applicability,
+            );
+        };
+    }
+
     fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
         if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
             if fmt_str.symbol == kw::Empty {
index a688050f63a6ad659603b248aa78ca785edcf093..c36bca06507d67b8544dd51850235166b9cf0015 100644 (file)
@@ -7,6 +7,7 @@ publish = false
 [dependencies]
 arrayvec = { version = "0.7", default-features = false }
 if_chain = "1.0"
+itertools = "0.10.1"
 rustc-semver = "1.1"
 
 [features]
index 313f1f1d9a6f1dd0d64c9d7bc287ae13b970ef1d..997e773b5da4eecc3cf2ea61ae2dc0a1f131c552 100644 (file)
@@ -28,6 +28,7 @@
 extern crate rustc_lexer;
 extern crate rustc_lint;
 extern crate rustc_middle;
+extern crate rustc_parse_format;
 extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_target;
@@ -87,6 +88,7 @@
     Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
     TraitRef, TyKind, UnOp,
 };
+use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::ty as rustc_ty;
 use rustc_session::Session;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::original_sp;
+use rustc_span::source_map::SourceMap;
 use rustc_span::sym;
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{Span, DUMMY_SP};
@@ -372,15 +375,19 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 
 /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 ///
-/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
+/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
 pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
     path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
 }
 
-/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
-/// diagnostic item.
-pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
-    path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
+/// it matches the given diagnostic item.
+pub fn is_path_diagnostic_item<'tcx>(
+    cx: &LateContext<'_>,
+    maybe_path: &impl MaybePath<'tcx>,
+    diag_item: Symbol,
+) -> bool {
+    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 }
 
 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
@@ -2274,6 +2281,18 @@ pub fn walk_to_expr_usage<'tcx, T>(
     None
 }
 
+/// Checks whether a given span has any comment token
+/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
+pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
+    let Ok(snippet) = sm.span_to_snippet(span) else { return false };
+    return tokenize(&snippet).any(|token| {
+        matches!(
+            token.kind,
+            TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
+        )
+    });
+}
+
 macro_rules! op_utils {
     ($($name:ident $assign:ident)*) => {
         /// Binary operation traits like `LangItem::Add`
index a268e339bb130df99138102b739e6def1d44c640..43e53f3feebd31429644fca6943951a93720019c 100644 (file)
@@ -1,16 +1,21 @@
 #![allow(clippy::similar_names)] // `expr` and `expn`
 
+use crate::is_path_diagnostic_item;
+use crate::source::snippet_opt;
 use crate::visitors::expr_visitor_no_bodies;
 
 use arrayvec::ArrayVec;
-use if_chain::if_chain;
+use itertools::{izip, Either, Itertools};
 use rustc_ast::ast::LitKind;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
+use rustc_lexer::unescape::unescape_literal;
+use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 use rustc_lint::LateContext;
+use rustc_parse_format::{self as rpf, Alignment};
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
-use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
+use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
 use std::ops::ControlFlow;
 
 const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
@@ -332,121 +337,495 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
     }
 }
 
-/// A parsed `format_args!` expansion
+/// The format string doesn't exist in the HIR, so we reassemble it from source code
 #[derive(Debug)]
-pub struct FormatArgsExpn<'tcx> {
-    /// Span of the first argument, the format string
-    pub format_string_span: Span,
-    /// The format string split by formatted args like `{..}`
-    pub format_string_parts: Vec<Symbol>,
-    /// Values passed after the format string
-    pub value_args: Vec<&'tcx Expr<'tcx>>,
-    /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`)
-    pub formatters: Vec<(usize, Symbol)>,
-    /// List of `fmt::v1::Argument { .. }` expressions. If this is empty,
-    /// then `formatters` represents the format args (`{..}`).
-    /// If this is non-empty, it represents the format args, and the `position`
-    /// parameters within the struct expressions are indexes of `formatters`.
-    pub specs: Vec<&'tcx Expr<'tcx>>,
+pub struct FormatString {
+    /// Span of the whole format string literal, including `[r#]"`.
+    pub span: Span,
+    /// Snippet of the whole format string literal, including `[r#]"`.
+    pub snippet: String,
+    /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
+    pub style: Option<usize>,
+    /// The unescaped value of the format string, e.g. `"val – {}"` for the literal
+    /// `"val \u{2013} {}"`.
+    pub unescaped: String,
+    /// The format string split by format args like `{..}`.
+    pub parts: Vec<Symbol>,
 }
 
-impl<'tcx> FormatArgsExpn<'tcx> {
-    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
-    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
-        macro_backtrace(expr.span).find(|macro_call| {
-            matches!(
-                cx.tcx.item_name(macro_call.def_id),
-                sym::const_format_args | sym::format_args | sym::format_args_nl
-            )
-        })?;
-        let mut format_string_span: Option<Span> = None;
-        let mut format_string_parts: Vec<Symbol> = Vec::new();
-        let mut value_args: Vec<&Expr<'_>> = Vec::new();
-        let mut formatters: Vec<(usize, Symbol)> = Vec::new();
-        let mut specs: Vec<&Expr<'_>> = Vec::new();
-        expr_visitor_no_bodies(|e| {
-            // if we're still inside of the macro definition...
-            if e.span.ctxt() == expr.span.ctxt() {
-                // 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;
-                    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
-                    if path.segments.last().unwrap().ident.name == sym::ArgumentV1;
-                    if seg.ident.name.as_str().starts_with("new_");
-                    then {
-                        let val_idx = if_chain! {
-                            if val.span.ctxt() == expr.span.ctxt();
-                            if let ExprKind::Field(_, field) = val.kind;
-                            if let Ok(idx) = field.name.as_str().parse();
-                            then {
-                                // tuple index
-                                idx
-                            } else {
-                                // assume the value expression is passed directly
-                                formatters.len()
-                            }
-                        };
-                        let fmt_trait = match seg.ident.name.as_str() {
-                            "new_display" => "Display",
-                            "new_debug" => "Debug",
-                            "new_lower_exp" => "LowerExp",
-                            "new_upper_exp" => "UpperExp",
-                            "new_octal" => "Octal",
-                            "new_pointer" => "Pointer",
-                            "new_binary" => "Binary",
-                            "new_lower_hex" => "LowerHex",
-                            "new_upper_hex" => "UpperHex",
-                            _ => unreachable!(),
-                        };
-                        formatters.push((val_idx, Symbol::intern(fmt_trait)));
-                    }
-                }
-                if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind {
-                    if path.segments.last().unwrap().ident.name == sym::Argument {
-                        specs.push(e);
-                    }
+impl FormatString {
+    fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
+        // format_args!(r"a {} b \", 1);
+        //
+        // expands to
+        //
+        // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
+        //      &[::core::fmt::ArgumentV1::new_display(&1)]);
+        //
+        // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
+        let span = pieces.span;
+        let snippet = snippet_opt(cx, span)?;
+
+        let (inner, style) = match tokenize(&snippet).next()?.kind {
+            TokenKind::Literal { kind, .. } => {
+                let style = match kind {
+                    LiteralKind::Str { .. } => None,
+                    LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
+                    _ => return None,
+                };
+
+                let start = style.map_or(1, |n| 2 + n);
+                let end = snippet.len() - style.map_or(1, |n| 1 + n);
+
+                (&snippet[start..end], style)
+            },
+            _ => return None,
+        };
+
+        let mode = if style.is_some() {
+            unescape::Mode::RawStr
+        } else {
+            unescape::Mode::Str
+        };
+
+        let mut unescaped = String::with_capacity(inner.len());
+        unescape_literal(inner, mode, &mut |_, ch| {
+            unescaped.push(ch.unwrap());
+        });
+
+        let mut parts = Vec::new();
+        expr_visitor_no_bodies(|expr| {
+            if let ExprKind::Lit(lit) = &expr.kind {
+                if let LitKind::Str(symbol, _) = lit.node {
+                    parts.push(symbol);
                 }
-                // walk through the macro expansion
-                return true;
             }
-            // assume that the first expr with a differing context represents
-            // (and has the span of) the format string
-            if format_string_span.is_none() {
-                format_string_span = Some(e.span);
-                let span = e.span;
-                // walk the expr and collect string literals which are format string parts
-                expr_visitor_no_bodies(|e| {
-                    if e.span.ctxt() != span.ctxt() {
-                        // defensive check, probably doesn't happen
-                        return false;
-                    }
-                    if let ExprKind::Lit(lit) = &e.kind {
-                        if let LitKind::Str(symbol, _s) = lit.node {
-                            format_string_parts.push(symbol);
-                        }
-                    }
-                    true
-                })
-                .visit_expr(e);
+
+            true
+        })
+        .visit_expr(pieces);
+
+        Some(Self {
+            span,
+            snippet,
+            style,
+            unescaped,
+            parts,
+        })
+    }
+}
+
+struct FormatArgsValues<'tcx> {
+    /// See `FormatArgsExpn::value_args`
+    value_args: Vec<&'tcx Expr<'tcx>>,
+    /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
+    /// `value_args`
+    pos_to_value_index: Vec<usize>,
+    /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
+    format_string_span: SpanData,
+}
+
+impl<'tcx> FormatArgsValues<'tcx> {
+    fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
+        let mut pos_to_value_index = Vec::new();
+        let mut value_args = Vec::new();
+        expr_visitor_no_bodies(|expr| {
+            if expr.span.ctxt() == args.span.ctxt() {
+                // ArgumentV1::new_<format_trait>(<val>)
+                // ArgumentV1::from_usize(<val>)
+                if let ExprKind::Call(callee, [val]) = expr.kind
+                    && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
+                    && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
+                    && path.segments.last().unwrap().ident.name == sym::ArgumentV1
+                {
+                    let val_idx = if val.span.ctxt() == expr.span.ctxt()
+                        && let ExprKind::Field(_, field) = val.kind
+                        && let Ok(idx) = field.name.as_str().parse()
+                    {
+                        // tuple index
+                        idx
+                    } else {
+                        // assume the value expression is passed directly
+                        pos_to_value_index.len()
+                    };
+
+                    pos_to_value_index.push(val_idx);
+                }
+
+                true
             } else {
-                // assume that any further exprs with a differing context are value args
-                value_args.push(e);
+                // assume that any expr with a differing span is a value
+                value_args.push(expr);
+
+                false
             }
-            // don't walk anything not from the macro expansion (e.a. inputs)
-            false
         })
-        .visit_expr(expr);
-        Some(FormatArgsExpn {
-            format_string_span: format_string_span?,
-            format_string_parts,
+        .visit_expr(args);
+
+        Self {
             value_args,
-            formatters,
-            specs,
+            pos_to_value_index,
+            format_string_span,
+        }
+    }
+}
+
+/// The positions of a format argument's value, precision and width
+///
+/// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
+#[derive(Debug, Default, Copy, Clone)]
+struct ParamPosition {
+    /// The position stored in `rt::v1::Argument::position`.
+    value: usize,
+    /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
+    width: Option<usize>,
+    /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
+    precision: Option<usize>,
+}
+
+/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
+fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
+    fn parse_count(expr: &Expr<'_>) -> Option<usize> {
+        // ::core::fmt::rt::v1::Count::Param(1usize),
+        if let ExprKind::Call(ctor, [val]) = expr.kind
+            && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
+            && path.segments.last()?.ident.name == sym::Param
+            && let ExprKind::Lit(lit) = &val.kind
+            && let LitKind::Int(pos, _) = lit.node
+        {
+            Some(pos as usize)
+        } else {
+            None
+        }
+    }
+
+    if let ExprKind::AddrOf(.., array) = fmt_arg.kind
+        && let ExprKind::Array(specs) = array.kind
+    {
+        Some(specs.iter().map(|spec| {
+            let mut position = ParamPosition::default();
+
+            // ::core::fmt::rt::v1::Argument {
+            //     position: 0usize,
+            //     format: ::core::fmt::rt::v1::FormatSpec {
+            //         ..
+            //         precision: ::core::fmt::rt::v1::Count::Implied,
+            //         width: ::core::fmt::rt::v1::Count::Implied,
+            //     },
+            // }
+
+            // TODO: this can be made much nicer next sync with `Visitor::visit_expr_field`
+            if let ExprKind::Struct(_, fields, _) = spec.kind {
+                for field in fields {
+                    match (field.ident.name, &field.expr.kind) {
+                        (sym::position, ExprKind::Lit(lit)) => {
+                            if let LitKind::Int(pos, _) = lit.node {
+                                position.value = pos as usize;
+                            }
+                        },
+                        (sym::format, &ExprKind::Struct(_, spec_fields, _)) => {
+                            for spec_field in spec_fields {
+                                match spec_field.ident.name {
+                                    sym::precision => {
+                                        position.precision = parse_count(spec_field.expr);
+                                    },
+                                    sym::width => {
+                                        position.width = parse_count(spec_field.expr);
+                                    },
+                                    _ => {},
+                                }
+                            }
+                        },
+                        _ => {},
+                    }
+                }
+            }
+
+            position
+        }))
+    } else {
+        None
+    }
+}
+
+/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
+fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
+    Span::new(
+        base.lo + BytePos::from_usize(inner.start),
+        base.lo + BytePos::from_usize(inner.end),
+        base.ctxt,
+        base.parent,
+    )
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum FormatParamKind {
+    /// An implicit parameter , such as `{}` or `{:?}`.
+    Implicit,
+    /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`,
+    /// `{:.0$}` or `{:.*}`.
+    Numbered,
+    /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
+    Named(Symbol),
+    /// An implicit named parameter, such as the `y` in `format!("{y}")`.
+    NamedInline(Symbol),
+}
+
+/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
+///
+/// ```
+/// let precision = 2;
+/// format!("{:.precision$}", 0.1234);
+/// ```
+///
+/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
+/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
+#[derive(Debug, Copy, Clone)]
+pub struct FormatParam<'tcx> {
+    /// The expression this parameter refers to.
+    pub value: &'tcx Expr<'tcx>,
+    /// How this parameter refers to its `value`.
+    pub kind: FormatParamKind,
+    /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
+    ///
+    /// ```text
+    /// format!("{}, {  }, {0}, {name}", ...);
+    ///          ^    ~~    ~    ~~~~
+    /// ```
+    pub span: Span,
+}
+
+impl<'tcx> FormatParam<'tcx> {
+    fn new(
+        mut kind: FormatParamKind,
+        position: usize,
+        inner: rpf::InnerSpan,
+        values: &FormatArgsValues<'tcx>,
+    ) -> Option<Self> {
+        let value_index = *values.pos_to_value_index.get(position)?;
+        let value = *values.value_args.get(value_index)?;
+        let span = span_from_inner(values.format_string_span, inner);
+
+        // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
+        // into the format string
+        if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
+            kind = FormatParamKind::NamedInline(name);
+        }
+
+        Some(Self { value, kind, span })
+    }
+}
+
+/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
+/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
+#[derive(Debug, Copy, Clone)]
+pub enum Count<'tcx> {
+    /// Specified with a literal number, stores the value.
+    Is(usize, Span),
+    /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
+    /// `FormatParamKind::Numbered`.
+    Param(FormatParam<'tcx>),
+    /// Not specified.
+    Implied,
+}
+
+impl<'tcx> Count<'tcx> {
+    fn new(
+        count: rpf::Count<'_>,
+        position: Option<usize>,
+        inner: Option<rpf::InnerSpan>,
+        values: &FormatArgsValues<'tcx>,
+    ) -> Option<Self> {
+        Some(match count {
+            rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)),
+            rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new(
+                FormatParamKind::Named(Symbol::intern(name)),
+                position?,
+                span,
+                values,
+            )?),
+            rpf::Count::CountIsParam(_) | rpf::Count::CountIsStar(_) => {
+                Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?)
+            },
+            rpf::Count::CountImplied => Self::Implied,
+        })
+    }
+
+    pub fn is_implied(self) -> bool {
+        matches!(self, Count::Implied)
+    }
+
+    pub fn param(self) -> Option<FormatParam<'tcx>> {
+        match self {
+            Count::Param(param) => Some(param),
+            _ => None,
+        }
+    }
+}
+
+/// Specification for the formatting of an argument in the format string. See
+/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
+#[derive(Debug)]
+pub struct FormatSpec<'tcx> {
+    /// Optionally specified character to fill alignment with.
+    pub fill: Option<char>,
+    /// Optionally specified alignment.
+    pub align: Alignment,
+    /// Packed version of various flags provided, see [`rustc_parse_format::Flag`].
+    pub flags: u32,
+    /// Represents either the maximum width or the integer precision.
+    pub precision: Count<'tcx>,
+    /// The minimum width, will be padded according to `width`/`align`
+    pub width: Count<'tcx>,
+    /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
+    /// `{:?}`.
+    pub r#trait: Symbol,
+    pub trait_span: Option<Span>,
+}
+
+impl<'tcx> FormatSpec<'tcx> {
+    fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
+        Some(Self {
+            fill: spec.fill,
+            align: spec.align,
+            flags: spec.flags,
+            precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?,
+            width: Count::new(spec.width, positions.width, spec.width_span, values)?,
+            r#trait: match spec.ty {
+                "" => sym::Display,
+                "?" => sym::Debug,
+                "o" => sym!(Octal),
+                "x" => sym!(LowerHex),
+                "X" => sym!(UpperHex),
+                "p" => sym::Pointer,
+                "b" => sym!(Binary),
+                "e" => sym!(LowerExp),
+                "E" => sym!(UpperExp),
+                _ => return None,
+            },
+            trait_span: spec
+                .ty_span
+                .map(|span| span_from_inner(values.format_string_span, span)),
         })
     }
 
-    /// Finds a nested call to `format_args!` within a `format!`-like macro call
+    /// Returns true if this format spec would change the contents of a string when formatted
+    pub fn has_string_formatting(&self) -> bool {
+        self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied()
+    }
+}
+
+/// A format argument, such as `{}`, `{foo:?}`.
+#[derive(Debug)]
+pub struct FormatArg<'tcx> {
+    /// The parameter the argument refers to.
+    pub param: FormatParam<'tcx>,
+    /// How to format `param`.
+    pub format: FormatSpec<'tcx>,
+    /// span of the whole argument, `{..}`.
+    pub span: Span,
+}
+
+/// A parsed `format_args!` expansion.
+#[derive(Debug)]
+pub struct FormatArgsExpn<'tcx> {
+    /// The format string literal.
+    pub format_string: FormatString,
+    // The format arguments, such as `{:?}`.
+    pub args: Vec<FormatArg<'tcx>>,
+    /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
+    /// include this added newline.
+    pub newline: bool,
+    /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
+    /// `format!("{x} {} {y}", 1, z + 2)`.
+    value_args: Vec<&'tcx Expr<'tcx>>,
+}
+
+impl<'tcx> FormatArgsExpn<'tcx> {
+    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
+        let macro_name = macro_backtrace(expr.span)
+            .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
+            .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
+        let newline = macro_name == sym::format_args_nl;
+
+        // ::core::fmt::Arguments::new_v1(pieces, args)
+        // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
+        if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
+            && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
+            && is_path_diagnostic_item(cx, ty, sym::Arguments)
+            && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
+        {
+            let format_string = FormatString::new(cx, pieces)?;
+
+            let mut parser = rpf::Parser::new(
+                &format_string.unescaped,
+                format_string.style,
+                Some(format_string.snippet.clone()),
+                // `format_string.unescaped` does not contain the appended newline
+                false,
+                rpf::ParseMode::Format,
+            );
+
+            let parsed_args = parser
+                .by_ref()
+                .filter_map(|piece| match piece {
+                    rpf::Piece::NextArgument(a) => Some(a),
+                    rpf::Piece::String(_) => None,
+                })
+                .collect_vec();
+            if !parser.errors.is_empty() {
+                return None;
+            }
+
+            let positions = if let Some(fmt_arg) = rest.first() {
+                // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
+                // them.
+
+                Either::Left(parse_rt_fmt(fmt_arg)?)
+            } else {
+                // If no format specs are given, the positions are in the given order and there are
+                // no `precision`/`width`s to consider.
+
+                Either::Right((0..).map(|n| ParamPosition {
+                    value: n,
+                    width: None,
+                    precision: None,
+                }))
+            };
+
+            let values = FormatArgsValues::new(args, format_string.span.data());
+
+            let args = izip!(positions, parsed_args, parser.arg_places)
+                .map(|(position, parsed_arg, arg_span)| {
+                    Some(FormatArg {
+                        param: FormatParam::new(
+                            match parsed_arg.position {
+                                rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
+                                rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
+                                // NamedInline is handled by `FormatParam::new()`
+                                rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
+                            },
+                            position.value,
+                            parsed_arg.position_span,
+                            &values,
+                        )?,
+                        format: FormatSpec::new(parsed_arg.format, position, &values)?,
+                        span: span_from_inner(values.format_string_span, arg_span),
+                    })
+                })
+                .collect::<Option<Vec<_>>>()?;
+
+            Some(Self {
+                format_string,
+                args,
+                value_args: values.value_args,
+                newline,
+            })
+        } else {
+            None
+        }
+    }
+
     pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
         let mut format_args = None;
         expr_visitor_no_bodies(|e| {
@@ -466,88 +845,23 @@ pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: Expn
         format_args
     }
 
-    /// Returns a vector of `FormatArgsArg`.
-    pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
-        if self.specs.is_empty() {
-            let args = std::iter::zip(&self.value_args, &self.formatters)
-                .map(|(value, &(_, format_trait))| FormatArgsArg {
-                    value,
-                    format_trait,
-                    spec: None,
-                })
-                .collect();
-            return Some(args);
-        }
-        self.specs
-            .iter()
-            .map(|spec| {
-                if_chain! {
-                    // struct `core::fmt::rt::v1::Argument`
-                    if let ExprKind::Struct(_, fields, _) = spec.kind;
-                    if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
-                    if let ExprKind::Lit(lit) = &position_field.expr.kind;
-                    if let LitKind::Int(position, _) = lit.node;
-                    if let Ok(i) = usize::try_from(position);
-                    if let Some(&(j, format_trait)) = self.formatters.get(i);
-                    then {
-                        Some(FormatArgsArg {
-                            value: self.value_args[j],
-                            format_trait,
-                            spec: Some(spec),
-                        })
-                    } else {
-                        None
-                    }
-                }
-            })
-            .collect()
-    }
-
     /// Source callsite span of all inputs
     pub fn inputs_span(&self) -> Span {
         match *self.value_args {
-            [] => self.format_string_span,
+            [] => self.format_string.span,
             [.., last] => self
-                .format_string_span
-                .to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())),
+                .format_string
+                .span
+                .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
         }
     }
-}
 
-/// Type representing a `FormatArgsExpn`'s format arguments
-pub struct FormatArgsArg<'tcx> {
-    /// An element of `value_args` according to `position`
-    pub value: &'tcx Expr<'tcx>,
-    /// An element of `args` according to `position`
-    pub format_trait: Symbol,
-    /// An element of `specs`
-    pub spec: Option<&'tcx Expr<'tcx>>,
-}
-
-impl<'tcx> FormatArgsArg<'tcx> {
-    /// Returns true if any formatting parameters are used that would have an effect on strings,
-    /// like `{:+2}` instead of just `{}`.
-    pub fn has_string_formatting(&self) -> bool {
-        self.spec.map_or(false, |spec| {
-            // `!` because these conditions check that `self` is unformatted.
-            !if_chain! {
-                // struct `core::fmt::rt::v1::Argument`
-                if let ExprKind::Struct(_, fields, _) = spec.kind;
-                if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
-                // struct `core::fmt::rt::v1::FormatSpec`
-                if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
-                if subfields.iter().all(|field| match field.ident.name {
-                    sym::precision | sym::width => match field.expr.kind {
-                        ExprKind::Path(QPath::Resolved(_, path)) => {
-                            path.segments.last().unwrap().ident.name == sym::Implied
-                        }
-                        _ => false,
-                    }
-                    _ => true,
-                });
-                then { true } else { false }
-            }
-        })
+    /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
+    pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
+        self.args
+            .iter()
+            .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
+            .flatten()
     }
 }
 
index 9e238c6f1ac0ef84a8883d8d6c84d695ccd28cfd..62020e21c81552a98e28c431396cdb18cde6c85d 100644 (file)
@@ -13,7 +13,7 @@ macro_rules! msrv_aliases {
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
     1,62,0 { BOOL_THEN_SOME }
-    1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN }
+    1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
     1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
     1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
     1,50,0 { BOOL_THEN }
@@ -32,8 +32,8 @@ macro_rules! msrv_aliases {
     1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
     1,28,0 { FROM_BOOL }
     1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
+    1,24,0 { IS_ASCII_DIGIT }
     1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
     1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
     1,16,0 { STR_REPEAT }
-    1,24,0 { IS_ASCII_DIGIT }
 }
index 8d697a301c444c354efd1ee383a5be24eae64099..fb0d34e02eece6b1d67c0ff67f498c36ee137d74 100644 (file)
@@ -71,7 +71,6 @@
 pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
 pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
 pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 #[cfg(feature = "internal")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
@@ -96,6 +95,7 @@
 pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
 pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
+pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
 pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 #[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
index e7d670766a050203793c22970b12c09bc26c2806..5a7f9568441c90acb857177748168c1f763772ca 100644 (file)
@@ -43,14 +43,6 @@ pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool
     }
 }
 
-/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
-pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
-    ty.walk().any(|inner| match inner.unpack() {
-        GenericArgKind::Type(inner_ty) => other_ty == inner_ty,
-        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-    })
-}
-
 /// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
 /// constructor.
 pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
@@ -410,7 +402,7 @@ fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
     peel(ty, 0)
 }
 
-/// Peels off all references on the type.Returns the underlying type, the number of references
+/// Peels off all references on the type. Returns the underlying type, the number of references
 /// removed, and whether the pointer is ultimately mutable or not.
 pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
     fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
@@ -839,3 +831,53 @@ pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tc
         })
         .unwrap_or(false)
 }
+
+/// Comes up with an "at least" guesstimate for the type's size, not taking into
+/// account the layout of type parameters.
+pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
+    use rustc_middle::ty::layout::LayoutOf;
+    if !is_normalizable(cx, cx.param_env, ty) {
+        return 0;
+    }
+    match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
+        (Ok(size), _) => size,
+        (Err(_), ty::Tuple(list)) => list.as_substs().types().map(|t| approx_ty_size(cx, t)).sum(),
+        (Err(_), ty::Array(t, n)) => {
+            n.try_eval_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
+        },
+        (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
+            .variants()
+            .iter()
+            .map(|v| {
+                v.fields
+                    .iter()
+                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+                    .sum::<u64>()
+            })
+            .sum(),
+        (Err(_), ty::Adt(def, subst)) if def.is_enum() => def
+            .variants()
+            .iter()
+            .map(|v| {
+                v.fields
+                    .iter()
+                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+                    .sum::<u64>()
+            })
+            .max()
+            .unwrap_or_default(),
+        (Err(_), ty::Adt(def, subst)) if def.is_union() => def
+            .variants()
+            .iter()
+            .map(|v| {
+                v.fields
+                    .iter()
+                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+                    .max()
+                    .unwrap_or_default()
+            })
+            .max()
+            .unwrap_or_default(),
+        (Err(_), _) => 0,
+    }
+}
index 7e14df4feea66316953a53425dcde75eee8bf066..85b60fefd60fc4ab84a271c50ebc510bd7449221 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-08-11"
+channel = "nightly-2022-08-27"
 components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index 5f289918a7c1306e8d908c7456e0c922dd36ef37..429dddc42ea9157f6830be6792489b3ab3d08f95 100644 (file)
@@ -84,7 +84,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 #[must_use]
 pub fn get_commit_hash() -> Option<String> {
     std::process::Command::new("git")
-        .args(&["rev-parse", "--short", "HEAD"])
+        .args(["rev-parse", "--short", "HEAD"])
         .output()
         .ok()
         .and_then(|r| String::from_utf8(r.stdout).ok())
@@ -93,7 +93,7 @@ pub fn get_commit_hash() -> Option<String> {
 #[must_use]
 pub fn get_commit_date() -> Option<String> {
     std::process::Command::new("git")
-        .args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
+        .args(["log", "-1", "--date=short", "--pretty=format:%cd"])
         .output()
         .ok()
         .and_then(|r| String::from_utf8(r.stdout).ok())
index 0defd45b68b064745537b02321f5bb2b56c688aa..e106583de4a2eb0a099d145cafe71b60453c2ef2 100644 (file)
@@ -13,7 +13,7 @@ fn fmt() {
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
     let output = Command::new("cargo")
         .current_dir(root_dir)
-        .args(&["dev", "fmt", "--check"])
+        .args(["dev", "fmt", "--check"])
         .output()
         .unwrap();
 
index 92ac1a2be56142e896c4ea0a9226055bbda56ea3..ba6186e599e9637f0481df911ab18085ee3a441f 100644 (file)
@@ -393,8 +393,8 @@ fn compile_test() {
     "search_is_some.rs",
     "single_component_path_imports_nested_first.rs",
     "string_add.rs",
+    "suspicious_to_owned.rs",
     "toplevel_ref_arg_non_rustfix.rs",
-    "trait_duplication_in_bounds.rs",
     "unit_arg.rs",
     "unnecessary_clone.rs",
     "unnecessary_lazy_eval_unfixable.rs",
@@ -404,16 +404,23 @@ fn compile_test() {
 ];
 
 fn check_rustfix_coverage() {
-    let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt");
+    let missing_coverage_path = Path::new("debug/test/ui/rustfix_missing_coverage.txt");
+    let missing_coverage_path = if let Ok(target_dir) = std::env::var("CARGO_TARGET_DIR") {
+        PathBuf::from(target_dir).join(missing_coverage_path)
+    } else {
+        missing_coverage_path.to_path_buf()
+    };
 
     if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) {
         assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new));
 
-        for rs_path in missing_coverage_contents.lines() {
-            if Path::new(rs_path).starts_with("tests/ui/crashes") {
+        for rs_file in missing_coverage_contents.lines() {
+            let rs_path = Path::new(rs_file);
+            if rs_path.starts_with("tests/ui/crashes") {
                 continue;
             }
-            let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap();
+            assert!(rs_path.starts_with("tests/ui/"), "{:?}", rs_file);
+            let filename = rs_path.strip_prefix("tests/ui/").unwrap();
             assert!(
                 RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS
                     .binary_search_by_key(&filename, Path::new)
@@ -421,7 +428,7 @@ fn check_rustfix_coverage() {
                 "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \
                 Please either add `// run-rustfix` at the top of the file or add the file to \
                 `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.",
-                rs_path,
+                rs_file,
             );
         }
     }
index 5697e8680cd6f86779a303edfd5b63d940e8da14..961525bbd9101dfbc6759f69ba3f05d7aa94e170 100644 (file)
@@ -87,11 +87,11 @@ fn run_clippy_for_package(project: &str, args: &[&str]) {
 
     if cfg!(feature = "internal") {
         // internal lints only exist if we build with the internal feature
-        command.args(&["-D", "clippy::internal"]);
+        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"]);
+        command.args(["-A", "unknown_lints"]);
     }
 
     let output = command.output().unwrap();
index c64425fa01a42a36b8351e934ae81523d75b7129..23a9bef3ccceaee4b9df52ecb40afb848e8f1873 100644 (file)
@@ -19,7 +19,7 @@ fn integration_test() {
     repo_dir.push(crate_name);
 
     let st = Command::new("git")
-        .args(&[
+        .args([
             OsStr::new("clone"),
             OsStr::new("--depth=1"),
             OsStr::new(&repo_url),
@@ -37,7 +37,7 @@ fn integration_test() {
         .current_dir(repo_dir)
         .env("RUST_BACKTRACE", "full")
         .env("CARGO_TARGET_DIR", target_dir)
-        .args(&[
+        .args([
             "clippy",
             "--all-targets",
             "--all-features",
index c3aae1a9aa2d01992ee0c00b6c847bc869b033d0..2e0f4e76075b38a1eaec8e6380001dcd5109886a 100644 (file)
@@ -19,7 +19,7 @@ fn new(path: PathBuf) -> Self {
         // we don't want the first letter after "error: ", "help: " ... to be capitalized
         // also no punctuation (except for "?" ?) at the end of a line
         static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
-            RegexSet::new(&[
+            RegexSet::new([
                 r"error: [A-Z]",
                 r"help: [A-Z]",
                 r"warning: [A-Z]",
@@ -37,7 +37,7 @@ fn new(path: PathBuf) -> Self {
         // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
         // we want to ask a question ending in "?"
         static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| {
-            RegexSet::new(&[
+            RegexSet::new([
                 r"\.\.\.$",
                 r".*C-like enum variant discriminant is not portable to 32-bit targets",
                 r".*Intel x86 assembly syntax used",
index 9f8e778b3b9d1d36b526de62226c03462ab1dff4..a52a0b5289fe4839f22bc68f9ec1bbfb206fc6e2 100644 (file)
@@ -19,6 +19,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
            enforced-import-renames
            enum-variant-name-threshold
            enum-variant-size-threshold
+           large-error-threshold
            literal-representation-threshold
            max-fn-params-bools
            max-include-file-size
index 0d65071af15ed1c4694daec5b20dac9baa96e56a..6f0485b5279b1112c173c8a5d07cfcbb872bd0d3 100644 (file)
@@ -14,31 +14,31 @@ fn is_rust_file(filename: &str) -> bool {
 
 fn main() {
     // std::string::String and &str should trigger the lint failure with .ext12
-    let _ = String::from("").ends_with(".ext12");
+    let _ = String::new().ends_with(".ext12");
     let _ = "str".ends_with(".ext12");
 
     // The test struct should not trigger the lint failure with .ext12
     TestStruct {}.ends_with(".ext12");
 
     // std::string::String and &str should trigger the lint failure with .EXT12
-    let _ = String::from("").ends_with(".EXT12");
+    let _ = String::new().ends_with(".EXT12");
     let _ = "str".ends_with(".EXT12");
 
     // The test struct should not trigger the lint failure with .EXT12
     TestStruct {}.ends_with(".EXT12");
 
     // Should not trigger the lint failure with .eXT12
-    let _ = String::from("").ends_with(".eXT12");
+    let _ = String::new().ends_with(".eXT12");
     let _ = "str".ends_with(".eXT12");
     TestStruct {}.ends_with(".eXT12");
 
     // Should not trigger the lint failure with .EXT123 (too long)
-    let _ = String::from("").ends_with(".EXT123");
+    let _ = String::new().ends_with(".EXT123");
     let _ = "str".ends_with(".EXT123");
     TestStruct {}.ends_with(".EXT123");
 
     // Shouldn't fail if it doesn't start with a dot
-    let _ = String::from("").ends_with("a.ext");
+    let _ = String::new().ends_with("a.ext");
     let _ = "str".ends_with("a.extA");
     TestStruct {}.ends_with("a.ext");
 }
index 05b98169f2d17b0dd1be7812b07acd5236fb566b..5d9a043edb9a5357dbce51e67c236a5f838901a2 100644 (file)
@@ -8,10 +8,10 @@ LL |     filename.ends_with(".rs")
    = help: consider using a case-insensitive comparison instead
 
 error: case-sensitive file extension comparison
-  --> $DIR/case_sensitive_file_extension_comparisons.rs:17:30
+  --> $DIR/case_sensitive_file_extension_comparisons.rs:17:27
    |
-LL |     let _ = String::from("").ends_with(".ext12");
-   |                              ^^^^^^^^^^^^^^^^^^^
+LL |     let _ = String::new().ends_with(".ext12");
+   |                           ^^^^^^^^^^^^^^^^^^^
    |
    = help: consider using a case-insensitive comparison instead
 
@@ -24,10 +24,10 @@ LL |     let _ = "str".ends_with(".ext12");
    = help: consider using a case-insensitive comparison instead
 
 error: case-sensitive file extension comparison
-  --> $DIR/case_sensitive_file_extension_comparisons.rs:24:30
+  --> $DIR/case_sensitive_file_extension_comparisons.rs:24:27
    |
-LL |     let _ = String::from("").ends_with(".EXT12");
-   |                              ^^^^^^^^^^^^^^^^^^^
+LL |     let _ = String::new().ends_with(".EXT12");
+   |                           ^^^^^^^^^^^^^^^^^^^
    |
    = help: consider using a case-insensitive comparison instead
 
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed
new file mode 100644 (file)
index 0000000..b70c191
--- /dev/null
@@ -0,0 +1,24 @@
+// run-rustfix
+#![warn(clippy::cast_slice_from_raw_parts)]
+
+#[allow(unused_imports, unused_unsafe)]
+fn main() {
+    let mut vec = vec![0u8; 1];
+    let ptr: *const u8 = vec.as_ptr();
+    let mptr = vec.as_mut_ptr();
+    let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) };
+    let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) };
+    let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+    {
+        use core::slice;
+        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        use slice as one;
+        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+    }
+    {
+        use std::slice;
+        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        use slice as one;
+        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs
new file mode 100644 (file)
index 0000000..c1b3167
--- /dev/null
@@ -0,0 +1,24 @@
+// run-rustfix
+#![warn(clippy::cast_slice_from_raw_parts)]
+
+#[allow(unused_imports, unused_unsafe)]
+fn main() {
+    let mut vec = vec![0u8; 1];
+    let ptr: *const u8 = vec.as_ptr();
+    let mptr = vec.as_mut_ptr();
+    let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
+    let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
+    let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
+    {
+        use core::slice;
+        let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+        use slice as one;
+        let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+    }
+    {
+        use std::slice;
+        let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+        use slice as one;
+        let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr
new file mode 100644 (file)
index 0000000..f07801c
--- /dev/null
@@ -0,0 +1,46 @@
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> $DIR/cast_raw_slice_pointer_cast.rs:9:35
+   |
+LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+   |
+   = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings`
+
+error: casting the result of `from_raw_parts_mut` to *mut [u8]
+  --> $DIR/cast_raw_slice_pointer_cast.rs:10:35
+   |
+LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> $DIR/cast_raw_slice_pointer_cast.rs:11:26
+   |
+LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> $DIR/cast_raw_slice_pointer_cast.rs:14:30
+   |
+LL |         let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> $DIR/cast_raw_slice_pointer_cast.rs:16:30
+   |
+LL |         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> $DIR/cast_raw_slice_pointer_cast.rs:20:30
+   |
+LL |         let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> $DIR/cast_raw_slice_pointer_cast.rs:22:30
+   |
+LL |         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/collapsible_str_replace.fixed b/src/tools/clippy/tests/ui/collapsible_str_replace.fixed
new file mode 100644 (file)
index 0000000..49fc9a9
--- /dev/null
@@ -0,0 +1,73 @@
+// run-rustfix
+
+#![warn(clippy::collapsible_str_replace)]
+
+fn get_filter() -> char {
+    'u'
+}
+
+fn main() {
+    let d = 'd';
+    let p = 'p';
+    let s = 's';
+    let u = 'u';
+    let l = "l";
+
+    let mut iter = ["l", "z"].iter();
+
+    // LINT CASES
+    let _ = "hesuo worpd".replace(['s', 'u'], "l");
+
+    let _ = "hesuo worpd".replace(['s', 'u'], l);
+
+    let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l");
+
+    let _ = "hesuo worpd"
+        .replace(['s', 'u', 'p', 'd'], "l");
+
+    let _ = "hesuo world".replace([s, 'u'], "l");
+
+    let _ = "hesuo worpd".replace([s, 'u', 'p'], "l");
+
+    let _ = "hesuo worpd".replace([s, u, 'p'], "l");
+
+    let _ = "hesuo worpd".replace([s, u, p], "l");
+
+    let _ = "hesuo worlp".replace(['s', 'u'], "l").replace('p', "d");
+
+    let _ = "hesuo worpd".replace('s', "x").replace(['u', 'p'], "l");
+
+    // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")`
+    let _ = "hesudo worpd".replace("su", "l").replace(['d', 'p'], "l");
+
+    let _ = "hesudo worpd".replace([d, 'p'], "l").replace("su", "l");
+
+    let _ = "hesuo world".replace([get_filter(), 's'], "l");
+
+    // NO LINT CASES
+    let _ = "hesuo world".replace('s', "l").replace('u', "p");
+
+    let _ = "hesuo worpd".replace('s', "l").replace('p', l);
+
+    let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l");
+
+    // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]`
+    let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l");
+
+    let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l");
+
+    let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l");
+
+    let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l");
+
+    let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l);
+
+    let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l");
+
+    let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l");
+
+    // Regression test
+    let _ = "hesuo worpd"
+        .replace('u', iter.next().unwrap())
+        .replace('s', iter.next().unwrap());
+}
diff --git a/src/tools/clippy/tests/ui/collapsible_str_replace.rs b/src/tools/clippy/tests/ui/collapsible_str_replace.rs
new file mode 100644 (file)
index 0000000..e3e25c4
--- /dev/null
@@ -0,0 +1,76 @@
+// run-rustfix
+
+#![warn(clippy::collapsible_str_replace)]
+
+fn get_filter() -> char {
+    'u'
+}
+
+fn main() {
+    let d = 'd';
+    let p = 'p';
+    let s = 's';
+    let u = 'u';
+    let l = "l";
+
+    let mut iter = ["l", "z"].iter();
+
+    // LINT CASES
+    let _ = "hesuo worpd".replace('s', "l").replace('u', "l");
+
+    let _ = "hesuo worpd".replace('s', l).replace('u', l);
+
+    let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l");
+
+    let _ = "hesuo worpd"
+        .replace('s', "l")
+        .replace('u', "l")
+        .replace('p', "l")
+        .replace('d', "l");
+
+    let _ = "hesuo world".replace(s, "l").replace('u', "l");
+
+    let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l");
+
+    let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l");
+
+    let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l");
+
+    let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d");
+
+    let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l");
+
+    // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")`
+    let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l");
+
+    let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l");
+
+    let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l");
+
+    // NO LINT CASES
+    let _ = "hesuo world".replace('s', "l").replace('u', "p");
+
+    let _ = "hesuo worpd".replace('s', "l").replace('p', l);
+
+    let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l");
+
+    // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]`
+    let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l");
+
+    let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l");
+
+    let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l");
+
+    let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l");
+
+    let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l);
+
+    let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l");
+
+    let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l");
+
+    // Regression test
+    let _ = "hesuo worpd"
+        .replace('u', iter.next().unwrap())
+        .replace('s', iter.next().unwrap());
+}
diff --git a/src/tools/clippy/tests/ui/collapsible_str_replace.stderr b/src/tools/clippy/tests/ui/collapsible_str_replace.stderr
new file mode 100644 (file)
index 0000000..8e3daf3
--- /dev/null
@@ -0,0 +1,86 @@
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:19:27
+   |
+LL |     let _ = "hesuo worpd".replace('s', "l").replace('u', "l");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")`
+   |
+   = note: `-D clippy::collapsible-str-replace` implied by `-D warnings`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:21:27
+   |
+LL |     let _ = "hesuo worpd".replace('s', l).replace('u', l);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:23:27
+   |
+LL |     let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:26:10
+   |
+LL |           .replace('s', "l")
+   |  __________^
+LL | |         .replace('u', "l")
+LL | |         .replace('p', "l")
+LL | |         .replace('d', "l");
+   | |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:31:27
+   |
+LL |     let _ = "hesuo world".replace(s, "l").replace('u', "l");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:33:27
+   |
+LL |     let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:35:27
+   |
+LL |     let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:37:27
+   |
+LL |     let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:39:27
+   |
+LL |     let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:41:45
+   |
+LL |     let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l");
+   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:44:47
+   |
+LL |     let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l");
+   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:46:28
+   |
+LL |     let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l");
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")`
+
+error: used consecutive `str::replace` call
+  --> $DIR/collapsible_str_replace.rs:48:27
+   |
+LL |     let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")`
+
+error: aborting due to 13 previous errors
+
index 1073acf6f0cd66b51c33588bfa7296e1a9364a57..d742595e14d4c17dfd987a6dbaab4aca98916da5 100644 (file)
@@ -6,8 +6,9 @@ fn expect_option() {
 }
 
 fn expect_result() {
-    let res: Result<u8, ()> = Ok(0);
+    let res: Result<u8, u8> = Ok(0);
     let _ = res.expect("");
+    let _ = res.expect_err("");
 }
 
 fn main() {
index ab28aac45563b1cd7bfd3192ad5b192f86638451..904c090464523c17dce9196547b56f306115afdb 100644 (file)
@@ -15,5 +15,13 @@ LL |     let _ = res.expect("");
    |
    = help: if this value is an `Err`, it will panic
 
-error: aborting due to 2 previous errors
+error: used `expect_err()` on `a Result` value
+  --> $DIR/expect.rs:11:13
+   |
+LL |     let _ = res.expect_err("");
+   |             ^^^^^^^^^^^^^^^^^^
+   |
+   = help: if this value is an `Ok`, it will panic
+
+error: aborting due to 3 previous errors
 
index ae7805fdf018eea7228cecc02adcb4781173768e..c86a502d15f0967fcf2e448d62f35f0886bdeff9 100644 (file)
@@ -5,6 +5,7 @@ fn main() {
     let x = 2f32;
     let _ = x.exp_m1();
     let _ = x.exp_m1() + 2.0;
+    let _ = (x as f32).exp_m1() + 2.0;
     // Cases where the lint shouldn't be applied
     let _ = x.exp() - 2.0;
     let _ = x.exp() - 1.0 * 2.0;
index 27e0b9bcbc937a829e9e1d9ce7d7fb286c0c63d6..e59589f912a21accd00d9d9306a70b5df937197a 100644 (file)
@@ -5,6 +5,7 @@ fn main() {
     let x = 2f32;
     let _ = x.exp() - 1.0;
     let _ = x.exp() - 1.0 + 2.0;
+    let _ = (x as f32).exp() - 1.0 + 2.0;
     // Cases where the lint shouldn't be applied
     let _ = x.exp() - 2.0;
     let _ = x.exp() - 1.0 * 2.0;
index 5cd999ad47cdd0d768138c901b1a9df01d891d8b..f84eede19872a2923b409a5d32c3fddb5f03e8b7 100644 (file)
@@ -13,16 +13,22 @@ LL |     let _ = x.exp() - 1.0 + 2.0;
    |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 
 error: (e.pow(x) - 1) can be computed more accurately
-  --> $DIR/floating_point_exp.rs:13:13
+  --> $DIR/floating_point_exp.rs:8:13
+   |
+LL |     let _ = (x as f32).exp() - 1.0 + 2.0;
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).exp_m1()`
+
+error: (e.pow(x) - 1) can be computed more accurately
+  --> $DIR/floating_point_exp.rs:14:13
    |
 LL |     let _ = x.exp() - 1.0;
    |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 
 error: (e.pow(x) - 1) can be computed more accurately
-  --> $DIR/floating_point_exp.rs:14:13
+  --> $DIR/floating_point_exp.rs:15:13
    |
 LL |     let _ = x.exp() - 1.0 + 2.0;
    |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
index 5b487bb8fcf790cae0bb771ae964327b04036cc5..4def9300bb7d2f70dae72beb47af6be5586fd03c 100644 (file)
@@ -12,6 +12,7 @@ fn check_log_base() {
     let _ = x.ln();
     let _ = x.log2();
     let _ = x.ln();
+    let _ = (x as f32).log2();
 
     let x = 1f64;
     let _ = x.log2();
index 01181484e7dee290c0973d60a38a93bd71062905..1e04caa7d2a865070f83c44e465dfe672ea1514a 100644 (file)
@@ -12,6 +12,7 @@ fn check_log_base() {
     let _ = x.log(std::f32::consts::E);
     let _ = x.log(TWO);
     let _ = x.log(E);
+    let _ = (x as f32).log(2f32);
 
     let x = 1f64;
     let _ = x.log(2f64);
index 96e5a15444170f4fd2d9453ac15774af596f86d7..89800a13a6ecc75ed966bc41d57b090427b0fc50 100644 (file)
@@ -31,25 +31,31 @@ LL |     let _ = x.log(E);
    |             ^^^^^^^^ help: consider using: `x.ln()`
 
 error: logarithm for bases 2, 10 and e can be computed more accurately
-  --> $DIR/floating_point_log.rs:17:13
+  --> $DIR/floating_point_log.rs:15:13
+   |
+LL |     let _ = (x as f32).log(2f32);
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()`
+
+error: logarithm for bases 2, 10 and e can be computed more accurately
+  --> $DIR/floating_point_log.rs:18:13
    |
 LL |     let _ = x.log(2f64);
    |             ^^^^^^^^^^^ help: consider using: `x.log2()`
 
 error: logarithm for bases 2, 10 and e can be computed more accurately
-  --> $DIR/floating_point_log.rs:18:13
+  --> $DIR/floating_point_log.rs:19:13
    |
 LL |     let _ = x.log(10f64);
    |             ^^^^^^^^^^^^ help: consider using: `x.log10()`
 
 error: logarithm for bases 2, 10 and e can be computed more accurately
-  --> $DIR/floating_point_log.rs:19:13
+  --> $DIR/floating_point_log.rs:20:13
    |
 LL |     let _ = x.log(std::f64::consts::E);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:24:13
+  --> $DIR/floating_point_log.rs:25:13
    |
 LL |     let _ = (1f32 + 2.).ln();
    |             ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
@@ -57,118 +63,118 @@ LL |     let _ = (1f32 + 2.).ln();
    = note: `-D clippy::imprecise-flops` implied by `-D warnings`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:25:13
+  --> $DIR/floating_point_log.rs:26:13
    |
 LL |     let _ = (1f32 + 2.0).ln();
    |             ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:26:13
+  --> $DIR/floating_point_log.rs:27:13
    |
 LL |     let _ = (1.0 + x).ln();
    |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:27:13
+  --> $DIR/floating_point_log.rs:28:13
    |
 LL |     let _ = (1.0 + x / 2.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:28:13
+  --> $DIR/floating_point_log.rs:29:13
    |
 LL |     let _ = (1.0 + x.powi(3)).ln();
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:29:13
+  --> $DIR/floating_point_log.rs:30:13
    |
 LL |     let _ = (1.0 + x.powi(3) / 2.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:30:13
+  --> $DIR/floating_point_log.rs:31:13
    |
 LL |     let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:31:13
+  --> $DIR/floating_point_log.rs:32:13
    |
 LL |     let _ = (x + 1.0).ln();
    |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:32:13
+  --> $DIR/floating_point_log.rs:33:13
    |
 LL |     let _ = (x.powi(3) + 1.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:33:13
+  --> $DIR/floating_point_log.rs:34:13
    |
 LL |     let _ = (x + 2.0 + 1.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:34:13
+  --> $DIR/floating_point_log.rs:35:13
    |
 LL |     let _ = (x / 2.0 + 1.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:42:13
+  --> $DIR/floating_point_log.rs:43:13
    |
 LL |     let _ = (1f64 + 2.).ln();
    |             ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:43:13
+  --> $DIR/floating_point_log.rs:44:13
    |
 LL |     let _ = (1f64 + 2.0).ln();
    |             ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:44:13
+  --> $DIR/floating_point_log.rs:45:13
    |
 LL |     let _ = (1.0 + x).ln();
    |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:45:13
+  --> $DIR/floating_point_log.rs:46:13
    |
 LL |     let _ = (1.0 + x / 2.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:46:13
+  --> $DIR/floating_point_log.rs:47:13
    |
 LL |     let _ = (1.0 + x.powi(3)).ln();
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:47:13
+  --> $DIR/floating_point_log.rs:48:13
    |
 LL |     let _ = (x + 1.0).ln();
    |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:48:13
+  --> $DIR/floating_point_log.rs:49:13
    |
 LL |     let _ = (x.powi(3) + 1.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:49:13
+  --> $DIR/floating_point_log.rs:50:13
    |
 LL |     let _ = (x + 2.0 + 1.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
-  --> $DIR/floating_point_log.rs:50:13
+  --> $DIR/floating_point_log.rs:51:13
    |
 LL |     let _ = (x / 2.0 + 1.0).ln();
    |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
 
-error: aborting due to 28 previous errors
+error: aborting due to 29 previous errors
 
index 13962a272d4552b6ccce46a9ff908890e0add9cf..936462f94066f681284506f22daacd4a6706dc84 100644 (file)
@@ -5,6 +5,7 @@ fn main() {
     let x = 3f32;
     let y = 5f32;
     let _ = x.log(y);
+    let _ = (x as f32).log(y);
     let _ = x.log(y);
     let _ = x.log(y);
     let _ = x.log(y);
index 26bc20d5370b1f0a6f678d1b7d54b6fe447487d8..0b56fa8fa41fa3112f63adfeac48176a2909c79e 100644 (file)
@@ -5,6 +5,7 @@ fn main() {
     let x = 3f32;
     let y = 5f32;
     let _ = x.ln() / y.ln();
+    let _ = (x as f32).ln() / y.ln();
     let _ = x.log2() / y.log2();
     let _ = x.log10() / y.log10();
     let _ = x.log(5f32) / y.log(5f32);
index 78354c2f62d432eaf0e9c7a9cd6670bfc3bbc4cf..384e3554cbbe1335f3707ebc03eb52b1015bbdee 100644 (file)
@@ -9,20 +9,26 @@ LL |     let _ = x.ln() / y.ln();
 error: log base can be expressed more clearly
   --> $DIR/floating_point_logbase.rs:8:13
    |
+LL |     let _ = (x as f32).ln() / y.ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log(y)`
+
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:9:13
+   |
 LL |     let _ = x.log2() / y.log2();
    |             ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
 
 error: log base can be expressed more clearly
-  --> $DIR/floating_point_logbase.rs:9:13
+  --> $DIR/floating_point_logbase.rs:10:13
    |
 LL |     let _ = x.log10() / y.log10();
    |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
 
 error: log base can be expressed more clearly
-  --> $DIR/floating_point_logbase.rs:10:13
+  --> $DIR/floating_point_logbase.rs:11:13
    |
 LL |     let _ = x.log(5f32) / y.log(5f32);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
index b0641a100cdc810b4a3c7d3d045928970dfe1272..7efe10a10f9e9809db283ef8bf73378a16bbe31a 100644 (file)
@@ -11,10 +11,13 @@ fn main() {
     let _ = (-3.1f32).exp();
     let _ = x.sqrt();
     let _ = x.cbrt();
+    let _ = (x as f32).cbrt();
     let _ = x.powi(3);
     let _ = x.powi(-2);
     let _ = x.powi(16_777_215);
     let _ = x.powi(-16_777_215);
+    let _ = (x as f32).powi(-16_777_215);
+    let _ = (x as f32).powi(3);
     // Cases where the lint shouldn't be applied
     let _ = x.powf(2.1);
     let _ = x.powf(-2.1);
index a0a2c973900f4b7ba943f29a7bb90dcecaf6a8ce..445080417f2ed8c2a0e5b7f1f1a799286077c2f8 100644 (file)
@@ -11,10 +11,13 @@ fn main() {
     let _ = std::f32::consts::E.powf(-3.1);
     let _ = x.powf(1.0 / 2.0);
     let _ = x.powf(1.0 / 3.0);
+    let _ = (x as f32).powf(1.0 / 3.0);
     let _ = x.powf(3.0);
     let _ = x.powf(-2.0);
     let _ = x.powf(16_777_215.0);
     let _ = x.powf(-16_777_215.0);
+    let _ = (x as f32).powf(-16_777_215.0);
+    let _ = (x as f32).powf(3.0);
     // Cases where the lint shouldn't be applied
     let _ = x.powf(2.1);
     let _ = x.powf(-2.1);
index 2422eb911e90a7041dedbecccf2fa2677a12710d..6ee696e6ada5ffda8d5580258d2ec7074bcfee3a 100644 (file)
@@ -50,101 +50,119 @@ LL |     let _ = x.powf(1.0 / 3.0);
    |
    = note: `-D clippy::imprecise-flops` implied by `-D warnings`
 
-error: exponentiation with integer powers can be computed more efficiently
+error: cube-root of a number can be computed more accurately
   --> $DIR/floating_point_powf.rs:14:13
    |
+LL |     let _ = (x as f32).powf(1.0 / 3.0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()`
+
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_powf.rs:15:13
+   |
 LL |     let _ = x.powf(3.0);
    |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 
 error: exponentiation with integer powers can be computed more efficiently
-  --> $DIR/floating_point_powf.rs:15:13
+  --> $DIR/floating_point_powf.rs:16:13
    |
 LL |     let _ = x.powf(-2.0);
    |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
 
 error: exponentiation with integer powers can be computed more efficiently
-  --> $DIR/floating_point_powf.rs:16:13
+  --> $DIR/floating_point_powf.rs:17:13
    |
 LL |     let _ = x.powf(16_777_215.0);
    |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)`
 
 error: exponentiation with integer powers can be computed more efficiently
-  --> $DIR/floating_point_powf.rs:17:13
+  --> $DIR/floating_point_powf.rs:18:13
    |
 LL |     let _ = x.powf(-16_777_215.0);
    |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)`
 
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_powf.rs:19:13
+   |
+LL |     let _ = (x as f32).powf(-16_777_215.0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)`
+
+error: exponentiation with integer powers can be computed more efficiently
+  --> $DIR/floating_point_powf.rs:20:13
+   |
+LL |     let _ = (x as f32).powf(3.0);
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)`
+
 error: exponent for bases 2 and e can be computed more accurately
-  --> $DIR/floating_point_powf.rs:25:13
+  --> $DIR/floating_point_powf.rs:28:13
    |
 LL |     let _ = 2f64.powf(x);
    |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
 
 error: exponent for bases 2 and e can be computed more accurately
-  --> $DIR/floating_point_powf.rs:26:13
+  --> $DIR/floating_point_powf.rs:29:13
    |
 LL |     let _ = 2f64.powf(3.1);
    |             ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()`
 
 error: exponent for bases 2 and e can be computed more accurately
-  --> $DIR/floating_point_powf.rs:27:13
+  --> $DIR/floating_point_powf.rs:30:13
    |
 LL |     let _ = 2f64.powf(-3.1);
    |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()`
 
 error: exponent for bases 2 and e can be computed more accurately
-  --> $DIR/floating_point_powf.rs:28:13
+  --> $DIR/floating_point_powf.rs:31:13
    |
 LL |     let _ = std::f64::consts::E.powf(x);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
 
 error: exponent for bases 2 and e can be computed more accurately
-  --> $DIR/floating_point_powf.rs:29:13
+  --> $DIR/floating_point_powf.rs:32:13
    |
 LL |     let _ = std::f64::consts::E.powf(3.1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()`
 
 error: exponent for bases 2 and e can be computed more accurately
-  --> $DIR/floating_point_powf.rs:30:13
+  --> $DIR/floating_point_powf.rs:33:13
    |
 LL |     let _ = std::f64::consts::E.powf(-3.1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()`
 
 error: square-root of a number can be computed more efficiently and accurately
-  --> $DIR/floating_point_powf.rs:31:13
+  --> $DIR/floating_point_powf.rs:34:13
    |
 LL |     let _ = x.powf(1.0 / 2.0);
    |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
 
 error: cube-root of a number can be computed more accurately
-  --> $DIR/floating_point_powf.rs:32:13
+  --> $DIR/floating_point_powf.rs:35:13
    |
 LL |     let _ = x.powf(1.0 / 3.0);
    |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
 
 error: exponentiation with integer powers can be computed more efficiently
-  --> $DIR/floating_point_powf.rs:33:13
+  --> $DIR/floating_point_powf.rs:36:13
    |
 LL |     let _ = x.powf(3.0);
    |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 
 error: exponentiation with integer powers can be computed more efficiently
-  --> $DIR/floating_point_powf.rs:34:13
+  --> $DIR/floating_point_powf.rs:37:13
    |
 LL |     let _ = x.powf(-2.0);
    |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
 
 error: exponentiation with integer powers can be computed more efficiently
-  --> $DIR/floating_point_powf.rs:35:13
+  --> $DIR/floating_point_powf.rs:38:13
    |
 LL |     let _ = x.powf(-2_147_483_648.0);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)`
 
 error: exponentiation with integer powers can be computed more efficiently
-  --> $DIR/floating_point_powf.rs:36:13
+  --> $DIR/floating_point_powf.rs:39:13
    |
 LL |     let _ = x.powf(2_147_483_647.0);
    |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)`
 
-error: aborting due to 24 previous errors
+error: aborting due to 27 previous errors
 
index 85f7c531e398ae28af7c4056f87c9e1bf4ebd89e..5758db7c6c82d90506cc76a561f5d3ac18dfe021 100644 (file)
@@ -8,6 +8,7 @@ fn main() {
     let y = 4f32;
     let _ = x.mul_add(x, y);
     let _ = y.mul_add(y, x);
+    let _ = (y as f32).mul_add(y as f32, x);
     let _ = x.mul_add(x, y).sqrt();
     let _ = y.mul_add(y, x).sqrt();
     // Cases where the lint shouldn't be applied
index ece61d1bec42d4bb78b2d9bdc0f419369c11805c..5926bf1b0004265c96a82b53a9e8abaefcdedcc2 100644 (file)
@@ -8,6 +8,7 @@ fn main() {
     let y = 4f32;
     let _ = x.powi(2) + y;
     let _ = x + y.powi(2);
+    let _ = x + (y as f32).powi(2);
     let _ = (x.powi(2) + y).sqrt();
     let _ = (x + y.powi(2)).sqrt();
     // Cases where the lint shouldn't be applied
index 37d840988bb23b3b422895d73c09fd6670109bcd..a3c74544212b22e1f082e7d4938b14c60775f062 100644 (file)
@@ -15,14 +15,20 @@ LL |     let _ = x + y.powi(2);
 error: multiply and add expressions can be calculated more efficiently and accurately
   --> $DIR/floating_point_powi.rs:11:13
    |
+LL |     let _ = x + (y as f32).powi(2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y as f32).mul_add(y as f32, x)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_powi.rs:12:13
+   |
 LL |     let _ = (x.powi(2) + y).sqrt();
    |             ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_powi.rs:12:13
+  --> $DIR/floating_point_powi.rs:13:13
    |
 LL |     let _ = (x + y.powi(2)).sqrt();
    |             ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
index ce91fe176c6fccec4fee57bc3339f7091e0e8827..27674b8a455b099a30d9f7be60cceb729628e077 100644 (file)
@@ -8,6 +8,11 @@ pub const fn const_context() {
     let _ = x * 180f32 / std::f32::consts::PI;
 }
 
+pub fn issue9391(degrees: i64) {
+    let _ = (degrees as f64).to_radians();
+    let _ = (degrees as f64).to_degrees();
+}
+
 fn main() {
     let x = 3f32;
     let _ = x.to_degrees();
index 8f3234986148b70d302dd0f5ac04852feec89c16..f1ea73df39845d988b5437125ee05a82057eb8bc 100644 (file)
@@ -8,6 +8,11 @@ pub const fn const_context() {
     let _ = x * 180f32 / std::f32::consts::PI;
 }
 
+pub fn issue9391(degrees: i64) {
+    let _ = degrees as f64 * std::f64::consts::PI / 180.0;
+    let _ = degrees as f64 * 180.0 / std::f64::consts::PI;
+}
+
 fn main() {
     let x = 3f32;
     let _ = x * 180f32 / std::f32::consts::PI;
index f12d3d23f3ab93fa38f53d5fc595857872e1d3a2..979442f2c24a371d57d38aa7546e790ef7b5c638 100644 (file)
@@ -1,40 +1,52 @@
+error: conversion to radians can be done more accurately
+  --> $DIR/floating_point_rad.rs:12:13
+   |
+LL |     let _ = degrees as f64 * std::f64::consts::PI / 180.0;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_radians()`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
 error: conversion to degrees can be done more accurately
   --> $DIR/floating_point_rad.rs:13:13
    |
+LL |     let _ = degrees as f64 * 180.0 / std::f64::consts::PI;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_degrees()`
+
+error: conversion to degrees can be done more accurately
+  --> $DIR/floating_point_rad.rs:18:13
+   |
 LL |     let _ = x * 180f32 / std::f32::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
-   |
-   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 
 error: conversion to degrees can be done more accurately
-  --> $DIR/floating_point_rad.rs:14:13
+  --> $DIR/floating_point_rad.rs:19:13
    |
 LL |     let _ = 90. * 180f64 / std::f64::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_degrees()`
 
 error: conversion to degrees can be done more accurately
-  --> $DIR/floating_point_rad.rs:15:13
+  --> $DIR/floating_point_rad.rs:20:13
    |
 LL |     let _ = 90.5 * 180f64 / std::f64::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_degrees()`
 
 error: conversion to radians can be done more accurately
-  --> $DIR/floating_point_rad.rs:16:13
+  --> $DIR/floating_point_rad.rs:21:13
    |
 LL |     let _ = x * std::f32::consts::PI / 180f32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
 
 error: conversion to radians can be done more accurately
-  --> $DIR/floating_point_rad.rs:17:13
+  --> $DIR/floating_point_rad.rs:22:13
    |
 LL |     let _ = 90. * std::f32::consts::PI / 180f32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_radians()`
 
 error: conversion to radians can be done more accurately
-  --> $DIR/floating_point_rad.rs:18:13
+  --> $DIR/floating_point_rad.rs:23:13
    |
 LL |     let _ = 90.5 * std::f32::consts::PI / 180f32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_radians()`
 
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
 
index 6b754f3bd7103bb3f6ff068ab637acc368f85c7c..b56d6aec508d38bda3189d67fd5610f971166d4e 100644 (file)
@@ -33,7 +33,7 @@ fn main() {
     format!("foo {}", "bar");
     format!("{} bar", "foo");
 
-    let arg: String = "".to_owned();
+    let arg = String::new();
     arg.to_string();
     format!("{:?}", arg); // Don't warn about debug.
     format!("{:8}", arg);
index ca9826b356ec8aea1642450c6f3789244323f071..4c1a3a840ed96721f5cd87d7496c3e1edec99f94 100644 (file)
@@ -35,7 +35,7 @@ fn main() {
     format!("foo {}", "bar");
     format!("{} bar", "foo");
 
-    let arg: String = "".to_owned();
+    let arg = String::new();
     format!("{}", arg);
     format!("{:?}", arg); // Don't warn about debug.
     format!("{:8}", arg);
index 69b5e1c722e0320df446f0fb6a01d74d20fead7f..e1c2d4d70be4f65effd89e0d84edfa3b77fcf3bd 100644 (file)
@@ -1,8 +1,6 @@
 // run-rustfix
 
-#![allow(unreachable_code)]
-#![allow(unused_macros)]
-#![allow(unused_variables)]
+#![allow(unused)]
 #![allow(clippy::assertions_on_constants)]
 #![allow(clippy::eq_op)]
 #![allow(clippy::print_literal)]
@@ -115,3 +113,50 @@ fn main() {
     // https://github.com/rust-lang/rust-clippy/issues/7903
     println!("{foo}{foo:?}", foo = "foo".to_string());
 }
+
+fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
+    println!(
+        "{:<9}  {:<10}  {}",
+        format!("0x{:x}", vendor_id),
+        format!("0x{:x}", product_id),
+        name
+    );
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8855
+mod issue_8855 {
+    #![allow(dead_code)]
+
+    struct A {}
+
+    impl std::fmt::Display for A {
+        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+            write!(f, "test")
+        }
+    }
+
+    fn main() {
+        let a = A {};
+        let b = A {};
+
+        let x = format!("{} {}", a, b);
+        dbg!(x);
+
+        let x = format!("{:>6} {:>6}", a, b.to_string());
+        dbg!(x);
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/9256
+mod issue_9256 {
+    #![allow(dead_code)]
+
+    fn print_substring(original: &str) {
+        assert!(original.len() > 10);
+        println!("{}", &original[..10]);
+    }
+
+    fn main() {
+        print_substring("Hello, world!");
+    }
+}
index 3a434c5bf002a3350a08a0da2988747de925bcc9..b9a4d66c28ad9f4f85390dc427dca8d08e66bcdc 100644 (file)
@@ -1,8 +1,6 @@
 // run-rustfix
 
-#![allow(unreachable_code)]
-#![allow(unused_macros)]
-#![allow(unused_variables)]
+#![allow(unused)]
 #![allow(clippy::assertions_on_constants)]
 #![allow(clippy::eq_op)]
 #![allow(clippy::print_literal)]
@@ -115,3 +113,50 @@ fn main() {
     // https://github.com/rust-lang/rust-clippy/issues/7903
     println!("{foo}{foo:?}", foo = "foo".to_string());
 }
+
+fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
+    println!(
+        "{:<9}  {:<10}  {}",
+        format!("0x{:x}", vendor_id),
+        format!("0x{:x}", product_id),
+        name
+    );
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/8855
+mod issue_8855 {
+    #![allow(dead_code)]
+
+    struct A {}
+
+    impl std::fmt::Display for A {
+        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+            write!(f, "test")
+        }
+    }
+
+    fn main() {
+        let a = A {};
+        let b = A {};
+
+        let x = format!("{} {}", a, b.to_string());
+        dbg!(x);
+
+        let x = format!("{:>6} {:>6}", a, b.to_string());
+        dbg!(x);
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/9256
+mod issue_9256 {
+    #![allow(dead_code)]
+
+    fn print_substring(original: &str) {
+        assert!(original.len() > 10);
+        println!("{}", original[..10].to_string());
+    }
+
+    fn main() {
+        print_substring("Hello, world!");
+    }
+}
index c0cbca507958d1306cfbf008fb3b1d16e2f3959d..aa6e3659b43b519d19f14fa7ecd395a7d7763bda 100644 (file)
@@ -1,5 +1,5 @@
 error: `to_string` applied to a type that implements `Display` in `format!` args
-  --> $DIR/format_args.rs:76:72
+  --> $DIR/format_args.rs:74:72
    |
 LL |     let _ = format!("error: something failed at {}", Location::caller().to_string());
    |                                                                        ^^^^^^^^^^^^ help: remove this
@@ -7,124 +7,136 @@ LL |     let _ = format!("error: something failed at {}", Location::caller().to_
    = note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
 
 error: `to_string` applied to a type that implements `Display` in `write!` args
-  --> $DIR/format_args.rs:80:27
+  --> $DIR/format_args.rs:78:27
    |
 LL |         Location::caller().to_string()
    |                           ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `writeln!` args
-  --> $DIR/format_args.rs:85:27
+  --> $DIR/format_args.rs:83:27
    |
 LL |         Location::caller().to_string()
    |                           ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `print!` args
-  --> $DIR/format_args.rs:87:63
+  --> $DIR/format_args.rs:85:63
    |
 LL |     print!("error: something failed at {}", Location::caller().to_string());
    |                                                               ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:88:65
+  --> $DIR/format_args.rs:86:65
    |
 LL |     println!("error: something failed at {}", Location::caller().to_string());
    |                                                                 ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `eprint!` args
-  --> $DIR/format_args.rs:89:64
+  --> $DIR/format_args.rs:87:64
    |
 LL |     eprint!("error: something failed at {}", Location::caller().to_string());
    |                                                                ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `eprintln!` args
-  --> $DIR/format_args.rs:90:66
+  --> $DIR/format_args.rs:88:66
    |
 LL |     eprintln!("error: something failed at {}", Location::caller().to_string());
    |                                                                  ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `format_args!` args
-  --> $DIR/format_args.rs:91:77
+  --> $DIR/format_args.rs:89:77
    |
 LL |     let _ = format_args!("error: something failed at {}", Location::caller().to_string());
    |                                                                             ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `assert!` args
-  --> $DIR/format_args.rs:92:70
+  --> $DIR/format_args.rs:90:70
    |
 LL |     assert!(true, "error: something failed at {}", Location::caller().to_string());
    |                                                                      ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `assert_eq!` args
-  --> $DIR/format_args.rs:93:73
+  --> $DIR/format_args.rs:91:73
    |
 LL |     assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
    |                                                                         ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `assert_ne!` args
-  --> $DIR/format_args.rs:94:73
+  --> $DIR/format_args.rs:92:73
    |
 LL |     assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
    |                                                                         ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `panic!` args
-  --> $DIR/format_args.rs:95:63
+  --> $DIR/format_args.rs:93:63
    |
 LL |     panic!("error: something failed at {}", Location::caller().to_string());
    |                                                               ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:96:20
+  --> $DIR/format_args.rs:94:20
    |
 LL |     println!("{}", X(1).to_string());
    |                    ^^^^^^^^^^^^^^^^ help: use this: `*X(1)`
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:97:20
+  --> $DIR/format_args.rs:95:20
    |
 LL |     println!("{}", Y(&X(1)).to_string());
    |                    ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))`
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:98:24
+  --> $DIR/format_args.rs:96:24
    |
 LL |     println!("{}", Z(1).to_string());
    |                        ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:99:20
+  --> $DIR/format_args.rs:97:20
    |
 LL |     println!("{}", x.to_string());
    |                    ^^^^^^^^^^^^^ help: use this: `**x`
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:100:20
+  --> $DIR/format_args.rs:98:20
    |
 LL |     println!("{}", x_ref.to_string());
    |                    ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:102:39
+  --> $DIR/format_args.rs:100:39
    |
 LL |     println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
    |                                       ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:103:52
+  --> $DIR/format_args.rs:101:52
    |
 LL |     println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
    |                                                    ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:104:39
+  --> $DIR/format_args.rs:102:39
    |
 LL |     println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
    |                                       ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:105:52
+  --> $DIR/format_args.rs:103:52
    |
 LL |     println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
    |                                                    ^^^^^^^^^^^^ help: remove this
 
-error: aborting due to 21 previous errors
+error: `to_string` applied to a type that implements `Display` in `format!` args
+  --> $DIR/format_args.rs:142:38
+   |
+LL |         let x = format!("{} {}", a, b.to_string());
+   |                                      ^^^^^^^^^^^^ help: remove this
+
+error: `to_string` applied to a type that implements `Display` in `println!` args
+  --> $DIR/format_args.rs:156:24
+   |
+LL |         println!("{}", original[..10].to_string());
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]`
+
+error: aborting due to 23 previous errors
 
index 5f9cebe212abd89844733156582e3c64771c0524..fa564e23cd275cb270a64ae9510504e91c81657d 100644 (file)
@@ -68,7 +68,7 @@ fn main() {
     &x;
     x;
 
-    let mut a = A("".into());
+    let mut a = A(String::new());
     let b = a << 0; // no error: non-integer
 
     1 * Meter; // no error: non-integer
index ca799c9cfac0f8a4c7e82c24d2e605d770fec561..3d06d2a73b628d7b760d3a8c3684c8d15c4ebde7 100644 (file)
@@ -68,7 +68,7 @@ fn main() {
     &x >> 0;
     x >> &0;
 
-    let mut a = A("".into());
+    let mut a = A(String::new());
     let b = a << 0; // no error: non-integer
 
     1 * Meter; // no error: non-integer
index 6cbfafbb38b9913e7ec71de26f6206fce73ae1cf..321feb0224ed11a97d111f645b8c77b7af073b89 100644 (file)
@@ -39,4 +39,12 @@ fn if_let_different_mutex() {
     };
 }
 
+fn mutex_ref(mutex: &Mutex<i32>) {
+    if let Ok(i) = mutex.lock() {
+        do_stuff(i);
+    } else {
+        let _x = mutex.lock();
+    };
+}
+
 fn main() {}
index e9c4d9163328f3066e9c67f244a8ce233c35555a..8a4d5dbac592b33d12a0d3b0061413a719e17248 100644 (file)
@@ -1,10 +1,14 @@
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
   --> $DIR/if_let_mutex.rs:10:5
    |
-LL | /     if let Err(locked) = m.lock() {
+LL |       if let Err(locked) = m.lock() {
+   |       ^                    - this Mutex will remain locked for the entire `if let`-block...
+   |  _____|
+   | |
 LL | |         do_stuff(locked);
 LL | |     } else {
 LL | |         let lock = m.lock().unwrap();
+   | |                    - ... and is tried to lock again here, which will always deadlock.
 LL | |         do_stuff(lock);
 LL | |     };
    | |_____^
@@ -15,15 +19,35 @@ LL | |     };
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
   --> $DIR/if_let_mutex.rs:22:5
    |
-LL | /     if let Some(locked) = m.lock().unwrap().deref() {
+LL |       if let Some(locked) = m.lock().unwrap().deref() {
+   |       ^                     - this Mutex will remain locked for the entire `if let`-block...
+   |  _____|
+   | |
 LL | |         do_stuff(locked);
 LL | |     } else {
 LL | |         let lock = m.lock().unwrap();
+   | |                    - ... and is tried to lock again here, which will always deadlock.
 LL | |         do_stuff(lock);
 LL | |     };
    | |_____^
    |
    = help: move the lock call outside of the `if let ...` expression
 
-error: aborting due to 2 previous errors
+error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
+  --> $DIR/if_let_mutex.rs:43:5
+   |
+LL |       if let Ok(i) = mutex.lock() {
+   |       ^              ----- this Mutex will remain locked for the entire `if let`-block...
+   |  _____|
+   | |
+LL | |         do_stuff(i);
+LL | |     } else {
+LL | |         let _x = mutex.lock();
+   | |                  ----- ... and is tried to lock again here, which will always deadlock.
+LL | |     };
+   | |_____^
+   |
+   = help: move the lock call outside of the `if let ...` expression
+
+error: aborting due to 3 previous errors
 
index 8cb22d569f4cc90bca59bf68e2971bb5e8415458..c22ace30d2dc21defe9cefa3108599f228860f27 100644 (file)
@@ -27,21 +27,21 @@ LL | |     };
    |
    = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
 
-error: this could be simplified with `bool::then`
+error: this could be simplified with `bool::then_some`
   --> $DIR/if_then_some_else_none.rs:23:28
    |
 LL |     let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider using `bool::then` like: `(o < 32).then(|| o)`
+   = help: consider using `bool::then_some` like: `(o < 32).then_some(o)`
 
-error: this could be simplified with `bool::then`
+error: this could be simplified with `bool::then_some`
   --> $DIR/if_then_some_else_none.rs:27:13
    |
 LL |     let _ = if !x { Some(0) } else { None };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider using `bool::then` like: `(!x).then(|| 0)`
+   = help: consider using `bool::then_some` like: `(!x).then_some(0)`
 
 error: this could be simplified with `bool::then`
   --> $DIR/if_then_some_else_none.rs:82:13
index 45a430edcb58998562e96b35e86d8c2a41db1e8b..7ebf6ee993cbd7a02b6db0b178a3de67f828ba8c 100644 (file)
@@ -3,7 +3,7 @@
 // We also check the out_of_bounds_indexing lint here, because it lints similar things and
 // we want to avoid false positives.
 #![warn(clippy::out_of_bounds_indexing)]
-#![allow(const_err, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(const_err, unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)]
 
 const ARR: [i32; 2] = [1, 2];
 const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr.
diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed b/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed
new file mode 100644 (file)
index 0000000..bd9b07a
--- /dev/null
@@ -0,0 +1,63 @@
+// run-rustfix
+#![warn(clippy::iter_on_empty_collections)]
+#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
+
+fn array() {
+    assert_eq!(std::iter::empty().next(), Option::<i32>::None);
+    assert_eq!(std::iter::empty().next(), Option::<&mut i32>::None);
+    assert_eq!(std::iter::empty().next(), Option::<&i32>::None);
+    assert_eq!(std::iter::empty().next(), Option::<i32>::None);
+    assert_eq!(std::iter::empty().next(), Option::<&mut i32>::None);
+    assert_eq!(std::iter::empty().next(), Option::<&i32>::None);
+
+    // Don't trigger on non-iter methods
+    let _: Option<String> = None.clone();
+    let _: [String; 0] = [].clone();
+
+    // Don't trigger on match or if branches
+    let _ = match 123 {
+        123 => [].iter(),
+        _ => ["test"].iter(),
+    };
+
+    let _ = if false { ["test"].iter() } else { [].iter() };
+}
+
+macro_rules! in_macros {
+    () => {
+        assert_eq!([].into_iter().next(), Option::<i32>::None);
+        assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
+        assert_eq!([].iter().next(), Option::<&i32>::None);
+        assert_eq!(None.into_iter().next(), Option::<i32>::None);
+        assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
+        assert_eq!(None.iter().next(), Option::<&i32>::None);
+    };
+}
+
+// Don't trigger on a `None` that isn't std's option
+mod custom_option {
+    #[allow(unused)]
+    enum CustomOption {
+        Some(i32),
+        None,
+    }
+
+    impl CustomOption {
+        fn iter(&self) {}
+        fn iter_mut(&mut self) {}
+        fn into_iter(self) {}
+    }
+    use CustomOption::*;
+
+    pub fn custom_option() {
+        None.iter();
+        None.iter_mut();
+        None.into_iter();
+    }
+}
+
+fn main() {
+    array();
+    custom_option::custom_option();
+    in_macros!();
+}
diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.rs b/src/tools/clippy/tests/ui/iter_on_empty_collections.rs
new file mode 100644 (file)
index 0000000..e15ba94
--- /dev/null
@@ -0,0 +1,63 @@
+// run-rustfix
+#![warn(clippy::iter_on_empty_collections)]
+#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
+
+fn array() {
+    assert_eq!([].into_iter().next(), Option::<i32>::None);
+    assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
+    assert_eq!([].iter().next(), Option::<&i32>::None);
+    assert_eq!(None.into_iter().next(), Option::<i32>::None);
+    assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
+    assert_eq!(None.iter().next(), Option::<&i32>::None);
+
+    // Don't trigger on non-iter methods
+    let _: Option<String> = None.clone();
+    let _: [String; 0] = [].clone();
+
+    // Don't trigger on match or if branches
+    let _ = match 123 {
+        123 => [].iter(),
+        _ => ["test"].iter(),
+    };
+
+    let _ = if false { ["test"].iter() } else { [].iter() };
+}
+
+macro_rules! in_macros {
+    () => {
+        assert_eq!([].into_iter().next(), Option::<i32>::None);
+        assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
+        assert_eq!([].iter().next(), Option::<&i32>::None);
+        assert_eq!(None.into_iter().next(), Option::<i32>::None);
+        assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
+        assert_eq!(None.iter().next(), Option::<&i32>::None);
+    };
+}
+
+// Don't trigger on a `None` that isn't std's option
+mod custom_option {
+    #[allow(unused)]
+    enum CustomOption {
+        Some(i32),
+        None,
+    }
+
+    impl CustomOption {
+        fn iter(&self) {}
+        fn iter_mut(&mut self) {}
+        fn into_iter(self) {}
+    }
+    use CustomOption::*;
+
+    pub fn custom_option() {
+        None.iter();
+        None.iter_mut();
+        None.into_iter();
+    }
+}
+
+fn main() {
+    array();
+    custom_option::custom_option();
+    in_macros!();
+}
diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr b/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr
new file mode 100644 (file)
index 0000000..cbd6117
--- /dev/null
@@ -0,0 +1,40 @@
+error: `into_iter` call on an empty collection
+  --> $DIR/iter_on_empty_collections.rs:6:16
+   |
+LL |     assert_eq!([].into_iter().next(), Option::<i32>::None);
+   |                ^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+   |
+   = note: `-D clippy::iter-on-empty-collections` implied by `-D warnings`
+
+error: `iter_mut` call on an empty collection
+  --> $DIR/iter_on_empty_collections.rs:7:16
+   |
+LL |     assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
+   |                ^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: `iter` call on an empty collection
+  --> $DIR/iter_on_empty_collections.rs:8:16
+   |
+LL |     assert_eq!([].iter().next(), Option::<&i32>::None);
+   |                ^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: `into_iter` call on an empty collection
+  --> $DIR/iter_on_empty_collections.rs:9:16
+   |
+LL |     assert_eq!(None.into_iter().next(), Option::<i32>::None);
+   |                ^^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: `iter_mut` call on an empty collection
+  --> $DIR/iter_on_empty_collections.rs:10:16
+   |
+LL |     assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
+   |                ^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: `iter` call on an empty collection
+  --> $DIR/iter_on_empty_collections.rs:11:16
+   |
+LL |     assert_eq!(None.iter().next(), Option::<&i32>::None);
+   |                ^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_on_single_items.fixed b/src/tools/clippy/tests/ui/iter_on_single_items.fixed
new file mode 100644 (file)
index 0000000..1fa4b03
--- /dev/null
@@ -0,0 +1,63 @@
+// run-rustfix
+#![warn(clippy::iter_on_single_items)]
+#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
+
+fn array() {
+    assert_eq!(std::iter::once(123).next(), Some(123));
+    assert_eq!(std::iter::once(&mut 123).next(), Some(&mut 123));
+    assert_eq!(std::iter::once(&123).next(), Some(&123));
+    assert_eq!(std::iter::once(123).next(), Some(123));
+    assert_eq!(std::iter::once(&mut 123).next(), Some(&mut 123));
+    assert_eq!(std::iter::once(&123).next(), Some(&123));
+
+    // Don't trigger on non-iter methods
+    let _: Option<String> = Some("test".to_string()).clone();
+    let _: [String; 1] = ["test".to_string()].clone();
+
+    // Don't trigger on match or if branches
+    let _ = match 123 {
+        123 => [].iter(),
+        _ => ["test"].iter(),
+    };
+
+    let _ = if false { ["test"].iter() } else { [].iter() };
+}
+
+macro_rules! in_macros {
+    () => {
+        assert_eq!([123].into_iter().next(), Some(123));
+        assert_eq!([123].iter_mut().next(), Some(&mut 123));
+        assert_eq!([123].iter().next(), Some(&123));
+        assert_eq!(Some(123).into_iter().next(), Some(123));
+        assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
+        assert_eq!(Some(123).iter().next(), Some(&123));
+    };
+}
+
+// Don't trigger on a `Some` that isn't std's option
+mod custom_option {
+    #[allow(unused)]
+    enum CustomOption {
+        Some(i32),
+        None,
+    }
+
+    impl CustomOption {
+        fn iter(&self) {}
+        fn iter_mut(&mut self) {}
+        fn into_iter(self) {}
+    }
+    use CustomOption::*;
+
+    pub fn custom_option() {
+        Some(3).iter();
+        Some(3).iter_mut();
+        Some(3).into_iter();
+    }
+}
+
+fn main() {
+    array();
+    custom_option::custom_option();
+    in_macros!();
+}
diff --git a/src/tools/clippy/tests/ui/iter_on_single_items.rs b/src/tools/clippy/tests/ui/iter_on_single_items.rs
new file mode 100644 (file)
index 0000000..ea96d80
--- /dev/null
@@ -0,0 +1,63 @@
+// run-rustfix
+#![warn(clippy::iter_on_single_items)]
+#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
+
+fn array() {
+    assert_eq!([123].into_iter().next(), Some(123));
+    assert_eq!([123].iter_mut().next(), Some(&mut 123));
+    assert_eq!([123].iter().next(), Some(&123));
+    assert_eq!(Some(123).into_iter().next(), Some(123));
+    assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
+    assert_eq!(Some(123).iter().next(), Some(&123));
+
+    // Don't trigger on non-iter methods
+    let _: Option<String> = Some("test".to_string()).clone();
+    let _: [String; 1] = ["test".to_string()].clone();
+
+    // Don't trigger on match or if branches
+    let _ = match 123 {
+        123 => [].iter(),
+        _ => ["test"].iter(),
+    };
+
+    let _ = if false { ["test"].iter() } else { [].iter() };
+}
+
+macro_rules! in_macros {
+    () => {
+        assert_eq!([123].into_iter().next(), Some(123));
+        assert_eq!([123].iter_mut().next(), Some(&mut 123));
+        assert_eq!([123].iter().next(), Some(&123));
+        assert_eq!(Some(123).into_iter().next(), Some(123));
+        assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
+        assert_eq!(Some(123).iter().next(), Some(&123));
+    };
+}
+
+// Don't trigger on a `Some` that isn't std's option
+mod custom_option {
+    #[allow(unused)]
+    enum CustomOption {
+        Some(i32),
+        None,
+    }
+
+    impl CustomOption {
+        fn iter(&self) {}
+        fn iter_mut(&mut self) {}
+        fn into_iter(self) {}
+    }
+    use CustomOption::*;
+
+    pub fn custom_option() {
+        Some(3).iter();
+        Some(3).iter_mut();
+        Some(3).into_iter();
+    }
+}
+
+fn main() {
+    array();
+    custom_option::custom_option();
+    in_macros!();
+}
diff --git a/src/tools/clippy/tests/ui/iter_on_single_items.stderr b/src/tools/clippy/tests/ui/iter_on_single_items.stderr
new file mode 100644 (file)
index 0000000..d6c5471
--- /dev/null
@@ -0,0 +1,40 @@
+error: `into_iter` call on a collection with only one item
+  --> $DIR/iter_on_single_items.rs:6:16
+   |
+LL |     assert_eq!([123].into_iter().next(), Some(123));
+   |                ^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(123)`
+   |
+   = note: `-D clippy::iter-on-single-items` implied by `-D warnings`
+
+error: `iter_mut` call on a collection with only one item
+  --> $DIR/iter_on_single_items.rs:7:16
+   |
+LL |     assert_eq!([123].iter_mut().next(), Some(&mut 123));
+   |                ^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&mut 123)`
+
+error: `iter` call on a collection with only one item
+  --> $DIR/iter_on_single_items.rs:8:16
+   |
+LL |     assert_eq!([123].iter().next(), Some(&123));
+   |                ^^^^^^^^^^^^ help: try: `std::iter::once(&123)`
+
+error: `into_iter` call on a collection with only one item
+  --> $DIR/iter_on_single_items.rs:9:16
+   |
+LL |     assert_eq!(Some(123).into_iter().next(), Some(123));
+   |                ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(123)`
+
+error: `iter_mut` call on a collection with only one item
+  --> $DIR/iter_on_single_items.rs:10:16
+   |
+LL |     assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
+   |                ^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&mut 123)`
+
+error: `iter` call on a collection with only one item
+  --> $DIR/iter_on_single_items.rs:11:16
+   |
+LL |     assert_eq!(Some(123).iter().next(), Some(&123));
+   |                ^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&123)`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_string_new.fixed b/src/tools/clippy/tests/ui/manual_string_new.fixed
new file mode 100644 (file)
index 0000000..a376411
--- /dev/null
@@ -0,0 +1,63 @@
+// run-rustfix
+
+#![warn(clippy::manual_string_new)]
+
+macro_rules! create_strings_from_macro {
+    // When inside a macro, nothing should warn to prevent false positives.
+    ($some_str:expr) => {
+        let _: String = $some_str.into();
+        let _ = $some_str.to_string();
+    };
+}
+
+fn main() {
+    // Method calls
+    let _ = String::new();
+    let _ = "no warning".to_string();
+
+    let _ = String::new();
+    let _ = "no warning".to_owned();
+
+    let _: String = String::new();
+    let _: String = "no warning".into();
+
+    let _: SomeOtherStruct = "no warning".into();
+    let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String.
+
+    // Calls
+    let _ = String::new();
+    let _ = String::new();
+    let _ = String::from("no warning");
+    let _ = SomeOtherStruct::from("no warning");
+    let _ = SomeOtherStruct::from(""); // Again: no warning.
+
+    let _ = String::new();
+    let _ = String::try_from("no warning").unwrap();
+    let _ = String::try_from("no warning").expect("this should not warn");
+    let _ = SomeOtherStruct::try_from("no warning").unwrap();
+    let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning.
+
+    let _: String = String::new();
+    let _: String = From::from("no warning");
+    let _: SomeOtherStruct = From::from("no warning");
+    let _: SomeOtherStruct = From::from(""); // Again: no warning.
+
+    let _: String = String::new();
+    let _: String = TryFrom::try_from("no warning").unwrap();
+    let _: String = TryFrom::try_from("no warning").expect("this should not warn");
+    let _: String = String::new();
+    let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap();
+    let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning.
+
+    // Macros (never warn)
+    create_strings_from_macro!("");
+    create_strings_from_macro!("Hey");
+}
+
+struct SomeOtherStruct {}
+
+impl From<&str> for SomeOtherStruct {
+    fn from(_value: &str) -> Self {
+        Self {}
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_string_new.rs b/src/tools/clippy/tests/ui/manual_string_new.rs
new file mode 100644 (file)
index 0000000..6bfc52f
--- /dev/null
@@ -0,0 +1,63 @@
+// run-rustfix
+
+#![warn(clippy::manual_string_new)]
+
+macro_rules! create_strings_from_macro {
+    // When inside a macro, nothing should warn to prevent false positives.
+    ($some_str:expr) => {
+        let _: String = $some_str.into();
+        let _ = $some_str.to_string();
+    };
+}
+
+fn main() {
+    // Method calls
+    let _ = "".to_string();
+    let _ = "no warning".to_string();
+
+    let _ = "".to_owned();
+    let _ = "no warning".to_owned();
+
+    let _: String = "".into();
+    let _: String = "no warning".into();
+
+    let _: SomeOtherStruct = "no warning".into();
+    let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String.
+
+    // Calls
+    let _ = String::from("");
+    let _ = <String>::from("");
+    let _ = String::from("no warning");
+    let _ = SomeOtherStruct::from("no warning");
+    let _ = SomeOtherStruct::from(""); // Again: no warning.
+
+    let _ = String::try_from("").unwrap();
+    let _ = String::try_from("no warning").unwrap();
+    let _ = String::try_from("no warning").expect("this should not warn");
+    let _ = SomeOtherStruct::try_from("no warning").unwrap();
+    let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning.
+
+    let _: String = From::from("");
+    let _: String = From::from("no warning");
+    let _: SomeOtherStruct = From::from("no warning");
+    let _: SomeOtherStruct = From::from(""); // Again: no warning.
+
+    let _: String = TryFrom::try_from("").unwrap();
+    let _: String = TryFrom::try_from("no warning").unwrap();
+    let _: String = TryFrom::try_from("no warning").expect("this should not warn");
+    let _: String = TryFrom::try_from("").expect("this should warn");
+    let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap();
+    let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning.
+
+    // Macros (never warn)
+    create_strings_from_macro!("");
+    create_strings_from_macro!("Hey");
+}
+
+struct SomeOtherStruct {}
+
+impl From<&str> for SomeOtherStruct {
+    fn from(_value: &str) -> Self {
+        Self {}
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_string_new.stderr b/src/tools/clippy/tests/ui/manual_string_new.stderr
new file mode 100644 (file)
index 0000000..e5ecfc6
--- /dev/null
@@ -0,0 +1,58 @@
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:15:13
+   |
+LL |     let _ = "".to_string();
+   |             ^^^^^^^^^^^^^^ help: consider using: `String::new()`
+   |
+   = note: `-D clippy::manual-string-new` implied by `-D warnings`
+
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:18:13
+   |
+LL |     let _ = "".to_owned();
+   |             ^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:21:21
+   |
+LL |     let _: String = "".into();
+   |                     ^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:28:13
+   |
+LL |     let _ = String::from("");
+   |             ^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:29:13
+   |
+LL |     let _ = <String>::from("");
+   |             ^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:34:13
+   |
+LL |     let _ = String::try_from("").unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:40:21
+   |
+LL |     let _: String = From::from("");
+   |                     ^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:45:21
+   |
+LL |     let _: String = TryFrom::try_from("").unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: empty String is being created manually
+  --> $DIR/manual_string_new.rs:48:21
+   |
+LL |     let _: String = TryFrom::try_from("").expect("this should warn");
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
+
+error: aborting due to 9 previous errors
+
index 1ccbfda64b73a5240ae68929f83c37d07cd66c05..95ca571d07bfbf88fcce5d626814d6f210156ee2 100644 (file)
@@ -167,4 +167,29 @@ fn main() {
             _ => false,
         };
     }
+
+    let x = ' ';
+    // ignore if match block contains comment
+    let _line_comments = match x {
+        // numbers are bad!
+        '1' | '2' | '3' => true,
+        // spaces are very important to be true.
+        ' ' => true,
+        // as are dots
+        '.' => true,
+        _ => false,
+    };
+
+    let _block_comments = match x {
+        /* numbers are bad!
+         */
+        '1' | '2' | '3' => true,
+        /* spaces are very important to be true.
+         */
+        ' ' => true,
+        /* as are dots
+         */
+        '.' => true,
+        _ => false,
+    };
 }
index a49991f594174473c51cc41609d3f808b1d80768..3b9c8cadadcc417ebbe731a3735dd98580837e46 100644 (file)
@@ -208,4 +208,29 @@ enum E {
             _ => false,
         };
     }
+
+    let x = ' ';
+    // ignore if match block contains comment
+    let _line_comments = match x {
+        // numbers are bad!
+        '1' | '2' | '3' => true,
+        // spaces are very important to be true.
+        ' ' => true,
+        // as are dots
+        '.' => true,
+        _ => false,
+    };
+
+    let _block_comments = match x {
+        /* numbers are bad!
+         */
+        '1' | '2' | '3' => true,
+        /* spaces are very important to be true.
+         */
+        ' ' => true,
+        /* as are dots
+         */
+        '.' => true,
+        _ => false,
+    };
 }
diff --git a/src/tools/clippy/tests/ui/multi_assignments.rs b/src/tools/clippy/tests/ui/multi_assignments.rs
new file mode 100644 (file)
index 0000000..b186bf8
--- /dev/null
@@ -0,0 +1,9 @@
+#![warn(clippy::multi_assignments)]
+fn main() {
+    let (mut a, mut b, mut c, mut d) = ((), (), (), ());
+    a = b = c;
+    a = b = c = d;
+    a = b = { c };
+    a = { b = c };
+    a = (b = c);
+}
diff --git a/src/tools/clippy/tests/ui/multi_assignments.stderr b/src/tools/clippy/tests/ui/multi_assignments.stderr
new file mode 100644 (file)
index 0000000..d6c42bb
--- /dev/null
@@ -0,0 +1,40 @@
+error: assignments don't nest intuitively
+  --> $DIR/multi_assignments.rs:4:5
+   |
+LL |     a = b = c;
+   |     ^^^^^^^^^
+   |
+   = note: `-D clippy::multi-assignments` implied by `-D warnings`
+
+error: assignments don't nest intuitively
+  --> $DIR/multi_assignments.rs:5:5
+   |
+LL |     a = b = c = d;
+   |     ^^^^^^^^^^^^^
+
+error: assignments don't nest intuitively
+  --> $DIR/multi_assignments.rs:5:9
+   |
+LL |     a = b = c = d;
+   |         ^^^^^^^^^
+
+error: assignments don't nest intuitively
+  --> $DIR/multi_assignments.rs:6:5
+   |
+LL |     a = b = { c };
+   |     ^^^^^^^^^^^^^
+
+error: assignments don't nest intuitively
+  --> $DIR/multi_assignments.rs:7:5
+   |
+LL |     a = { b = c };
+   |     ^^^^^^^^^^^^^
+
+error: assignments don't nest intuitively
+  --> $DIR/multi_assignments.rs:8:5
+   |
+LL |     a = (b = c);
+   |     ^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
index bfd2725ecaaa874b9409502e5b72514663ad7cb8..8cf93bd248173ee93cef0b0ec5e87815e11f370b 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![feature(lint_reasons)]
+#![feature(custom_inner_attributes, lint_reasons)]
 
 #[warn(clippy::all, clippy::needless_borrow)]
 #[allow(unused_variables, clippy::unnecessary_mut_passed)]
@@ -127,6 +127,20 @@ fn main() {
             0
         }
     }
+
+    let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
+    let _ = std::path::Path::new(".").join(".");
+    deref_target_is_x(X);
+    multiple_constraints([[""]]);
+    multiple_constraints_normalizes_to_same(X, X);
+    let _ = Some("").unwrap_or("");
+
+    only_sized(&""); // Don't lint. `Sized` is only bound
+    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
+    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
+    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
+    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
+    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 }
 
 #[allow(clippy::needless_borrowed_reference)]
@@ -183,3 +197,104 @@ mod issue9160 {
         }
     }
 }
+
+#[derive(Clone, Copy)]
+struct X;
+
+impl std::ops::Deref for X {
+    type Target = X;
+    fn deref(&self) -> &Self::Target {
+        self
+    }
+}
+
+fn deref_target_is_x<T>(_: T)
+where
+    T: std::ops::Deref<Target = X>,
+{
+}
+
+fn multiple_constraints<T, U, V, X, Y>(_: T)
+where
+    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
+    U: IntoIterator<Item = V>,
+    V: AsRef<str>,
+    X: IntoIterator<Item = Y>,
+    Y: AsRef<std::ffi::OsStr>,
+{
+}
+
+fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
+where
+    T: std::ops::Deref<Target = U>,
+    U: std::ops::Deref<Target = V>,
+{
+}
+
+fn only_sized<T>(_: T) {}
+
+fn ref_as_ref_path<T: 'static>(_: &'static T)
+where
+    &'static T: AsRef<std::path::Path>,
+{
+}
+
+trait RefsOnly {
+    type Referent;
+}
+
+impl<T> RefsOnly for &T {
+    type Referent = T;
+}
+
+fn refs_only<T, U>(_: T)
+where
+    T: RefsOnly<Referent = U>,
+{
+}
+
+fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
+where
+    T: IntoIterator<Item = U>,
+    U: IntoIterator<Item = V>,
+    V: AsRef<str>,
+{
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+#[allow(dead_code)]
+mod copyable_iterator {
+    #[derive(Clone, Copy)]
+    struct Iter;
+    impl Iterator for Iter {
+        type Item = ();
+        fn next(&mut self) -> Option<Self::Item> {
+            None
+        }
+    }
+    fn takes_iter(_: impl Iterator) {}
+    fn dont_warn(mut x: Iter) {
+        takes_iter(&mut x);
+    }
+    fn warn(mut x: &mut Iter) {
+        takes_iter(&mut x)
+    }
+}
+
+mod under_msrv {
+    #![allow(dead_code)]
+    #![clippy::msrv = "1.52.0"]
+
+    fn foo() {
+        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+    }
+}
+
+mod meets_msrv {
+    #![allow(dead_code)]
+    #![clippy::msrv = "1.53.0"]
+
+    fn foo() {
+        let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
+    }
+}
index c457d8c5471886f5491aba8bfc3c08f4a85d2621..fd9b2a11df96f1cba7dcd147dd7d6ca96774435d 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![feature(lint_reasons)]
+#![feature(custom_inner_attributes, lint_reasons)]
 
 #[warn(clippy::all, clippy::needless_borrow)]
 #[allow(unused_variables, clippy::unnecessary_mut_passed)]
@@ -127,6 +127,20 @@ fn from(s: &S) -> Self {
             0
         }
     }
+
+    let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+    let _ = std::path::Path::new(".").join(&&".");
+    deref_target_is_x(&X);
+    multiple_constraints(&[[""]]);
+    multiple_constraints_normalizes_to_same(&X, X);
+    let _ = Some("").unwrap_or(&"");
+
+    only_sized(&""); // Don't lint. `Sized` is only bound
+    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
+    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
+    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
+    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
+    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 }
 
 #[allow(clippy::needless_borrowed_reference)]
@@ -183,3 +197,104 @@ fn calls_mut_field(&mut self) -> T {
         }
     }
 }
+
+#[derive(Clone, Copy)]
+struct X;
+
+impl std::ops::Deref for X {
+    type Target = X;
+    fn deref(&self) -> &Self::Target {
+        self
+    }
+}
+
+fn deref_target_is_x<T>(_: T)
+where
+    T: std::ops::Deref<Target = X>,
+{
+}
+
+fn multiple_constraints<T, U, V, X, Y>(_: T)
+where
+    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
+    U: IntoIterator<Item = V>,
+    V: AsRef<str>,
+    X: IntoIterator<Item = Y>,
+    Y: AsRef<std::ffi::OsStr>,
+{
+}
+
+fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
+where
+    T: std::ops::Deref<Target = U>,
+    U: std::ops::Deref<Target = V>,
+{
+}
+
+fn only_sized<T>(_: T) {}
+
+fn ref_as_ref_path<T: 'static>(_: &'static T)
+where
+    &'static T: AsRef<std::path::Path>,
+{
+}
+
+trait RefsOnly {
+    type Referent;
+}
+
+impl<T> RefsOnly for &T {
+    type Referent = T;
+}
+
+fn refs_only<T, U>(_: T)
+where
+    T: RefsOnly<Referent = U>,
+{
+}
+
+fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
+where
+    T: IntoIterator<Item = U>,
+    U: IntoIterator<Item = V>,
+    V: AsRef<str>,
+{
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+#[allow(dead_code)]
+mod copyable_iterator {
+    #[derive(Clone, Copy)]
+    struct Iter;
+    impl Iterator for Iter {
+        type Item = ();
+        fn next(&mut self) -> Option<Self::Item> {
+            None
+        }
+    }
+    fn takes_iter(_: impl Iterator) {}
+    fn dont_warn(mut x: Iter) {
+        takes_iter(&mut x);
+    }
+    fn warn(mut x: &mut Iter) {
+        takes_iter(&mut x)
+    }
+}
+
+mod under_msrv {
+    #![allow(dead_code)]
+    #![clippy::msrv = "1.52.0"]
+
+    fn foo() {
+        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+    }
+}
+
+mod meets_msrv {
+    #![allow(dead_code)]
+    #![clippy::msrv = "1.53.0"]
+
+    fn foo() {
+        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+    }
+}
index 66588689d81851a8f81862bac08e1bf08ce95d58..5af68706d4ba579311308199b560dbfcad016ad2 100644 (file)
@@ -120,17 +120,59 @@ error: this expression creates a reference which is immediately dereferenced by
 LL |     (&&5).foo();
    |     ^^^^^ help: change this to: `(&5)`
 
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrow.rs:131:51
+   |
+LL |     let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+   |                                                   ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrow.rs:132:44
+   |
+LL |     let _ = std::path::Path::new(".").join(&&".");
+   |                                            ^^^^^ help: change this to: `"."`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrow.rs:133:23
+   |
+LL |     deref_target_is_x(&X);
+   |                       ^^ help: change this to: `X`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrow.rs:134:26
+   |
+LL |     multiple_constraints(&[[""]]);
+   |                          ^^^^^^^ help: change this to: `[[""]]`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrow.rs:135:45
+   |
+LL |     multiple_constraints_normalizes_to_same(&X, X);
+   |                                             ^^ help: change this to: `X`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:136:32
+   |
+LL |     let _ = Some("").unwrap_or(&"");
+   |                                ^^^ help: change this to: `""`
+
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:173:13
+  --> $DIR/needless_borrow.rs:187:13
    |
 LL |             (&self.f)()
    |             ^^^^^^^^^ help: change this to: `(self.f)`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:182:13
+  --> $DIR/needless_borrow.rs:196:13
    |
 LL |             (&mut self.f)()
    |             ^^^^^^^^^^^^^ help: change this to: `(self.f)`
 
-error: aborting due to 22 previous errors
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrow.rs:298:55
+   |
+LL |         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+   |                                                       ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
+
+error: aborting due to 29 previous errors
 
index 1f11d1f8d563c2bcb213f6924ac169512950d762..12a9ace1ee688679a5745698e314990480eda9f7 100644 (file)
@@ -112,3 +112,192 @@ fn allow_test() {
     let v = [1].iter().collect::<Vec<_>>();
     v.into_iter().collect::<HashSet<_>>();
 }
+
+mod issue_8553 {
+    fn test_for() {
+        let vec = vec![1, 2];
+        let w: Vec<usize> = vec.iter().map(|i| i * i).collect();
+
+        for i in 0..2 {
+            // Do not lint, because this method call is in the loop
+            w.contains(&i);
+        }
+
+        for i in 0..2 {
+            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+            // Do lint
+            y.contains(&i);
+            for j in 0..2 {
+                // Do not lint, because this method call is in the loop
+                z.contains(&j);
+            }
+        }
+
+        // Do not lint, because this variable is used.
+        w.contains(&0);
+    }
+
+    fn test_while() {
+        let vec = vec![1, 2];
+        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let mut n = 0;
+        while n > 1 {
+            // Do not lint, because this method call is in the loop
+            x.contains(&n);
+            n += 1;
+        }
+
+        while n > 2 {
+            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+            // Do lint
+            y.contains(&n);
+            n += 1;
+            while n > 4 {
+                // Do not lint, because this method call is in the loop
+                z.contains(&n);
+                n += 1;
+            }
+        }
+    }
+
+    fn test_loop() {
+        let vec = vec![1, 2];
+        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let mut n = 0;
+        loop {
+            if n < 1 {
+                // Do not lint, because this method call is in the loop
+                x.contains(&n);
+                n += 1;
+            } else {
+                break;
+            }
+        }
+
+        loop {
+            if n < 2 {
+                let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+                let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+                // Do lint
+                y.contains(&n);
+                n += 1;
+                loop {
+                    if n < 4 {
+                        // Do not lint, because this method call is in the loop
+                        z.contains(&n);
+                        n += 1;
+                    } else {
+                        break;
+                    }
+                }
+            } else {
+                break;
+            }
+        }
+    }
+
+    fn test_while_let() {
+        let vec = vec![1, 2];
+        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let optional = Some(0);
+        let mut n = 0;
+        while let Some(value) = optional {
+            if n < 1 {
+                // Do not lint, because this method call is in the loop
+                x.contains(&n);
+                n += 1;
+            } else {
+                break;
+            }
+        }
+
+        while let Some(value) = optional {
+            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+            if n < 2 {
+                // Do lint
+                y.contains(&n);
+                n += 1;
+            } else {
+                break;
+            }
+
+            while let Some(value) = optional {
+                if n < 4 {
+                    // Do not lint, because this method call is in the loop
+                    z.contains(&n);
+                    n += 1;
+                } else {
+                    break;
+                }
+            }
+        }
+    }
+
+    fn test_if_cond() {
+        let vec = vec![1, 2];
+        let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let w = v.iter().collect::<Vec<_>>();
+        // Do lint
+        for _ in 0..w.len() {
+            todo!();
+        }
+    }
+
+    fn test_if_cond_false_case() {
+        let vec = vec![1, 2];
+        let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let w = v.iter().collect::<Vec<_>>();
+        // Do not lint, because w is used.
+        for _ in 0..w.len() {
+            todo!();
+        }
+
+        w.len();
+    }
+
+    fn test_while_cond() {
+        let mut vec = vec![1, 2];
+        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let mut w = v.iter().collect::<Vec<_>>();
+        // Do lint
+        while 1 == w.len() {
+            todo!();
+        }
+    }
+
+    fn test_while_cond_false_case() {
+        let mut vec = vec![1, 2];
+        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let mut w = v.iter().collect::<Vec<_>>();
+        // Do not lint, because w is used.
+        while 1 == w.len() {
+            todo!();
+        }
+
+        w.len();
+    }
+
+    fn test_while_let_cond() {
+        let mut vec = vec![1, 2];
+        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let mut w = v.iter().collect::<Vec<_>>();
+        // Do lint
+        while let Some(i) = Some(w.len()) {
+            todo!();
+        }
+    }
+
+    fn test_while_let_cond_false_case() {
+        let mut vec = vec![1, 2];
+        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
+        let mut w = v.iter().collect::<Vec<_>>();
+        // Do not lint, because w is used.
+        while let Some(i) = Some(w.len()) {
+            todo!();
+        }
+        w.len();
+    }
+}
index 0f5e78f91198c27dd50d74e25bce448c89207d37..9f0880cc6069d1449c7ec43d6386387deddcc5b7 100644 (file)
@@ -125,5 +125,122 @@ LL ~
 LL ~         sample.iter().count()
    |
 
-error: aborting due to 9 previous errors
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:127:59
+   |
+LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+   |                                                           ^^^^^^^
+...
+LL |             y.contains(&i);
+   |             -------------- the iterator could be used here instead
+   |
+help: check if the original Iterator contains an element instead of collecting then checking
+   |
+LL ~             
+LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+LL |             // Do lint
+LL ~             vec.iter().map(|k| k * k).any(|x| x == i);
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:152:59
+   |
+LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+   |                                                           ^^^^^^^
+...
+LL |             y.contains(&n);
+   |             -------------- the iterator could be used here instead
+   |
+help: check if the original Iterator contains an element instead of collecting then checking
+   |
+LL ~             
+LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+LL |             // Do lint
+LL ~             vec.iter().map(|k| k * k).any(|x| x == n);
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:181:63
+   |
+LL |                 let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+   |                                                               ^^^^^^^
+...
+LL |                 y.contains(&n);
+   |                 -------------- the iterator could be used here instead
+   |
+help: check if the original Iterator contains an element instead of collecting then checking
+   |
+LL ~                 
+LL |                 let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+LL |                 // Do lint
+LL ~                 vec.iter().map(|k| k * k).any(|x| x == n);
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:217:59
+   |
+LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
+   |                                                           ^^^^^^^
+...
+LL |                 y.contains(&n);
+   |                 -------------- the iterator could be used here instead
+   |
+help: check if the original Iterator contains an element instead of collecting then checking
+   |
+LL ~             
+LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
+LL |             if n < 2 {
+LL |                 // Do lint
+LL ~                 vec.iter().map(|k| k * k).any(|x| x == n);
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:242:26
+   |
+LL |         let w = v.iter().collect::<Vec<_>>();
+   |                          ^^^^^^^
+LL |         // Do lint
+LL |         for _ in 0..w.len() {
+   |                     ------- the iterator could be used here instead
+   |
+help: take the original Iterator's count instead of collecting it and finding the length
+   |
+LL ~         
+LL |         // Do lint
+LL ~         for _ in 0..v.iter().count() {
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:264:30
+   |
+LL |         let mut w = v.iter().collect::<Vec<_>>();
+   |                              ^^^^^^^
+LL |         // Do lint
+LL |         while 1 == w.len() {
+   |                    ------- the iterator could be used here instead
+   |
+help: take the original Iterator's count instead of collecting it and finding the length
+   |
+LL ~         
+LL |         // Do lint
+LL ~         while 1 == v.iter().count() {
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:286:30
+   |
+LL |         let mut w = v.iter().collect::<Vec<_>>();
+   |                              ^^^^^^^
+LL |         // Do lint
+LL |         while let Some(i) = Some(w.len()) {
+   |                                  ------- the iterator could be used here instead
+   |
+help: take the original Iterator's count instead of collecting it and finding the length
+   |
+LL ~         
+LL |         // Do lint
+LL ~         while let Some(i) = Some(v.iter().count()) {
+   |
+
+error: aborting due to 16 previous errors
 
index 0c9178fb85efe39a0a3e7938e658b162fcb2efc1..7e47406798cf9b90d96e98893ed8c66c89109406 100644 (file)
@@ -207,4 +207,43 @@ impl Tr for Result<i32, i32> {
     }
 }
 
+mod issue9084 {
+    fn wildcard_if() {
+        let mut some_bool = true;
+        let e = Some(1);
+
+        // should lint
+        let _ = e;
+
+        // should lint
+        let _ = e;
+
+        // should not lint
+        let _ = match e {
+            _ if some_bool => e,
+            _ => Some(2),
+        };
+
+        // should not lint
+        let _ = match e {
+            Some(i) => Some(i + 1),
+            _ if some_bool => e,
+            _ => e,
+        };
+
+        // should not lint (guard has side effects)
+        let _ = match e {
+            Some(i) => Some(i),
+            _ if {
+                some_bool = false;
+                some_bool
+            } =>
+            {
+                e
+            },
+            _ => e,
+        };
+    }
+}
+
 fn main() {}
index f66f01d7ccaf4ef2f96be1e2e2247cebf83884f2..809c694bf400464bb4685b115010b04c63ce6147 100644 (file)
@@ -244,4 +244,50 @@ fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
     }
 }
 
+mod issue9084 {
+    fn wildcard_if() {
+        let mut some_bool = true;
+        let e = Some(1);
+
+        // should lint
+        let _ = match e {
+            _ if some_bool => e,
+            _ => e,
+        };
+
+        // should lint
+        let _ = match e {
+            Some(i) => Some(i),
+            _ if some_bool => e,
+            _ => e,
+        };
+
+        // should not lint
+        let _ = match e {
+            _ if some_bool => e,
+            _ => Some(2),
+        };
+
+        // should not lint
+        let _ = match e {
+            Some(i) => Some(i + 1),
+            _ if some_bool => e,
+            _ => e,
+        };
+
+        // should not lint (guard has side effects)
+        let _ = match e {
+            Some(i) => Some(i),
+            _ if {
+                some_bool = false;
+                some_bool
+            } =>
+            {
+                e
+            },
+            _ => e,
+        };
+    }
+}
+
 fn main() {}
index 5bc79800a1a748bcbbb696e6fa824c791af3e647..28e78441c2522fcd65528069876fc567cf071b5b 100644 (file)
@@ -109,5 +109,26 @@ LL | |             Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(
 LL | |         };
    | |_________^ help: replace it with: `ce`
 
-error: aborting due to 11 previous errors
+error: this match expression is unnecessary
+  --> $DIR/needless_match.rs:253:17
+   |
+LL |           let _ = match e {
+   |  _________________^
+LL | |             _ if some_bool => e,
+LL | |             _ => e,
+LL | |         };
+   | |_________^ help: replace it with: `e`
+
+error: this match expression is unnecessary
+  --> $DIR/needless_match.rs:259:17
+   |
+LL |           let _ = match e {
+   |  _________________^
+LL | |             Some(i) => Some(i),
+LL | |             _ if some_bool => e,
+LL | |             _ => e,
+LL | |         };
+   | |_________^ help: replace it with: `e`
+
+error: aborting due to 13 previous errors
 
index 0bc0d0011efe00cd009475cb7972c3162cde1c62..87c8fc03b3c3627766edaaf6d965f5afdc6ced2c 100644 (file)
@@ -228,13 +228,9 @@ fn needless_return_macro() -> String {
     format!("Hello {}", "world!")
 }
 
-fn check_expect() -> bool {
-    if true {
-        // no error!
-        return true;
-    }
-    #[expect(clippy::needless_return)]
-    return true;
+fn issue_9361() -> i32 {
+    #[allow(clippy::integer_arithmetic)]
+    return 1 + 2;
 }
 
 fn main() {}
index eb9f72e8e7822fe8839060d77bc9ca237d1f5057..5a86e656255dd1413d7ca1e3dd2d543846cf7350 100644 (file)
@@ -228,13 +228,9 @@ fn needless_return_macro() -> String {
     return format!("Hello {}", "world!");
 }
 
-fn check_expect() -> bool {
-    if true {
-        // no error!
-        return true;
-    }
-    #[expect(clippy::needless_return)]
-    return true;
+fn issue_9361() -> i32 {
+    #[allow(clippy::integer_arithmetic)]
+    return 1 + 2;
 }
 
 fn main() {}
index 5768434f988ebf8b141fc4ee5e67a3971df67f29..f71e8ead5195ea27653bbfd7f24869490ffcc41b 100644 (file)
 #![warn(clippy::only_used_in_recursion)]
 
-fn simple(a: usize, b: usize) -> usize {
-    if a == 0 { 1 } else { simple(a - 1, b) }
+fn _simple(x: u32) -> u32 {
+    x
 }
 
-fn with_calc(a: usize, b: isize) -> usize {
-    if a == 0 { 1 } else { with_calc(a - 1, -b + 1) }
+fn _simple2(x: u32) -> u32 {
+    _simple(x)
 }
 
-fn tuple((a, b): (usize, usize)) -> usize {
-    if a == 0 { 1 } else { tuple((a - 1, b + 1)) }
+fn _one_unused(flag: u32, a: usize) -> usize {
+    if flag == 0 { 0 } else { _one_unused(flag - 1, a) }
 }
 
-fn let_tuple(a: usize, b: usize) -> usize {
-    let (c, d) = (a, b);
-    if c == 0 { 1 } else { let_tuple(c - 1, d + 1) }
+fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+    if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
 }
 
-fn array([a, b]: [usize; 2]) -> usize {
-    if a == 0 { 1 } else { array([a - 1, b + 1]) }
-}
-
-fn index(a: usize, mut b: &[usize], c: usize) -> usize {
-    if a == 0 { 1 } else { index(a - 1, b, c + b[0]) }
-}
-
-fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
-    let c = loop {
-        b += 1;
-        c += 1;
-        if c == 10 {
-            break b;
-        }
-    };
-
-    if a == 0 { 1 } else { break_(a - 1, c, c) }
+fn _with_calc(flag: u32, a: i64) -> usize {
+    if flag == 0 {
+        0
+    } else {
+        _with_calc(flag - 1, (-a + 10) * 5)
+    }
 }
 
-// this has a side effect
-fn mut_ref(a: usize, b: &mut usize) -> usize {
-    *b = 1;
-    if a == 0 { 1 } else { mut_ref(a - 1, b) }
+// Don't lint
+fn _used_with_flag(flag: u32, a: u32) -> usize {
+    if flag == 0 { 0 } else { _used_with_flag(flag ^ a, a - 1) }
 }
 
-fn mut_ref2(a: usize, b: &mut usize) -> usize {
-    let mut c = *b;
-    if a == 0 { 1 } else { mut_ref2(a - 1, &mut c) }
+fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+    if flag == 0 {
+        0
+    } else {
+        _used_with_unused(flag - 1, -a, a + b)
+    }
 }
 
-fn not_primitive(a: usize, b: String) -> usize {
-    if a == 0 { 1 } else { not_primitive(a - 1, b) }
+fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+    if flag == 0 {
+        0
+    } else {
+        _codependent_unused(flag - 1, a * b, a + b)
+    }
 }
 
-// this doesn't have a side effect,
-// but `String` is not primitive.
-fn not_primitive_op(a: usize, b: String, c: &str) -> usize {
-    if a == 1 { 1 } else { not_primitive_op(a, b + c, c) }
+fn _not_primitive(flag: u32, b: String) -> usize {
+    if flag == 0 { 0 } else { _not_primitive(flag - 1, b) }
 }
 
 struct A;
 
 impl A {
-    fn method(a: usize, b: usize) -> usize {
-        if a == 0 { 1 } else { A::method(a - 1, b - 1) }
+    fn _method(flag: usize, a: usize) -> usize {
+        if flag == 0 { 0 } else { Self::_method(flag - 1, a) }
     }
 
-    fn method2(&self, a: usize, b: usize) -> usize {
-        if a == 0 { 1 } else { self.method2(a - 1, b + 1) }
+    fn _method_self(&self, flag: usize, a: usize) -> usize {
+        if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
     }
 }
 
 trait B {
-    fn hello(a: usize, b: usize) -> usize;
-
-    fn hello2(&self, a: usize, b: usize) -> usize;
+    fn method(flag: u32, a: usize) -> usize;
+    fn method_self(&self, flag: u32, a: usize) -> usize;
 }
 
 impl B for A {
-    fn hello(a: usize, b: usize) -> usize {
-        if a == 0 { 1 } else { A::hello(a - 1, b + 1) }
+    fn method(flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { Self::method(flag - 1, a) }
     }
 
-    fn hello2(&self, a: usize, b: usize) -> usize {
-        if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
+    fn method_self(&self, flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
     }
 }
 
-trait C {
-    fn hello(a: usize, b: usize) -> usize {
-        if a == 0 { 1 } else { Self::hello(a - 1, b + 1) }
+impl B for () {
+    fn method(flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { a }
     }
 
-    fn hello2(&self, a: usize, b: usize) -> usize {
-        if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
+    fn method_self(&self, flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { a }
     }
 }
 
-fn ignore(a: usize, _: usize) -> usize {
-    if a == 1 { 1 } else { ignore(a - 1, 0) }
-}
+impl B for u32 {
+    fn method(flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { <() as B>::method(flag, a) }
+    }
 
-fn ignore2(a: usize, _b: usize) -> usize {
-    if a == 1 { 1 } else { ignore2(a - 1, _b) }
+    fn method_self(&self, flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { ().method_self(flag, a) }
+    }
 }
 
-fn f1(a: u32) -> u32 {
-    a
-}
+trait C {
+    fn method(flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+    }
 
-fn f2(a: u32) -> u32 {
-    f1(a)
+    fn method_self(&self, flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+    }
 }
 
-fn inner_fn(a: u32) -> u32 {
-    fn inner_fn(a: u32) -> u32 {
-        a
-    }
-    inner_fn(a)
+fn _ignore(flag: usize, _a: usize) -> usize {
+    if flag == 0 { 0 } else { _ignore(flag - 1, _a) }
 }
 
 fn main() {}
index 6fe9361bf5feb2eee0d903887ae911f3673f5706..74057ddcfda4c2cca6e77a64c43313539fe5066c 100644 (file)
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:3:21
+  --> $DIR/only_used_in_recursion.rs:11:27
    |
-LL | fn simple(a: usize, b: usize) -> usize {
-   |                     ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | fn _one_unused(flag: u32, a: usize) -> usize {
+   |                           ^ help: if this is intentional, prefix it with an underscore: `_a`
    |
    = note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:12:53
+   |
+LL |     if flag == 0 { 0 } else { _one_unused(flag - 1, a) }
+   |                                                     ^
+
+error: parameter is only used in recursion
+  --> $DIR/only_used_in_recursion.rs:15:27
+   |
+LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+   |                           ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:16:53
+   |
+LL |     if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
+   |                                                     ^
+
+error: parameter is only used in recursion
+  --> $DIR/only_used_in_recursion.rs:15:35
+   |
+LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+   |                                   ^ help: if this is intentional, prefix it with an underscore: `_b`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:16:56
+   |
+LL |     if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
+   |                                                        ^
+
+error: parameter is only used in recursion
+  --> $DIR/only_used_in_recursion.rs:19:26
+   |
+LL | fn _with_calc(flag: u32, a: i64) -> usize {
+   |                          ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:23:32
+   |
+LL |         _with_calc(flag - 1, (-a + 10) * 5)
+   |                                ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:7:24
+  --> $DIR/only_used_in_recursion.rs:32:33
+   |
+LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+   |                                 ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:36:38
    |
-LL | fn with_calc(a: usize, b: isize) -> usize {
-   |                        ^ help: if this is intentional, prefix with an underscore: `_b`
+LL |         _used_with_unused(flag - 1, -a, a + b)
+   |                                      ^  ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:11:14
+  --> $DIR/only_used_in_recursion.rs:32:41
+   |
+LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+   |                                         ^ help: if this is intentional, prefix it with an underscore: `_b`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:36:45
    |
-LL | fn tuple((a, b): (usize, usize)) -> usize {
-   |              ^ help: if this is intentional, prefix with an underscore: `_b`
+LL |         _used_with_unused(flag - 1, -a, a + b)
+   |                                             ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:15:24
+  --> $DIR/only_used_in_recursion.rs:40:35
    |
-LL | fn let_tuple(a: usize, b: usize) -> usize {
-   |                        ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+   |                                   ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:44:39
+   |
+LL |         _codependent_unused(flag - 1, a * b, a + b)
+   |                                       ^      ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:20:14
+  --> $DIR/only_used_in_recursion.rs:40:43
+   |
+LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+   |                                           ^ help: if this is intentional, prefix it with an underscore: `_b`
    |
-LL | fn array([a, b]: [usize; 2]) -> usize {
-   |              ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:44:43
+   |
+LL |         _codependent_unused(flag - 1, a * b, a + b)
+   |                                           ^      ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:24:20
+  --> $DIR/only_used_in_recursion.rs:48:30
+   |
+LL | fn _not_primitive(flag: u32, b: String) -> usize {
+   |                              ^ help: if this is intentional, prefix it with an underscore: `_b`
    |
-LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
-   |                    ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:49:56
+   |
+LL |     if flag == 0 { 0 } else { _not_primitive(flag - 1, b) }
+   |                                                        ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:24:37
+  --> $DIR/only_used_in_recursion.rs:55:29
+   |
+LL |     fn _method(flag: usize, a: usize) -> usize {
+   |                             ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:56:59
    |
-LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
-   |                                     ^ help: if this is intentional, prefix with an underscore: `_c`
+LL |         if flag == 0 { 0 } else { Self::_method(flag - 1, a) }
+   |                                                           ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:28:21
+  --> $DIR/only_used_in_recursion.rs:59:22
+   |
+LL |     fn _method_self(&self, flag: usize, a: usize) -> usize {
+   |                      ^^^^
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:60:35
    |
-LL | fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
-   |                     ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
+LL |         if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
+   |                                   ^^^^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:46:23
+  --> $DIR/only_used_in_recursion.rs:59:41
    |
-LL | fn mut_ref2(a: usize, b: &mut usize) -> usize {
-   |                       ^ help: if this is intentional, prefix with an underscore: `_b`
+LL |     fn _method_self(&self, flag: usize, a: usize) -> usize {
+   |                                         ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:60:63
+   |
+LL |         if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
+   |                                                               ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:51:28
+  --> $DIR/only_used_in_recursion.rs:70:26
+   |
+LL |     fn method(flag: u32, a: usize) -> usize {
+   |                          ^ help: if this is intentional, prefix it with an underscore: `_a`
    |
-LL | fn not_primitive(a: usize, b: String) -> usize {
-   |                            ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:71:58
+   |
+LL |         if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+   |                                                          ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:68:33
+  --> $DIR/only_used_in_recursion.rs:74:38
+   |
+LL |     fn method_self(&self, flag: u32, a: usize) -> usize {
+   |                                      ^ help: if this is intentional, prefix it with an underscore: `_a`
    |
-LL |     fn method2(&self, a: usize, b: usize) -> usize {
-   |                                 ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:75:62
+   |
+LL |         if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+   |                                                              ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:90:24
+  --> $DIR/only_used_in_recursion.rs:100:26
+   |
+LL |     fn method(flag: u32, a: usize) -> usize {
+   |                          ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:101:58
    |
-LL |     fn hello(a: usize, b: usize) -> usize {
-   |                        ^ help: if this is intentional, prefix with an underscore: `_b`
+LL |         if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+   |                                                          ^
 
 error: parameter is only used in recursion
-  --> $DIR/only_used_in_recursion.rs:94:32
+  --> $DIR/only_used_in_recursion.rs:104:38
+   |
+LL |     fn method_self(&self, flag: u32, a: usize) -> usize {
+   |                                      ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion.rs:105:62
    |
-LL |     fn hello2(&self, a: usize, b: usize) -> usize {
-   |                                ^ help: if this is intentional, prefix with an underscore: `_b`
+LL |         if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+   |                                                              ^
 
-error: aborting due to 13 previous errors
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/only_used_in_recursion2.rs b/src/tools/clippy/tests/ui/only_used_in_recursion2.rs
new file mode 100644 (file)
index 0000000..45dd055
--- /dev/null
@@ -0,0 +1,91 @@
+#![warn(clippy::only_used_in_recursion)]
+
+fn _with_inner(flag: u32, a: u32, b: u32) -> usize {
+    fn inner(flag: u32, a: u32) -> u32 {
+        if flag == 0 { 0 } else { inner(flag, a) }
+    }
+
+    let x = inner(flag, a);
+    if flag == 0 { 0 } else { _with_inner(flag, a, b + x) }
+}
+
+fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 {
+    if let Some(x) = a.and_then(|x| f(x, x)) {
+        _with_closure(Some(x), b, f)
+    } else {
+        0
+    }
+}
+
+// Issue #8560
+trait D {
+    fn foo(&mut self, arg: u32) -> u32;
+}
+
+mod m {
+    pub struct S(u32);
+    impl S {
+        pub fn foo(&mut self, arg: u32) -> u32 {
+            arg + self.0
+        }
+    }
+}
+
+impl D for m::S {
+    fn foo(&mut self, arg: u32) -> u32 {
+        self.foo(arg)
+    }
+}
+
+// Issue #8782
+fn only_let(x: u32) {
+    let y = 10u32;
+    let _z = x * y;
+}
+
+trait E<T: E<()>> {
+    fn method(flag: u32, a: usize) -> usize {
+        if flag == 0 {
+            0
+        } else {
+            <T as E<()>>::method(flag - 1, a)
+        }
+    }
+}
+
+impl E<()> for () {
+    fn method(flag: u32, a: usize) -> usize {
+        if flag == 0 { 0 } else { a }
+    }
+}
+
+fn overwritten_param(flag: u32, mut a: usize) -> usize {
+    if flag == 0 {
+        return 0;
+    } else if flag > 5 {
+        a += flag as usize;
+    } else {
+        a = 5;
+    }
+    overwritten_param(flag, a)
+}
+
+fn field_direct(flag: u32, mut a: (usize,)) -> usize {
+    if flag == 0 {
+        0
+    } else {
+        a.0 += 5;
+        field_direct(flag - 1, a)
+    }
+}
+
+fn field_deref(flag: u32, a: &mut Box<(usize,)>) -> usize {
+    if flag == 0 {
+        0
+    } else {
+        a.0 += 5;
+        field_deref(flag - 1, a)
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/only_used_in_recursion2.stderr b/src/tools/clippy/tests/ui/only_used_in_recursion2.stderr
new file mode 100644 (file)
index 0000000..23f6ffd
--- /dev/null
@@ -0,0 +1,63 @@
+error: parameter is only used in recursion
+  --> $DIR/only_used_in_recursion2.rs:3:35
+   |
+LL | fn _with_inner(flag: u32, a: u32, b: u32) -> usize {
+   |                                   ^ help: if this is intentional, prefix it with an underscore: `_b`
+   |
+   = note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
+note: parameter used here
+  --> $DIR/only_used_in_recursion2.rs:9:52
+   |
+LL |     if flag == 0 { 0 } else { _with_inner(flag, a, b + x) }
+   |                                                    ^
+
+error: parameter is only used in recursion
+  --> $DIR/only_used_in_recursion2.rs:4:25
+   |
+LL |     fn inner(flag: u32, a: u32) -> u32 {
+   |                         ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion2.rs:5:47
+   |
+LL |         if flag == 0 { 0 } else { inner(flag, a) }
+   |                                               ^
+
+error: parameter is only used in recursion
+  --> $DIR/only_used_in_recursion2.rs:12:34
+   |
+LL | fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 {
+   |                                  ^ help: if this is intentional, prefix it with an underscore: `_b`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion2.rs:14:32
+   |
+LL |         _with_closure(Some(x), b, f)
+   |                                ^
+
+error: parameter is only used in recursion
+  --> $DIR/only_used_in_recursion2.rs:62:37
+   |
+LL | fn overwritten_param(flag: u32, mut a: usize) -> usize {
+   |                                     ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion2.rs:70:29
+   |
+LL |     overwritten_param(flag, a)
+   |                             ^
+
+error: parameter is only used in recursion
+  --> $DIR/only_used_in_recursion2.rs:73:32
+   |
+LL | fn field_direct(flag: u32, mut a: (usize,)) -> usize {
+   |                                ^ help: if this is intentional, prefix it with an underscore: `_a`
+   |
+note: parameter used here
+  --> $DIR/only_used_in_recursion2.rs:78:32
+   |
+LL |         field_direct(flag - 1, a)
+   |                                ^
+
+error: aborting due to 5 previous errors
+
index b6d5e106f057a36ab1503f701f144a37614e98db..f15ac551bb3ccfeefa774ae183e91f98a66f6ce0 100644 (file)
@@ -179,4 +179,13 @@ fn main() {
 
     let _ = pattern_to_vec("hello world");
     let _ = complex_subpat();
+
+    // issue #8492
+    let _ = s.map_or(1, |string| string.len());
+    let _ = Some(10).map_or(5, |a| a + 1);
+
+    let res: Result<i32, i32> = Ok(5);
+    let _ = res.map_or(1, |a| a + 1);
+    let _ = res.map_or(1, |a| a + 1);
+    let _ = res.map_or(5, |a| a + 1);
 }
index 35bae159343587be7ea1a98c959260a0cc3ab62b..9eeaea12d3bc91e615cb2fac5f3d3b10c0ffd0c9 100644 (file)
@@ -208,4 +208,25 @@ async fn _f2() {
 
     let _ = pattern_to_vec("hello world");
     let _ = complex_subpat();
+
+    // issue #8492
+    let _ = match s {
+        Some(string) => string.len(),
+        None => 1,
+    };
+    let _ = match Some(10) {
+        Some(a) => a + 1,
+        None => 5,
+    };
+
+    let res: Result<i32, i32> = Ok(5);
+    let _ = match res {
+        Ok(a) => a + 1,
+        _ => 1,
+    };
+    let _ = match res {
+        Err(_) => 1,
+        Ok(a) => a + 1,
+    };
+    let _ = if let Ok(a) = res { a + 1 } else { 5 };
 }
index daba606004e114d68ed95d95682e94a4fd59c7f2..a5dbf6e1f2218020a7439059f9a59f5dba493fde 100644 (file)
@@ -206,5 +206,51 @@ LL +         s.len() + x
 LL ~     });
    |
 
-error: aborting due to 15 previous errors
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:213:13
+   |
+LL |       let _ = match s {
+   |  _____________^
+LL | |         Some(string) => string.len(),
+LL | |         None => 1,
+LL | |     };
+   | |_____^ help: try: `s.map_or(1, |string| string.len())`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:217:13
+   |
+LL |       let _ = match Some(10) {
+   |  _____________^
+LL | |         Some(a) => a + 1,
+LL | |         None => 5,
+LL | |     };
+   | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:223:13
+   |
+LL |       let _ = match res {
+   |  _____________^
+LL | |         Ok(a) => a + 1,
+LL | |         _ => 1,
+LL | |     };
+   | |_____^ help: try: `res.map_or(1, |a| a + 1)`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:227:13
+   |
+LL |       let _ = match res {
+   |  _____________^
+LL | |         Err(_) => 1,
+LL | |         Ok(a) => a + 1,
+LL | |     };
+   | |_____^ help: try: `res.map_or(1, |a| a + 1)`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:231:13
+   |
+LL |     let _ = if let Ok(a) = res { a + 1 } else { 5 };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)`
+
+error: aborting due to 20 previous errors
 
index fdb08d953ff1dbc95263e6d41e88861310d9a6b5..18ea4e550292a4b3ddd5a729f4ba946afe2f4d13 100644 (file)
@@ -90,8 +90,8 @@ fn or_fun_call() {
     let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
     btree_vec.entry(42).or_insert(vec![]);
 
-    let stringy = Some(String::from(""));
-    let _ = stringy.unwrap_or_else(|| "".to_owned());
+    let stringy = Some(String::new());
+    let _ = stringy.unwrap_or_default();
 
     let opt = Some(1);
     let hello = "Hello";
index 57ab5f03ee2851318b3ba58d9fcc2c72d23e442e..c353b41e4495d96feee4525b5f1d20634d8bc694 100644 (file)
@@ -90,8 +90,8 @@ fn make<T>() -> T {
     let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
     btree_vec.entry(42).or_insert(vec![]);
 
-    let stringy = Some(String::from(""));
-    let _ = stringy.unwrap_or("".to_owned());
+    let stringy = Some(String::new());
+    let _ = stringy.unwrap_or(String::new());
 
     let opt = Some(1);
     let hello = "Hello";
index 4c5938ab88b90926c25c189063ad1b4ab1e268f5..887f23ac9761dfd7167f44f269d3224cb6d75e75 100644 (file)
@@ -66,11 +66,11 @@ error: use of `unwrap_or` followed by a function call
 LL |     without_default.unwrap_or(Foo::new());
    |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
 
-error: use of `unwrap_or` followed by a function call
+error: use of `unwrap_or` followed by a call to `new`
   --> $DIR/or_fun_call.rs:94:21
    |
-LL |     let _ = stringy.unwrap_or("".to_owned());
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
+LL |     let _ = stringy.unwrap_or(String::new());
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a function call
   --> $DIR/or_fun_call.rs:102:21
index f3e4c58d6949223e0fe16b7e32c5bac5d22a34de..4644ea8f51da1ecd6def7c067aff736cdcebfd8a 100644 (file)
@@ -26,6 +26,18 @@ fn optref() -> &'static &'static Option<()> {
     &&None
 }
 
+pub fn macro_expansion() {
+    macro_rules! foo {
+        () => {
+            None::<()>
+        };
+    }
+
+    let _ = foobar() == foo!();
+    let _ = foo!() == foobar();
+    let _ = foo!() == foo!();
+}
+
 fn main() {
     let x = Some(0);
 
index 767b2a38bcc17ddba17267c2faa09ad10bef6cc1..61011b3a8c553cdb4a70704a1f46aaeea838494a 100644 (file)
@@ -26,6 +26,18 @@ fn optref() -> &'static &'static Option<()> {
     &&None
 }
 
+pub fn macro_expansion() {
+    macro_rules! foo {
+        () => {
+            None::<()>
+        };
+    }
+
+    let _ = foobar() == foo!();
+    let _ = foo!() == foobar();
+    let _ = foo!() == foo!();
+}
+
 fn main() {
     let x = Some(0);
 
index de15a9f7baaf030b02b5d4ab7a33a2e960134082..d06ab7aee558b3e04b59948413d022199a980956 100644 (file)
@@ -7,55 +7,55 @@ LL |     if f != None { "yay" } else { "nay" }
    = note: `-D clippy::partialeq-to-none` implied by `-D warnings`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:32:13
+  --> $DIR/partialeq_to_none.rs:44:13
    |
 LL |     let _ = x == None;
    |             ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:33:13
+  --> $DIR/partialeq_to_none.rs:45:13
    |
 LL |     let _ = x != None;
    |             ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:34:13
+  --> $DIR/partialeq_to_none.rs:46:13
    |
 LL |     let _ = None == x;
    |             ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:35:13
+  --> $DIR/partialeq_to_none.rs:47:13
    |
 LL |     let _ = None != x;
    |             ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:37:8
+  --> $DIR/partialeq_to_none.rs:49:8
    |
 LL |     if foobar() == None {}
    |        ^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `foobar().is_none()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:39:8
+  --> $DIR/partialeq_to_none.rs:51:8
    |
 LL |     if bar().ok() != None {}
    |        ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `bar().ok().is_some()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:41:13
+  --> $DIR/partialeq_to_none.rs:53:13
    |
 LL |     let _ = Some(1 + 2) != None;
    |             ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `Some(1 + 2).is_some()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:43:13
+  --> $DIR/partialeq_to_none.rs:55:13
    |
 LL |     let _ = { Some(0) } == None;
    |             ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `{ Some(0) }.is_none()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:45:13
+  --> $DIR/partialeq_to_none.rs:57:13
    |
 LL |       let _ = {
    |  _____________^
@@ -77,31 +77,31 @@ LL ~     }.is_some();
    |
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:55:13
+  --> $DIR/partialeq_to_none.rs:67:13
    |
 LL |     let _ = optref() == &&None;
    |             ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:56:13
+  --> $DIR/partialeq_to_none.rs:68:13
    |
 LL |     let _ = &&None != optref();
    |             ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:57:13
+  --> $DIR/partialeq_to_none.rs:69:13
    |
 LL |     let _ = **optref() == None;
    |             ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:58:13
+  --> $DIR/partialeq_to_none.rs:70:13
    |
 LL |     let _ = &None != *optref();
    |             ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
 
 error: binary comparison to literal `Option::None`
-  --> $DIR/partialeq_to_none.rs:61:13
+  --> $DIR/partialeq_to_none.rs:73:13
    |
 LL |     let _ = None != *x;
    |             ^^^^^^^^^^ help: use `Option::is_some()` instead: `(*x).is_some()`
diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.fixed b/src/tools/clippy/tests/ui/positional_named_format_parameters.fixed
new file mode 100644 (file)
index 0000000..4170e10
--- /dev/null
@@ -0,0 +1,56 @@
+// run-rustfix
+#![allow(unused_must_use)]
+#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
+#![warn(clippy::positional_named_format_parameters)]
+
+use std::io::Write;
+
+fn main() {
+    let mut v = Vec::new();
+    let hello = "Hello";
+
+    println!("{hello:.foo$}", foo = 2);
+    writeln!(v, "{hello:.foo$}", foo = 2);
+
+    // Warnings
+    println!("{zero} {one:?}", zero = 0, one = 1);
+    println!("This is a test {zero} {one:?}", zero = 0, one = 1);
+    println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
+    println!("Hello {one:zero$}!", zero = 5, one = 1);
+    println!("Hello {zero:one$}!", zero = 4, one = 1);
+    println!("Hello {zero:0one$}!", zero = 4, one = 1);
+    println!("Hello is {one:.zero$}", zero = 5, one = 0.01);
+    println!("Hello is {one:<6.zero$}", zero = 5, one = 0.01);
+    println!("{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
+    println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
+    println!("Hello {world} {world}!", world = 5);
+
+    writeln!(v, "{zero} {one:?}", zero = 0, one = 1);
+    writeln!(v, "This is a test {zero} {one:?}", zero = 0, one = 1);
+    writeln!(v, "Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
+    writeln!(v, "Hello {one:zero$}!", zero = 4, one = 1);
+    writeln!(v, "Hello {zero:one$}!", zero = 4, one = 1);
+    writeln!(v, "Hello {zero:0one$}!", zero = 4, one = 1);
+    writeln!(v, "Hello is {one:.zero$}", zero = 3, one = 0.01);
+    writeln!(v, "Hello is {one:<6.zero$}", zero = 2, one = 0.01);
+    writeln!(v, "{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
+    writeln!(v, "Hello {one} is {two:.zero$}", zero = 1, one = hello, two = 0.01);
+    writeln!(v, "Hello {world} {world}!", world = 0);
+
+    // Tests from other files
+    println!("{w:w$}", w = 1);
+    println!("{p:.p$}", p = 1);
+    println!("{v}", v = 1);
+    println!("{v:v$}", v = 1);
+    println!("{v:v$}", v = 1);
+    println!("{v:v$.v$}", v = 1);
+    println!("{v:v$.v$}", v = 1);
+    println!("{v:v$.v$}", v = 1);
+    println!("{v:v$.v$}", v = 1);
+    println!("{v:v$.v$}", v = 1);
+    println!("{v:v$.v$}", v = 1);
+    println!("{v:v$.v$}", v = 1);
+    println!("{w:w$}", w = 1);
+    println!("{p:.p$}", p = 1);
+    println!("{:p$.w$}", 1, w = 1, p = 1);
+}
diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.rs b/src/tools/clippy/tests/ui/positional_named_format_parameters.rs
new file mode 100644 (file)
index 0000000..553d849
--- /dev/null
@@ -0,0 +1,56 @@
+// run-rustfix
+#![allow(unused_must_use)]
+#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
+#![warn(clippy::positional_named_format_parameters)]
+
+use std::io::Write;
+
+fn main() {
+    let mut v = Vec::new();
+    let hello = "Hello";
+
+    println!("{hello:.foo$}", foo = 2);
+    writeln!(v, "{hello:.foo$}", foo = 2);
+
+    // Warnings
+    println!("{} {1:?}", zero = 0, one = 1);
+    println!("This is a test { } {000001:?}", zero = 0, one = 1);
+    println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+    println!("Hello {1:0$}!", zero = 5, one = 1);
+    println!("Hello {0:1$}!", zero = 4, one = 1);
+    println!("Hello {0:01$}!", zero = 4, one = 1);
+    println!("Hello is {1:.*}", zero = 5, one = 0.01);
+    println!("Hello is {:<6.*}", zero = 5, one = 0.01);
+    println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+    println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+    println!("Hello {world} {}!", world = 5);
+
+    writeln!(v, "{} {1:?}", zero = 0, one = 1);
+    writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
+    writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+    writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
+    writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
+    writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
+    writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
+    writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
+    writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+    writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
+    writeln!(v, "Hello {world} {}!", world = 0);
+
+    // Tests from other files
+    println!("{:w$}", w = 1);
+    println!("{:.p$}", p = 1);
+    println!("{}", v = 1);
+    println!("{:0$}", v = 1);
+    println!("{0:0$}", v = 1);
+    println!("{:0$.0$}", v = 1);
+    println!("{0:0$.0$}", v = 1);
+    println!("{0:0$.v$}", v = 1);
+    println!("{0:v$.0$}", v = 1);
+    println!("{v:0$.0$}", v = 1);
+    println!("{v:v$.0$}", v = 1);
+    println!("{v:0$.v$}", v = 1);
+    println!("{:w$}", w = 1);
+    println!("{:.p$}", p = 1);
+    println!("{:p$.w$}", 1, w = 1, p = 1);
+}
diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.stderr b/src/tools/clippy/tests/ui/positional_named_format_parameters.stderr
new file mode 100644 (file)
index 0000000..48ddb6d
--- /dev/null
@@ -0,0 +1,418 @@
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:16:16
+   |
+LL |     println!("{} {1:?}", zero = 0, one = 1);
+   |                ^ help: replace it with: `zero`
+   |
+   = note: `-D clippy::positional-named-format-parameters` implied by `-D warnings`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:16:19
+   |
+LL |     println!("{} {1:?}", zero = 0, one = 1);
+   |                   ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:17:31
+   |
+LL |     println!("This is a test { } {000001:?}", zero = 0, one = 1);
+   |                               ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:17:35
+   |
+LL |     println!("This is a test { } {000001:?}", zero = 0, one = 1);
+   |                                   ^^^^^^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:18:32
+   |
+LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                                ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:18:22
+   |
+LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                      ^ help: replace it with: `one`
+
+error: named parameter two is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:18:29
+   |
+LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                             ^ help: replace it with: `two`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:19:24
+   |
+LL |     println!("Hello {1:0$}!", zero = 5, one = 1);
+   |                        ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:19:22
+   |
+LL |     println!("Hello {1:0$}!", zero = 5, one = 1);
+   |                      ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:20:22
+   |
+LL |     println!("Hello {0:1$}!", zero = 4, one = 1);
+   |                      ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:20:24
+   |
+LL |     println!("Hello {0:1$}!", zero = 4, one = 1);
+   |                        ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:21:22
+   |
+LL |     println!("Hello {0:01$}!", zero = 4, one = 1);
+   |                      ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:21:25
+   |
+LL |     println!("Hello {0:01$}!", zero = 4, one = 1);
+   |                         ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:22:28
+   |
+LL |     println!("Hello is {1:.*}", zero = 5, one = 0.01);
+   |                            ^ help: replace it with: `zero$`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:22:25
+   |
+LL |     println!("Hello is {1:.*}", zero = 5, one = 0.01);
+   |                         ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:23:29
+   |
+LL |     println!("Hello is {:<6.*}", zero = 5, one = 0.01);
+   |                             ^ help: replace it with: `zero$`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:23:25
+   |
+LL |     println!("Hello is {:<6.*}", zero = 5, one = 0.01);
+   |                         ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:24:16
+   |
+LL |     println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+   |                ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:24:28
+   |
+LL |     println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+   |                            ^ help: replace it with: `one$`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:25:32
+   |
+LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                                ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:25:22
+   |
+LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                      ^ help: replace it with: `one`
+
+error: named parameter two is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:25:29
+   |
+LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                             ^ help: replace it with: `two`
+
+error: named parameter world is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:26:30
+   |
+LL |     println!("Hello {world} {}!", world = 5);
+   |                              ^ help: replace it with: `world`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:28:19
+   |
+LL |     writeln!(v, "{} {1:?}", zero = 0, one = 1);
+   |                   ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:28:22
+   |
+LL |     writeln!(v, "{} {1:?}", zero = 0, one = 1);
+   |                      ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:29:34
+   |
+LL |     writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
+   |                                  ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:29:38
+   |
+LL |     writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
+   |                                      ^^^^^^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:30:35
+   |
+LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                                   ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:30:25
+   |
+LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                         ^ help: replace it with: `one`
+
+error: named parameter two is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:30:32
+   |
+LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
+   |                                ^ help: replace it with: `two`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:31:27
+   |
+LL |     writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
+   |                           ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:31:25
+   |
+LL |     writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
+   |                         ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:32:25
+   |
+LL |     writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
+   |                         ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:32:27
+   |
+LL |     writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
+   |                           ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:33:25
+   |
+LL |     writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
+   |                         ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:33:28
+   |
+LL |     writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
+   |                            ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:34:31
+   |
+LL |     writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
+   |                               ^ help: replace it with: `zero$`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:34:28
+   |
+LL |     writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
+   |                            ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:35:32
+   |
+LL |     writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
+   |                                ^ help: replace it with: `zero$`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:35:28
+   |
+LL |     writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
+   |                            ^ help: replace it with: `one`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:36:19
+   |
+LL |     writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+   |                   ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:36:31
+   |
+LL |     writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
+   |                               ^ help: replace it with: `one$`
+
+error: named parameter zero is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:37:35
+   |
+LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
+   |                                   ^ help: replace it with: `zero`
+
+error: named parameter one is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:37:25
+   |
+LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
+   |                         ^ help: replace it with: `one`
+
+error: named parameter two is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:37:32
+   |
+LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
+   |                                ^ help: replace it with: `two`
+
+error: named parameter world is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:38:33
+   |
+LL |     writeln!(v, "Hello {world} {}!", world = 0);
+   |                                 ^ help: replace it with: `world`
+
+error: named parameter w is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:41:16
+   |
+LL |     println!("{:w$}", w = 1);
+   |                ^ help: replace it with: `w`
+
+error: named parameter p is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:42:16
+   |
+LL |     println!("{:.p$}", p = 1);
+   |                ^ help: replace it with: `p`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:43:16
+   |
+LL |     println!("{}", v = 1);
+   |                ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:44:16
+   |
+LL |     println!("{:0$}", v = 1);
+   |                ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:44:17
+   |
+LL |     println!("{:0$}", v = 1);
+   |                 ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:45:16
+   |
+LL |     println!("{0:0$}", v = 1);
+   |                ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:45:18
+   |
+LL |     println!("{0:0$}", v = 1);
+   |                  ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:46:16
+   |
+LL |     println!("{:0$.0$}", v = 1);
+   |                ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:46:20
+   |
+LL |     println!("{:0$.0$}", v = 1);
+   |                    ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:46:17
+   |
+LL |     println!("{:0$.0$}", v = 1);
+   |                 ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:47:16
+   |
+LL |     println!("{0:0$.0$}", v = 1);
+   |                ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:47:21
+   |
+LL |     println!("{0:0$.0$}", v = 1);
+   |                     ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:47:18
+   |
+LL |     println!("{0:0$.0$}", v = 1);
+   |                  ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:48:16
+   |
+LL |     println!("{0:0$.v$}", v = 1);
+   |                ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:48:18
+   |
+LL |     println!("{0:0$.v$}", v = 1);
+   |                  ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:49:16
+   |
+LL |     println!("{0:v$.0$}", v = 1);
+   |                ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:49:21
+   |
+LL |     println!("{0:v$.0$}", v = 1);
+   |                     ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:50:21
+   |
+LL |     println!("{v:0$.0$}", v = 1);
+   |                     ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:50:18
+   |
+LL |     println!("{v:0$.0$}", v = 1);
+   |                  ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:51:21
+   |
+LL |     println!("{v:v$.0$}", v = 1);
+   |                     ^ help: replace it with: `v`
+
+error: named parameter v is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:52:18
+   |
+LL |     println!("{v:0$.v$}", v = 1);
+   |                  ^ help: replace it with: `v`
+
+error: named parameter w is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:53:16
+   |
+LL |     println!("{:w$}", w = 1);
+   |                ^ help: replace it with: `w`
+
+error: named parameter p is used as a positional parameter
+  --> $DIR/positional_named_format_parameters.rs:54:16
+   |
+LL |     println!("{:.p$}", p = 1);
+   |                ^ help: replace it with: `p`
+
+error: aborting due to 69 previous errors
+
index c4c9c82143336647e2dafb04292544c81797c6d5..57f23bd1916ffaa46272f152ebc52f5d79c1f8e3 100644 (file)
@@ -207,4 +207,19 @@ fn option_map() -> Option<bool> {
     }
 }
 
+pub struct PatternedError {
+    flag: bool,
+}
+
+// No warning
+fn pattern() -> Result<(), PatternedError> {
+    let res = Ok(());
+
+    if let Err(err @ PatternedError { flag: true }) = res {
+        return Err(err);
+    }
+
+    res
+}
+
 fn main() {}
index cdbc7b1606f80782c7d107e213311e4b79cb6769..436f027c215d54f459d244628acd9b358899c51c 100644 (file)
@@ -243,4 +243,19 @@ fn option_map() -> Option<bool> {
     }
 }
 
+pub struct PatternedError {
+    flag: bool,
+}
+
+// No warning
+fn pattern() -> Result<(), PatternedError> {
+    let res = Ok(());
+
+    if let Err(err @ PatternedError { flag: true }) = res {
+        return Err(err);
+    }
+
+    res
+}
+
 fn main() {}
index f7f3b195ccc18f28143e4503dceab210a0d583e9..f0e1a8128d7c36b5bc44d3d77c7ee270988780d7 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(unused)]
+#![allow(unused, clippy::needless_borrow)]
 #![warn(clippy::invalid_regex, clippy::trivial_regex)]
 
 extern crate regex;
diff --git a/src/tools/clippy/tests/ui/result_large_err.rs b/src/tools/clippy/tests/ui/result_large_err.rs
new file mode 100644 (file)
index 0000000..78d8f76
--- /dev/null
@@ -0,0 +1,98 @@
+#![warn(clippy::result_large_err)]
+
+pub fn small_err() -> Result<(), u128> {
+    Ok(())
+}
+
+pub fn large_err() -> Result<(), [u8; 512]> {
+    Ok(())
+}
+
+pub struct FullyDefinedLargeError {
+    _foo: u128,
+    _bar: [u8; 100],
+    _foobar: [u8; 120],
+}
+
+impl FullyDefinedLargeError {
+    pub fn ret() -> Result<(), Self> {
+        Ok(())
+    }
+}
+
+pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
+    Ok(())
+}
+
+type Fdlr<T> = std::result::Result<T, FullyDefinedLargeError>;
+pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
+    Ok(x)
+}
+
+pub fn param_small_error<R>() -> Result<(), (R, u128)> {
+    Ok(())
+}
+
+pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
+    Ok(())
+}
+
+pub enum LargeErrorVariants<T> {
+    _Small(u8),
+    _Omg([u8; 512]),
+    _Param(T),
+}
+
+impl LargeErrorVariants<()> {
+    pub fn large_enum_error() -> Result<(), Self> {
+        Ok(())
+    }
+}
+
+trait TraitForcesLargeError {
+    fn large_error() -> Result<(), [u8; 512]> {
+        Ok(())
+    }
+}
+
+struct TraitImpl;
+
+impl TraitForcesLargeError for TraitImpl {
+    // Should not lint
+    fn large_error() -> Result<(), [u8; 512]> {
+        Ok(())
+    }
+}
+
+pub union FullyDefinedUnionError {
+    _maybe: u8,
+    _or_even: [[u8; 16]; 32],
+}
+
+pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
+    Ok(())
+}
+
+pub union UnionError<T: Copy> {
+    _maybe: T,
+    _or_perhaps_even: (T, [u8; 512]),
+}
+
+pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
+    Ok(())
+}
+
+pub struct ArrayError<T, U> {
+    _large_array: [T; 32],
+    _other_stuff: U,
+}
+
+pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
+    Ok(())
+}
+
+pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
+    Ok(())
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/result_large_err.stderr b/src/tools/clippy/tests/ui/result_large_err.stderr
new file mode 100644 (file)
index 0000000..0f1f39d
--- /dev/null
@@ -0,0 +1,91 @@
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:7:23
+   |
+LL | pub fn large_err() -> Result<(), [u8; 512]> {
+   |                       ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
+   |
+   = note: `-D clippy::result-large-err` implied by `-D warnings`
+   = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:18:21
+   |
+LL |     pub fn ret() -> Result<(), Self> {
+   |                     ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
+   |
+   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:23:26
+   |
+LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
+   |
+   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:28:45
+   |
+LL | pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
+   |                                             ^^^^^^^ the `Err`-variant is at least 240 bytes
+   |
+   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:36:34
+   |
+LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes
+   |
+   = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:47:34
+   |
+LL |     pub fn large_enum_error() -> Result<(), Self> {
+   |                                  ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes
+   |
+   = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box<LargeErrorVariants<()>>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:53:25
+   |
+LL |     fn large_error() -> Result<(), [u8; 512]> {
+   |                         ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
+   |
+   = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:72:29
+   |
+LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
+   |
+   = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box<FullyDefinedUnionError>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:81:40
+   |
+LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
+   |
+   = help: try reducing the size of `UnionError<T>`, for example by boxing large elements or replacing it with `Box<UnionError<T>>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:90:34
+   |
+LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
+   |
+   = help: try reducing the size of `ArrayError<i32, U>`, for example by boxing large elements or replacing it with `Box<ArrayError<i32, U>>`
+
+error: the `Err`-variant returned from this function is very large
+  --> $DIR/result_large_err.rs:94:31
+   |
+LL | pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
+   |
+   = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box<ArrayError<(i32, T), U>>`
+
+error: aborting due to 11 previous errors
+
index 99964f0de075c04a53467f12fd732e2f97a9506e..af01a8df71b015cff39987006088878f8a4db000 100644 (file)
@@ -151,6 +151,7 @@ impl T for S {}
 
     // Fix #6987
     let mut vec = Vec::new();
+    #[allow(clippy::needless_borrow)]
     for _ in 0..10 {
         vec.push(1);
         vec.extend(&[2]);
index 30fd17c59e518ed931dd82d8ed01c5e64865cf62..16673c01e63017da5769a39ea8085cf9f348dc76 100644 (file)
@@ -7,13 +7,13 @@
 #[allow(clippy::string_add_assign, unused)]
 fn main() {
     // ignores assignment distinction
-    let mut x = "".to_owned();
+    let mut x = String::new();
 
     for _ in 1..3 {
         x = x + ".";
     }
 
-    let y = "".to_owned();
+    let y = String::new();
     let z = y + "...";
 
     assert_eq!(&x, &z);
index db71bab1e5214a96baceeb1a62ee426e3d6173a3..b687f43b2541a016af4cb3abe8a07ce865b5de2c 100644 (file)
@@ -4,13 +4,13 @@
 #[warn(clippy::string_add_assign)]
 fn main() {
     // ignores assignment distinction
-    let mut x = "".to_owned();
+    let mut x = String::new();
 
     for _ in 1..3 {
         x += ".";
     }
 
-    let y = "".to_owned();
+    let y = String::new();
     let z = y + "...";
 
     assert_eq!(&x, &z);
index 644991945cbe2b77da49ca71a1e5e8a8830301a3..e5dbde108fbdbebd46df2e51583b9f18f34603c8 100644 (file)
@@ -4,13 +4,13 @@
 #[warn(clippy::string_add_assign)]
 fn main() {
     // ignores assignment distinction
-    let mut x = "".to_owned();
+    let mut x = String::new();
 
     for _ in 1..3 {
         x = x + ".";
     }
 
-    let y = "".to_owned();
+    let y = String::new();
     let z = y + "...";
 
     assert_eq!(&x, &z);
diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.rs b/src/tools/clippy/tests/ui/suspicious_to_owned.rs
new file mode 100644 (file)
index 0000000..cba21bf
--- /dev/null
@@ -0,0 +1,62 @@
+#![warn(clippy::suspicious_to_owned)]
+#![warn(clippy::implicit_clone)]
+#![allow(clippy::redundant_clone)]
+use std::borrow::Cow;
+use std::ffi::{c_char, CStr};
+
+fn main() {
+    let moo = "Moooo";
+    let c_moo = b"Moooo\0";
+    let c_moo_ptr = c_moo.as_ptr() as *const c_char;
+    let moos = ['M', 'o', 'o'];
+    let moos_vec = moos.to_vec();
+
+    // we expect this to be linted
+    let cow = Cow::Borrowed(moo);
+    let _ = cow.to_owned();
+    // we expect no lints for this
+    let cow = Cow::Borrowed(moo);
+    let _ = cow.into_owned();
+    // we expect no lints for this
+    let cow = Cow::Borrowed(moo);
+    let _ = cow.clone();
+
+    // we expect this to be linted
+    let cow = Cow::Borrowed(&moos);
+    let _ = cow.to_owned();
+    // we expect no lints for this
+    let cow = Cow::Borrowed(&moos);
+    let _ = cow.into_owned();
+    // we expect no lints for this
+    let cow = Cow::Borrowed(&moos);
+    let _ = cow.clone();
+
+    // we expect this to be linted
+    let cow = Cow::Borrowed(&moos_vec);
+    let _ = cow.to_owned();
+    // we expect no lints for this
+    let cow = Cow::Borrowed(&moos_vec);
+    let _ = cow.into_owned();
+    // we expect no lints for this
+    let cow = Cow::Borrowed(&moos_vec);
+    let _ = cow.clone();
+
+    // we expect this to be linted
+    let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
+    let _ = cow.to_owned();
+    // we expect no lints for this
+    let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
+    let _ = cow.into_owned();
+    // we expect no lints for this
+    let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
+    let _ = cow.clone();
+
+    // we expect no lints for these
+    let _ = moo.to_owned();
+    let _ = c_moo.to_owned();
+    let _ = moos.to_owned();
+
+    // we expect implicit_clone lints for these
+    let _ = String::from(moo).to_owned();
+    let _ = moos_vec.to_owned();
+}
diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.stderr b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr
new file mode 100644 (file)
index 0000000..92e1024
--- /dev/null
@@ -0,0 +1,42 @@
+error: this `to_owned` call clones the std::borrow::Cow<str> itself and does not cause the std::borrow::Cow<str> contents to become owned
+  --> $DIR/suspicious_to_owned.rs:16:13
+   |
+LL |     let _ = cow.to_owned();
+   |             ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
+   |
+   = note: `-D clippy::suspicious-to-owned` implied by `-D warnings`
+
+error: this `to_owned` call clones the std::borrow::Cow<[char; 3]> itself and does not cause the std::borrow::Cow<[char; 3]> contents to become owned
+  --> $DIR/suspicious_to_owned.rs:26:13
+   |
+LL |     let _ = cow.to_owned();
+   |             ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
+
+error: this `to_owned` call clones the std::borrow::Cow<std::vec::Vec<char>> itself and does not cause the std::borrow::Cow<std::vec::Vec<char>> contents to become owned
+  --> $DIR/suspicious_to_owned.rs:36:13
+   |
+LL |     let _ = cow.to_owned();
+   |             ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
+
+error: this `to_owned` call clones the std::borrow::Cow<str> itself and does not cause the std::borrow::Cow<str> contents to become owned
+  --> $DIR/suspicious_to_owned.rs:46:13
+   |
+LL |     let _ = cow.to_owned();
+   |             ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
+
+error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type
+  --> $DIR/suspicious_to_owned.rs:60:13
+   |
+LL |     let _ = String::from(moo).to_owned();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::from(moo).clone()`
+   |
+   = note: `-D clippy::implicit-clone` implied by `-D warnings`
+
+error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type
+  --> $DIR/suspicious_to_owned.rs:61:13
+   |
+LL |     let _ = moos_vec.to_owned();
+   |             ^^^^^^^^^^^^^^^^^^^ help: consider using: `moos_vec.clone()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
new file mode 100644 (file)
index 0000000..4ce5d42
--- /dev/null
@@ -0,0 +1,112 @@
+// run-rustfix
+#![deny(clippy::trait_duplication_in_bounds)]
+#![allow(unused)]
+
+fn bad_foo<T: Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+    unimplemented!();
+}
+
+fn bad_bar<T, U>(arg0: T, arg1: U)
+where
+    T: Clone + Copy,
+    U: Clone + Copy,
+{
+    unimplemented!();
+}
+
+fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
+    unimplemented!();
+}
+
+fn good_foo<T, U>(arg0: T, arg1: U)
+where
+    T: Clone + Copy,
+    U: Clone + Copy,
+{
+    unimplemented!();
+}
+
+trait GoodSelfTraitBound: Clone + Copy {
+    fn f();
+}
+
+trait GoodSelfWhereClause {
+    fn f()
+    where
+        Self: Clone + Copy;
+}
+
+trait BadSelfTraitBound: Clone {
+    fn f();
+}
+
+trait BadSelfWhereClause {
+    fn f()
+    where
+        Self: Clone;
+}
+
+trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
+    fn f();
+}
+
+trait GoodWhereClause<T, U> {
+    fn f()
+    where
+        T: Clone + Copy,
+        U: Clone + Copy;
+}
+
+trait BadTraitBound<T: Clone + Copy, U: Clone + Copy> {
+    fn f();
+}
+
+trait BadWhereClause<T, U> {
+    fn f()
+    where
+        T: Clone + Copy,
+        U: Clone + Copy;
+}
+
+struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
+    t: T,
+    u: U,
+}
+
+impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
+    // this should not warn
+    fn f() {}
+}
+
+struct GoodStructWhereClause;
+
+impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
+where
+    T: Clone + Copy,
+    U: Clone + Copy,
+{
+    // this should not warn
+    fn f() {}
+}
+
+fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
+
+trait GenericTrait<T> {}
+
+fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+    unimplemented!();
+}
+
+fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+    unimplemented!();
+}
+
+mod foo {
+    pub trait Clone {}
+}
+
+fn qualified_path<T: std::clone::Clone + foo::Clone>(arg0: T) {
+    unimplemented!();
+}
+
+fn main() {}
index a5751c58aab8f09f1b7c032f281d4281e001e2d5..7f2e96a22e6648eb27aaf1178eec55ff2c86cab5 100644 (file)
+// run-rustfix
 #![deny(clippy::trait_duplication_in_bounds)]
 #![allow(unused)]
 
-use std::collections::BTreeMap;
-use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+    unimplemented!();
+}
 
-fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+fn bad_bar<T, U>(arg0: T, arg1: U)
 where
-    T: Clone,
-    T: Default,
+    T: Clone + Clone + Clone + Copy,
+    U: Clone + Copy,
 {
     unimplemented!();
 }
 
-fn good_bar<T: Clone + Default>(arg: T) {
+fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
     unimplemented!();
 }
 
-fn good_foo<T>(arg: T)
+fn good_foo<T, U>(arg0: T, arg1: U)
 where
-    T: Clone + Default,
+    T: Clone + Copy,
+    U: Clone + Copy,
 {
     unimplemented!();
 }
 
-fn good_foobar<T: Default>(arg: T)
-where
-    T: Clone,
-{
-    unimplemented!();
+trait GoodSelfTraitBound: Clone + Copy {
+    fn f();
 }
 
-trait T: Default {
+trait GoodSelfWhereClause {
     fn f()
     where
-        Self: Default;
+        Self: Clone + Copy;
 }
 
-trait U: Default {
+trait BadSelfTraitBound: Clone + Clone + Clone {
+    fn f();
+}
+
+trait BadSelfWhereClause {
     fn f()
     where
-        Self: Clone;
+        Self: Clone + Clone + Clone;
+}
+
+trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
+    fn f();
 }
 
-trait ZZ: Default {
-    fn g();
-    fn h();
+trait GoodWhereClause<T, U> {
     fn f()
     where
-        Self: Default + Clone;
+        T: Clone + Copy,
+        U: Clone + Copy;
 }
 
-trait BadTrait: Default + Clone {
+trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+    fn f();
+}
+
+trait BadWhereClause<T, U> {
     fn f()
     where
-        Self: Default + Clone;
-    fn g()
-    where
-        Self: Default;
-    fn h()
-    where
-        Self: Copy;
+        T: Clone + Clone + Clone + Copy,
+        U: Clone + Copy;
 }
 
-#[derive(Default, Clone)]
-struct Life;
+struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
+    t: T,
+    u: U,
+}
 
-impl T for Life {
+impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
     // this should not warn
     fn f() {}
 }
 
-impl U for Life {
+struct GoodStructWhereClause;
+
+impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
+where
+    T: Clone + Copy,
+    U: Clone + Copy,
+{
     // this should not warn
     fn f() {}
 }
 
-// should not warn
-trait Iter: Iterator {
-    fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
-    where
-        Self: Iterator<Item = (K, V)> + Sized,
-        K: Ord + Eq,
-    {
-        unimplemented!();
-    }
-}
+fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
 
-struct Foo;
+trait GenericTrait<T> {}
 
-trait FooIter: Iterator<Item = Foo> {
-    fn bar()
-    where
-        Self: Iterator<Item = Foo>,
-    {
-    }
+fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+    unimplemented!();
 }
 
-// This should not lint
-fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
-
-mod repeated_where_clauses_or_trait_bounds {
-    fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
-        unimplemented!();
-    }
-
-    fn bad_bar<T, U>(arg0: T, arg1: U)
-    where
-        T: Clone + Clone + Clone + Copy,
-        U: Clone + Copy,
-    {
-        unimplemented!();
-    }
-
-    fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
-        unimplemented!();
-    }
-
-    fn good_foo<T, U>(arg0: T, arg1: U)
-    where
-        T: Clone + Copy,
-        U: Clone + Copy,
-    {
-        unimplemented!();
-    }
-
-    trait GoodSelfTraitBound: Clone + Copy {
-        fn f();
-    }
-
-    trait GoodSelfWhereClause {
-        fn f()
-        where
-            Self: Clone + Copy;
-    }
-
-    trait BadSelfTraitBound: Clone + Clone + Clone {
-        fn f();
-    }
-
-    trait BadSelfWhereClause {
-        fn f()
-        where
-            Self: Clone + Clone + Clone;
-    }
-
-    trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
-        fn f();
-    }
-
-    trait GoodWhereClause<T, U> {
-        fn f()
-        where
-            T: Clone + Copy,
-            U: Clone + Copy;
-    }
-
-    trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
-        fn f();
-    }
-
-    trait BadWhereClause<T, U> {
-        fn f()
-        where
-            T: Clone + Clone + Clone + Copy,
-            U: Clone + Copy;
-    }
-
-    struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
-        t: T,
-        u: U,
-    }
-
-    impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
-        // this should not warn
-        fn f() {}
-    }
-
-    struct GoodStructWhereClause;
-
-    impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
-    where
-        T: Clone + Copy,
-        U: Clone + Copy,
-    {
-        // this should not warn
-        fn f() {}
-    }
-
-    fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
-
-    trait GenericTrait<T> {}
-
-    // This should not warn but currently does see #8757
-    fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
-        unimplemented!();
-    }
-
-    fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
-        unimplemented!();
-    }
+fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+    unimplemented!();
+}
 
-    mod foo {
-        pub trait Clone {}
-    }
+mod foo {
+    pub trait Clone {}
+}
 
-    fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
-        unimplemented!();
-    }
+fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+    unimplemented!();
 }
 
 fn main() {}
index 7ef04e52708f4c91e16c67c6dddf6cc786779c9b..af800ba78880c39ed5e5b936c850e5138f993c59 100644 (file)
-error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:7:15
+error: these bounds contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:5:15
    |
-LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
-   |               ^^^^^
+LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
    |
 note: the lint level is defined here
-  --> $DIR/trait_duplication_in_bounds.rs:1:9
+  --> $DIR/trait_duplication_in_bounds.rs:2:9
    |
 LL | #![deny(clippy::trait_duplication_in_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:7:23
-   |
-LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
-   |                       ^^^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:36:15
-   |
-LL |         Self: Default;
-   |               ^^^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:50:15
-   |
-LL |         Self: Default + Clone;
-   |               ^^^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:56:15
-   |
-LL |         Self: Default + Clone;
-   |               ^^^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:56:25
-   |
-LL |         Self: Default + Clone;
-   |                         ^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:59:15
-   |
-LL |         Self: Default;
-   |               ^^^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:94:15
-   |
-LL |         Self: Iterator<Item = Foo>,
-   |               ^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:103:19
-   |
-LL |     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
-   |                   ^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: these bounds contain repeated elements
-  --> $DIR/trait_duplication_in_bounds.rs:103:19
-   |
-LL |     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
-
-error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:109:12
-   |
-LL |         T: Clone + Clone + Clone + Copy,
-   |            ^^^^^
-   |
-   = help: consider removing this trait bound
 
 error: these where clauses contain repeated elements
-  --> $DIR/trait_duplication_in_bounds.rs:109:12
+  --> $DIR/trait_duplication_in_bounds.rs:11:8
    |
-LL |         T: Clone + Clone + Clone + Copy,
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+LL |     T: Clone + Clone + Clone + Copy,
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 
 error: these bounds contain repeated elements
-  --> $DIR/trait_duplication_in_bounds.rs:137:30
+  --> $DIR/trait_duplication_in_bounds.rs:39:26
    |
-LL |     trait BadSelfTraitBound: Clone + Clone + Clone {
-   |                              ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
+LL | trait BadSelfTraitBound: Clone + Clone + Clone {
+   |                          ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
 
 error: these where clauses contain repeated elements
-  --> $DIR/trait_duplication_in_bounds.rs:144:19
-   |
-LL |             Self: Clone + Clone + Clone;
-   |                   ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
-
-error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:158:28
+  --> $DIR/trait_duplication_in_bounds.rs:46:15
    |
-LL |     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
-   |                            ^^^^^
-   |
-   = help: consider removing this trait bound
+LL |         Self: Clone + Clone + Clone;
+   |               ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
 
 error: these bounds contain repeated elements
-  --> $DIR/trait_duplication_in_bounds.rs:158:28
+  --> $DIR/trait_duplication_in_bounds.rs:60:24
    |
-LL |     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 
 error: these where clauses contain repeated elements
-  --> $DIR/trait_duplication_in_bounds.rs:165:16
-   |
-LL |             T: Clone + Clone + Clone + Copy,
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
-
-error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:195:24
-   |
-LL |     fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
-   |                        ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider removing this trait bound
-
-error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:199:23
+  --> $DIR/trait_duplication_in_bounds.rs:67:12
    |
-LL |     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
-   |                       ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider removing this trait bound
+LL |         T: Clone + Clone + Clone + Copy,
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 
 error: these bounds contain repeated elements
-  --> $DIR/trait_duplication_in_bounds.rs:199:23
-   |
-LL |     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
-   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
-
-error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:207:26
-   |
-LL |     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
-   |                          ^^^^^^^^^^^^^^^^^
+  --> $DIR/trait_duplication_in_bounds.rs:100:19
    |
-   = help: consider removing this trait bound
+LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u64> + GenericTrait<u32>`
 
 error: these bounds contain repeated elements
-  --> $DIR/trait_duplication_in_bounds.rs:207:26
+  --> $DIR/trait_duplication_in_bounds.rs:108:22
    |
-LL |     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
+LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone`
 
-error: aborting due to 22 previous errors
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.rs
new file mode 100644 (file)
index 0000000..5630a03
--- /dev/null
@@ -0,0 +1,166 @@
+#![deny(clippy::trait_duplication_in_bounds)]
+
+use std::collections::BTreeMap;
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+where
+    T: Clone,
+    T: Default,
+{
+    unimplemented!();
+}
+
+fn good_bar<T: Clone + Default>(arg: T) {
+    unimplemented!();
+}
+
+fn good_foo<T>(arg: T)
+where
+    T: Clone + Default,
+{
+    unimplemented!();
+}
+
+fn good_foobar<T: Default>(arg: T)
+where
+    T: Clone,
+{
+    unimplemented!();
+}
+
+trait T: Default {
+    fn f()
+    where
+        Self: Default;
+}
+
+trait U: Default {
+    fn f()
+    where
+        Self: Clone;
+}
+
+trait ZZ: Default {
+    fn g();
+    fn h();
+    fn f()
+    where
+        Self: Default + Clone;
+}
+
+trait BadTrait: Default + Clone {
+    fn f()
+    where
+        Self: Default + Clone;
+    fn g()
+    where
+        Self: Default;
+    fn h()
+    where
+        Self: Copy;
+}
+
+#[derive(Default, Clone)]
+struct Life;
+
+impl T for Life {
+    // this should not warn
+    fn f() {}
+}
+
+impl U for Life {
+    // this should not warn
+    fn f() {}
+}
+
+// should not warn
+trait Iter: Iterator {
+    fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
+    where
+        Self: Iterator<Item = (K, V)> + Sized,
+        K: Ord + Eq,
+    {
+        unimplemented!();
+    }
+}
+
+struct Foo;
+
+trait FooIter: Iterator<Item = Foo> {
+    fn bar()
+    where
+        Self: Iterator<Item = Foo>,
+    {
+    }
+}
+
+// The below should not lint and exist to guard against false positives
+fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+
+pub mod one {
+    #[derive(Clone, Debug)]
+    struct MultiProductIter<I>
+    where
+        I: Iterator + Clone,
+        I::Item: Clone,
+    {
+        _marker: I,
+    }
+
+    pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
+    where
+        I: Iterator + Clone,
+        I::Item: Clone;
+
+    pub fn multi_cartesian_product<H>(_: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
+    where
+        H: Iterator,
+        H::Item: IntoIterator,
+        <H::Item as IntoIterator>::IntoIter: Clone,
+        <H::Item as IntoIterator>::Item: Clone,
+    {
+        todo!()
+    }
+}
+
+pub mod two {
+    use std::iter::Peekable;
+
+    pub struct MergeBy<I, J, F>
+    where
+        I: Iterator,
+        J: Iterator<Item = I::Item>,
+    {
+        _i: Peekable<I>,
+        _j: Peekable<J>,
+        _f: F,
+    }
+
+    impl<I, J, F> Clone for MergeBy<I, J, F>
+    where
+        I: Iterator,
+        J: Iterator<Item = I::Item>,
+        std::iter::Peekable<I>: Clone,
+        std::iter::Peekable<J>: Clone,
+        F: Clone,
+    {
+        fn clone(&self) -> Self {
+            Self {
+                _i: self._i.clone(),
+                _j: self._j.clone(),
+                _f: self._f.clone(),
+            }
+        }
+    }
+}
+
+pub trait Trait {}
+
+pub fn f(_a: impl Trait, _b: impl Trait) {}
+
+pub trait ImplTrait<T> {}
+
+impl<A, B> ImplTrait<(A, B)> for Foo where Foo: ImplTrait<A> + ImplTrait<B> {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.stderr
new file mode 100644 (file)
index 0000000..fbd9abb
--- /dev/null
@@ -0,0 +1,71 @@
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:6:15
+   |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+   |               ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:1:9
+   |
+LL | #![deny(clippy::trait_duplication_in_bounds)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:6:23
+   |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+   |                       ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:35:15
+   |
+LL |         Self: Default;
+   |               ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:49:15
+   |
+LL |         Self: Default + Clone;
+   |               ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:55:15
+   |
+LL |         Self: Default + Clone;
+   |               ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:55:25
+   |
+LL |         Self: Default + Clone;
+   |                         ^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:58:15
+   |
+LL |         Self: Default;
+   |               ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds_unfixable.rs:93:15
+   |
+LL |         Self: Iterator<Item = Foo>,
+   |               ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: aborting due to 8 previous errors
+
index ebcaa7a84cfb1e9efc1601d6ac0691c4ad89f790..5aad0b44270a3cc5b9e72a5dd1405e9d52fee5e7 100644 (file)
@@ -4,6 +4,7 @@
 use core::any::TypeId;
 use core::ffi::c_void;
 use core::mem::{size_of, transmute, MaybeUninit};
+use core::ptr::NonNull;
 
 fn value<T>() -> T {
     unimplemented!()
@@ -109,6 +110,17 @@ trait Trait {}
         let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok
 
         let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok
+
+        let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<Ty2<u32, u32>, u32>>()); // Ok
+        let _: *const Ty2C<Ty2<u32, u32>, u32> = transmute(value::<*const Ty2<u32, u32>>()); // Ok
+        let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<(), Ty2<u32, u32>>>()); // Ok
+        let _: *const Ty2C<(), Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Ok
+
+        let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<u32, Ty2<u32, u32>>>()); // Err
+        let _: *const Ty2C<u32, Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Err
+
+        let _: NonNull<u8> = transmute(value::<NonNull<(String, String)>>()); // Ok
+        let _: NonNull<(String, String)> = transmute(value::<NonNull<u8>>()); // Ok
     }
 }
 
index 28bfba6c7571dc249ad3c97ae5c31982a4e07f9a..e50a773290e17c60fe6a5bc839e0d009fe821650 100644 (file)
@@ -1,5 +1,5 @@
 error: transmute from `Ty2<u32, i32>` which has an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:27:33
+  --> $DIR/transmute_undefined_repr.rs:28:33
    |
 LL |         let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,13 +7,13 @@ LL |         let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lin
    = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
 
 error: transmute into `Ty2<u32, i32>` which has an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:28:32
+  --> $DIR/transmute_undefined_repr.rs:29:32
    |
 LL |         let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:33:32
+  --> $DIR/transmute_undefined_repr.rs:34:32
    |
 LL |         let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -21,7 +21,7 @@ LL |         let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); //
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:34:36
+  --> $DIR/transmute_undefined_repr.rs:35:36
    |
 LL |         let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -29,7 +29,7 @@ LL |         let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); //
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:39:33
+  --> $DIR/transmute_undefined_repr.rs:40:33
    |
 LL |         let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL |         let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); /
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:40:37
+  --> $DIR/transmute_undefined_repr.rs:41:37
    |
 LL |         let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,7 +45,7 @@ LL |         let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); /
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:57:45
+  --> $DIR/transmute_undefined_repr.rs:58:45
    |
 LL |         let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
    |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -53,15 +53,31 @@ LL |         let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32,
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
 error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:58:37
+  --> $DIR/transmute_undefined_repr.rs:59:37
    |
 LL |         let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: two instances of the same generic type (`Ty2`) may have different layouts
 
+error: transmute into `*const Ty2<u32, u32>` which has an undefined layout
+  --> $DIR/transmute_undefined_repr.rs:119:39
+   |
+LL |         let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<u32, Ty2<u32, u32>>>()); // Err
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the contained type `Ty2<u32, u32>` has an undefined layout
+
+error: transmute from `*const Ty2<u32, u32>` which has an undefined layout
+  --> $DIR/transmute_undefined_repr.rs:120:50
+   |
+LL |         let _: *const Ty2C<u32, Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Err
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the contained type `Ty2<u32, u32>` has an undefined layout
+
 error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:138:35
+  --> $DIR/transmute_undefined_repr.rs:150:35
    |
 LL |         let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -69,12 +85,12 @@ LL |         let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); /
    = note: two instances of the same generic type (`Vec`) may have different layouts
 
 error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout
-  --> $DIR/transmute_undefined_repr.rs:139:35
+  --> $DIR/transmute_undefined_repr.rs:151:35
    |
 LL |         let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: two instances of the same generic type (`Vec`) may have different layouts
 
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
 
index 328cda369e11bb89a59a5db907abb14deefbf91e..94b4723452fada45a142dcd5dc7992e028ae8b60 100644 (file)
@@ -1,4 +1,7 @@
 // run-rustfix
+// compile-flags: --test
+#![allow(dead_code)]
+
 #[warn(clippy::invisible_characters)]
 fn zero() {
     print!("Here >\u{200B}< is a ZWS, and \u{200B}another");
@@ -15,22 +18,43 @@ fn canon() {
     print!("a\u{0300}h?"); // also ok
 }
 
-#[warn(clippy::non_ascii_literal)]
-fn uni() {
-    print!("\u{dc}ben!");
-    print!("\u{DC}ben!"); // this is ok
-}
+mod non_ascii_literal {
+    #![deny(clippy::non_ascii_literal)]
+
+    fn uni() {
+        print!("\u{dc}ben!");
+        print!("\u{DC}ben!"); // this is ok
+    }
+
+    // issue 8013
+    fn single_quote() {
+        const _EMPTY_BLOCK: char = '\u{25b1}';
+        const _FULL_BLOCK: char = '\u{25b0}';
+    }
+
+    #[test]
+    pub fn issue_7739() {
+        // Ryū crate: https://github.com/dtolnay/ryu
+    }
+
+    mod issue_8263 {
+        #![deny(clippy::non_ascii_literal)]
+
+        // Re-allow for a single test
+        #[test]
+        #[allow(clippy::non_ascii_literal)]
+        fn allowed() {
+            let _ = "悲しいかな、ここに日本語を書くことはできない。";
+        }
 
-// issue 8013
-#[warn(clippy::non_ascii_literal)]
-fn single_quote() {
-    const _EMPTY_BLOCK: char = '\u{25b1}';
-    const _FULL_BLOCK: char = '\u{25b0}';
+        #[test]
+        fn denied() {
+            let _ = "\u{60b2}\u{3057}\u{3044}\u{304b}\u{306a}\u{3001}\u{3053}\u{3053}\u{306b}\u{65e5}\u{672c}\u{8a9e}\u{3092}\u{66f8}\u{304f}\u{3053}\u{3068}\u{306f}\u{3067}\u{304d}\u{306a}\u{3044}\u{3002}";
+        }
+    }
 }
 
 fn main() {
     zero();
-    uni();
     canon();
-    single_quote();
 }
index 7828d6bcbea7ad39e4a68c82ef69b9c9ede16015..6ad0b255b94856c96943f0af5d329db3f7d46f8e 100644 (file)
@@ -1,4 +1,7 @@
 // run-rustfix
+// compile-flags: --test
+#![allow(dead_code)]
+
 #[warn(clippy::invisible_characters)]
 fn zero() {
     print!("Here >​< is a ZWS, and ​another");
@@ -15,22 +18,43 @@ fn canon() {
     print!("a\u{0300}h?"); // also ok
 }
 
-#[warn(clippy::non_ascii_literal)]
-fn uni() {
-    print!("Üben!");
-    print!("\u{DC}ben!"); // this is ok
-}
+mod non_ascii_literal {
+    #![deny(clippy::non_ascii_literal)]
+
+    fn uni() {
+        print!("Üben!");
+        print!("\u{DC}ben!"); // this is ok
+    }
+
+    // issue 8013
+    fn single_quote() {
+        const _EMPTY_BLOCK: char = '▱';
+        const _FULL_BLOCK: char = '▰';
+    }
+
+    #[test]
+    pub fn issue_7739() {
+        // Ryū crate: https://github.com/dtolnay/ryu
+    }
+
+    mod issue_8263 {
+        #![deny(clippy::non_ascii_literal)]
+
+        // Re-allow for a single test
+        #[test]
+        #[allow(clippy::non_ascii_literal)]
+        fn allowed() {
+            let _ = "悲しいかな、ここに日本語を書くことはできない。";
+        }
 
-// issue 8013
-#[warn(clippy::non_ascii_literal)]
-fn single_quote() {
-    const _EMPTY_BLOCK: char = '▱';
-    const _FULL_BLOCK: char = '▰';
+        #[test]
+        fn denied() {
+            let _ = "悲しいかな、ここに日本語を書くことはできない。";
+        }
+    }
 }
 
 fn main() {
     zero();
-    uni();
     canon();
-    single_quote();
 }
index 01d3f3c0296799cc15b4cabcbc811b786e8da427..ea74a81451e3a9c3157bd99d3819c1690d602266 100644 (file)
@@ -1,5 +1,5 @@
 error: invisible character detected
-  --> $DIR/unicode.rs:4:12
+  --> $DIR/unicode.rs:7:12
    |
 LL |     print!("Here >​< is a ZWS, and ​another");
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
@@ -7,19 +7,19 @@ LL |     print!("Here >​< is a ZWS, and ​another");
    = note: `-D clippy::invisible-characters` implied by `-D warnings`
 
 error: invisible character detected
-  --> $DIR/unicode.rs:6:12
+  --> $DIR/unicode.rs:9:12
    |
 LL |     print!("Here >­< is a SHY, and ­another");
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"`
 
 error: invisible character detected
-  --> $DIR/unicode.rs:8:12
+  --> $DIR/unicode.rs:11:12
    |
 LL |     print!("Here >⁠< is a WJ, and ⁠another");
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"`
 
 error: non-NFC Unicode sequence detected
-  --> $DIR/unicode.rs:14:12
+  --> $DIR/unicode.rs:17:12
    |
 LL |     print!("̀àh?");
    |            ^^^^^ help: consider replacing the string with: `"̀àh?"`
@@ -27,24 +27,40 @@ LL |     print!("̀àh?");
    = note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
 
 error: literal non-ASCII character detected
-  --> $DIR/unicode.rs:20:12
+  --> $DIR/unicode.rs:25:16
    |
-LL |     print!("Üben!");
-   |            ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
+LL |         print!("Üben!");
+   |                ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
    |
-   = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
+note: the lint level is defined here
+  --> $DIR/unicode.rs:22:13
+   |
+LL |     #![deny(clippy::non_ascii_literal)]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: literal non-ASCII character detected
+  --> $DIR/unicode.rs:31:36
+   |
+LL |         const _EMPTY_BLOCK: char = '▱';
+   |                                    ^^^ help: consider replacing the string with: `'/u{25b1}'`
 
 error: literal non-ASCII character detected
-  --> $DIR/unicode.rs:27:32
+  --> $DIR/unicode.rs:32:35
    |
-LL |     const _EMPTY_BLOCK: char = '▱';
-   |                                ^^^ help: consider replacing the string with: `'/u{25b1}'`
+LL |         const _FULL_BLOCK: char = '▰';
+   |                                   ^^^ help: consider replacing the string with: `'/u{25b0}'`
 
 error: literal non-ASCII character detected
-  --> $DIR/unicode.rs:28:31
+  --> $DIR/unicode.rs:52:21
+   |
+LL |             let _ = "悲しいかな、ここに日本語を書くことはできない。";
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"/u{60b2}/u{3057}/u{3044}/u{304b}/u{306a}/u{3001}/u{3053}/u{3053}/u{306b}/u{65e5}/u{672c}/u{8a9e}/u{3092}/u{66f8}/u{304f}/u{3053}/u{3068}/u{306f}/u{3067}/u{304d}/u{306a}/u{3044}/u{3002}"`
+   |
+note: the lint level is defined here
+  --> $DIR/unicode.rs:41:17
    |
-LL |     const _FULL_BLOCK: char = '▰';
-   |                               ^^^ help: consider replacing the string with: `'/u{25b0}'`
+LL |         #![deny(clippy::non_ascii_literal)]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
 
index b352b285c86267f42fed1f4be2ce85d51c544afd..ee9f157342d48f835a39883ffa3736e9f9a30885 100644 (file)
@@ -88,4 +88,13 @@ mod fixable {
     }
 
     type I32Alias = i32;
+
+    fn issue_9380() {
+        let _: i32 = -1_i32;
+        let _: f32 = -(1) as f32;
+        let _: i64 = -1_i64;
+        let _: i64 = -(1.0) as i64;
+
+        let _ = -(1 + 1) as i64;
+    }
 }
index 6c8cc3effe8fefcc9ca0cf5c806549bd0037b0f2..5b70412424c06f602e02d3ecf02025f5f7b59dc7 100644 (file)
@@ -88,4 +88,13 @@ fn main() {
     }
 
     type I32Alias = i32;
+
+    fn issue_9380() {
+        let _: i32 = -(1) as i32;
+        let _: f32 = -(1) as f32;
+        let _: i64 = -(1) as i64;
+        let _: i64 = -(1.0) as i64;
+
+        let _ = -(1 + 1) as i64;
+    }
 }
index bad45f0025b2292fe0360c52ec59effefac4c361..f7829ff3b0efd049d72f7619bcdeb6e7708b1d8d 100644 (file)
@@ -150,5 +150,17 @@ error: casting float literal to `f32` is unnecessary
 LL |         let _ = -1.0 as f32;
    |                 ^^^^^^^^^^^ help: try: `-1.0_f32`
 
-error: aborting due to 25 previous errors
+error: casting integer literal to `i32` is unnecessary
+  --> $DIR/unnecessary_cast.rs:93:22
+   |
+LL |         let _: i32 = -(1) as i32;
+   |                      ^^^^^^^^^^^ help: try: `-1_i32`
+
+error: casting integer literal to `i64` is unnecessary
+  --> $DIR/unnecessary_cast.rs:95:22
+   |
+LL |         let _: i64 = -(1) as i64;
+   |                      ^^^^^^^^^^^ help: try: `-1_i64`
+
+error: aborting due to 27 previous errors
 
index f95f91329a2faeeae22da74496d42568e89512c8..40052c41039e5d01c320f34743fe48331fc9e81a 100644 (file)
@@ -12,6 +12,7 @@ fn main() {
     ref_str_argument("");
 
     // should be linted
+    #[allow(clippy::manual_string_new)]
     ref_str_argument("");
 
     // should not be linted
index 0cbdc151ed9b1b0b3a2056ed6e4e78665f175ee0..2304dff5192b9ee66c34805feab80ab2d55bd62c 100644 (file)
@@ -12,6 +12,7 @@ fn main() {
     ref_str_argument(&String::new());
 
     // should be linted
+    #[allow(clippy::manual_string_new)]
     ref_str_argument(&String::from(""));
 
     // should not be linted
index 46bc4597b335f0bfb7be4ce8053f46b3735e95b4..1eb198a8675ea1b2f172667f6f8807d2f624d0fa 100644 (file)
@@ -7,7 +7,7 @@ LL |     ref_str_argument(&String::new());
    = 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
+  --> $DIR/unnecessary_owned_empty_strings.rs:16:22
    |
 LL |     ref_str_argument(&String::from(""));
    |                      ^^^^^^^^^^^^^^^^^ help: try: `""`
index f4f76cd3dd493e159a656fe95276b2a41002299b..9cd5bc73b1ec51852af3c2bbeae84cb5ddf05594 100644 (file)
@@ -329,3 +329,31 @@ mod issue_8759_variant {
         rw.set_view(&rw.default_view().to_owned());
     }
 }
+
+mod issue_9317 {
+    #![allow(dead_code)]
+
+    struct Bytes {}
+
+    impl ToString for Bytes {
+        fn to_string(&self) -> String {
+            "123".to_string()
+        }
+    }
+
+    impl AsRef<[u8]> for Bytes {
+        fn as_ref(&self) -> &[u8] {
+            &[1, 2, 3]
+        }
+    }
+
+    fn consume<C: AsRef<[u8]>>(c: C) {
+        let _ = c;
+    }
+
+    pub fn main() {
+        let b = Bytes {};
+        // Should not lint.
+        consume(b.to_string());
+    }
+}
index fe09a489ab0a67b81cf5280f3dc8b9c1140aa061..7f62ba3ab5d559ea0d8a60da23f977389044f054 100644 (file)
@@ -329,3 +329,31 @@ fn main() {
         rw.set_view(&rw.default_view().to_owned());
     }
 }
+
+mod issue_9317 {
+    #![allow(dead_code)]
+
+    struct Bytes {}
+
+    impl ToString for Bytes {
+        fn to_string(&self) -> String {
+            "123".to_string()
+        }
+    }
+
+    impl AsRef<[u8]> for Bytes {
+        fn as_ref(&self) -> &[u8] {
+            &[1, 2, 3]
+        }
+    }
+
+    fn consume<C: AsRef<[u8]>>(c: C) {
+        let _ = c;
+    }
+
+    pub fn main() {
+        let b = Bytes {};
+        // Should not lint.
+        consume(b.to_string());
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unused_peekable.rs b/src/tools/clippy/tests/ui/unused_peekable.rs
new file mode 100644 (file)
index 0000000..153457e
--- /dev/null
@@ -0,0 +1,144 @@
+#![warn(clippy::unused_peekable)]
+#![allow(clippy::no_effect)]
+
+use std::iter::Empty;
+use std::iter::Peekable;
+
+fn main() {
+    invalid();
+    valid();
+}
+
+#[allow(clippy::unused_unit)]
+fn invalid() {
+    let peekable = std::iter::empty::<u32>().peekable();
+
+    // Only lint `new_local`
+    let old_local = std::iter::empty::<u32>().peekable();
+    let new_local = old_local;
+
+    // Behind mut ref
+    let mut by_mut_ref_test = std::iter::empty::<u32>().peekable();
+    let by_mut_ref = &mut by_mut_ref_test;
+
+    // Explicitly returns `Peekable`
+    fn returns_peekable() -> Peekable<Empty<u32>> {
+        std::iter::empty().peekable()
+    }
+
+    let peekable_from_fn = returns_peekable();
+
+    // Using a method not exclusive to `Peekable`
+    let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable();
+    peekable_using_iterator_method.next();
+
+    // Passed by ref to another function
+    fn takes_ref(_peek: &Peekable<Empty<u32>>) {}
+    let passed_along_ref = std::iter::empty::<u32>().peekable();
+    takes_ref(&passed_along_ref);
+
+    // `by_ref` without `peek`
+    let mut by_ref_test = std::iter::empty::<u32>().peekable();
+    let _by_ref = by_ref_test.by_ref();
+
+    let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable();
+    for x in peekable_in_for_loop {}
+}
+
+fn valid() {
+    fn takes_peekable(_peek: Peekable<Empty<u32>>) {}
+
+    // Passed to another function
+    let passed_along = std::iter::empty::<u32>().peekable();
+    takes_peekable(passed_along);
+
+    // Passed to another method
+    struct PeekableConsumer;
+    impl PeekableConsumer {
+        fn consume(&self, _: Peekable<Empty<u32>>) {}
+        fn consume_mut_ref(&self, _: &mut Peekable<Empty<u32>>) {}
+    }
+
+    let peekable_consumer = PeekableConsumer;
+    let mut passed_along_to_method = std::iter::empty::<u32>().peekable();
+    peekable_consumer.consume_mut_ref(&mut passed_along_to_method);
+    peekable_consumer.consume(passed_along_to_method);
+
+    // `peek` called in another block
+    let mut peekable_in_block = std::iter::empty::<u32>().peekable();
+    {
+        peekable_in_block.peek();
+    }
+
+    // Check the other `Peekable` methods :)
+    {
+        let mut peekable_with_peek_mut = std::iter::empty::<u32>().peekable();
+        peekable_with_peek_mut.peek_mut();
+
+        let mut peekable_with_next_if = std::iter::empty::<u32>().peekable();
+        peekable_with_next_if.next_if(|_| true);
+
+        let mut peekable_with_next_if_eq = std::iter::empty::<u32>().peekable();
+        peekable_with_next_if_eq.next_if_eq(&3);
+    }
+
+    let mut peekable_in_closure = std::iter::empty::<u32>().peekable();
+    let call_peek = |p: &mut Peekable<Empty<u32>>| {
+        p.peek();
+    };
+    call_peek(&mut peekable_in_closure);
+
+    // From a macro
+    macro_rules! make_me_a_peekable_please {
+        () => {
+            std::iter::empty::<u32>().peekable()
+        };
+    }
+
+    let _unsuspecting_macro_user = make_me_a_peekable_please!();
+
+    // Generic Iterator returned
+    fn return_an_iter() -> impl Iterator<Item = u32> {
+        std::iter::empty::<u32>().peekable()
+    }
+
+    let _unsuspecting_user = return_an_iter();
+
+    // Call `peek` in a macro
+    macro_rules! peek_iter {
+        ($iter:ident) => {
+            $iter.peek();
+        };
+    }
+
+    let mut peek_in_macro = std::iter::empty::<u32>().peekable();
+    peek_iter!(peek_in_macro);
+
+    // Behind mut ref
+    let mut by_mut_ref_test = std::iter::empty::<u32>().peekable();
+    let by_mut_ref = &mut by_mut_ref_test;
+    by_mut_ref.peek();
+
+    // Behind ref
+    let mut by_ref_test = std::iter::empty::<u32>().peekable();
+    let by_ref = &by_ref_test;
+    by_ref_test.peek();
+
+    // In struct
+    struct PeekableWrapper {
+        f: Peekable<Empty<u32>>,
+    }
+
+    let struct_test = std::iter::empty::<u32>().peekable();
+    PeekableWrapper { f: struct_test };
+
+    // `by_ref` before `peek`
+    let mut by_ref_test = std::iter::empty::<u32>().peekable();
+    let peeked_val = by_ref_test.by_ref().peek();
+
+    // `peek` called in another block as the last expression
+    let mut peekable_last_expr = std::iter::empty::<u32>().peekable();
+    {
+        peekable_last_expr.peek();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unused_peekable.stderr b/src/tools/clippy/tests/ui/unused_peekable.stderr
new file mode 100644 (file)
index 0000000..d557f54
--- /dev/null
@@ -0,0 +1,67 @@
+error: `peek` never called on `Peekable` iterator
+  --> $DIR/unused_peekable.rs:14:9
+   |
+LL |     let peekable = std::iter::empty::<u32>().peekable();
+   |         ^^^^^^^^
+   |
+   = note: `-D clippy::unused-peekable` implied by `-D warnings`
+   = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+  --> $DIR/unused_peekable.rs:18:9
+   |
+LL |     let new_local = old_local;
+   |         ^^^^^^^^^
+   |
+   = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+  --> $DIR/unused_peekable.rs:22:9
+   |
+LL |     let by_mut_ref = &mut by_mut_ref_test;
+   |         ^^^^^^^^^^
+   |
+   = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+  --> $DIR/unused_peekable.rs:29:9
+   |
+LL |     let peekable_from_fn = returns_peekable();
+   |         ^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+  --> $DIR/unused_peekable.rs:32:13
+   |
+LL |     let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+  --> $DIR/unused_peekable.rs:37:9
+   |
+LL |     let passed_along_ref = std::iter::empty::<u32>().peekable();
+   |         ^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+  --> $DIR/unused_peekable.rs:42:9
+   |
+LL |     let _by_ref = by_ref_test.by_ref();
+   |         ^^^^^^^
+   |
+   = help: consider removing the call to `peekable`
+
+error: `peek` never called on `Peekable` iterator
+  --> $DIR/unused_peekable.rs:44:13
+   |
+LL |     let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable();
+   |             ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing the call to `peekable`
+
+error: aborting due to 8 previous errors
+
index a4a3cd1d37977d3dcb16181db4ad8c20a54ffaab..d9fd402e7cfb9d200cc79f794e9b4b41c9c94c82 100644 (file)
@@ -6,8 +6,9 @@ fn unwrap_option() {
 }
 
 fn unwrap_result() {
-    let res: Result<u8, ()> = Ok(0);
+    let res: Result<u8, u8> = Ok(0);
     let _ = res.unwrap();
+    let _ = res.unwrap_err();
 }
 
 fn main() {
index 4f0858005f6e7f140cd0c69bcea3c9111e82b673..78422757819d502d3ba231fdc6b803e2a5407205 100644 (file)
@@ -15,5 +15,13 @@ LL |     let _ = res.unwrap();
    |
    = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
 
-error: aborting due to 2 previous errors
+error: used `unwrap_err()` on `a Result` value
+  --> $DIR/unwrap.rs:11:13
+   |
+LL |     let _ = res.unwrap_err();
+   |             ^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `Ok` case gracefully, consider using `expect_err()` to provide a better panic message
+
+error: aborting due to 3 previous errors
 
index 0d4a0504a6e04142ea69b6940c01833b7d9f7bbb..9f27fef82494b8207437530017889d3bd523455d 100644 (file)
@@ -1,10 +1,35 @@
 #![warn(clippy::unwrap_used, clippy::expect_used)]
 
+trait OptionExt {
+    type Item;
+
+    fn unwrap_err(self) -> Self::Item;
+
+    fn expect_err(self, msg: &str) -> Self::Item;
+}
+
+impl<T> OptionExt for Option<T> {
+    type Item = T;
+    fn unwrap_err(self) -> T {
+        panic!();
+    }
+
+    fn expect_err(self, msg: &str) -> T {
+        panic!();
+    }
+}
+
 fn main() {
     Some(3).unwrap();
     Some(3).expect("Hello world!");
 
+    // Don't trigger on unwrap_err on an option
+    Some(3).unwrap_err();
+    Some(3).expect_err("Hellow none!");
+
     let a: Result<i32, i32> = Ok(3);
     a.unwrap();
     a.expect("Hello world!");
+    a.unwrap_err();
+    a.expect_err("Hello error!");
 }
index f54bfd617c4ee5589b1c906e4ab066c99fb7981e..1a19459b2c174c0ca8dfaee96d6148effd554094 100644 (file)
@@ -1,5 +1,5 @@
 error: used `unwrap()` on `an Option` value
-  --> $DIR/unwrap_expect_used.rs:4:5
+  --> $DIR/unwrap_expect_used.rs:23:5
    |
 LL |     Some(3).unwrap();
    |     ^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     Some(3).unwrap();
    = help: if this value is `None`, it will panic
 
 error: used `expect()` on `an Option` value
-  --> $DIR/unwrap_expect_used.rs:5:5
+  --> $DIR/unwrap_expect_used.rs:24:5
    |
 LL |     Some(3).expect("Hello world!");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL |     Some(3).expect("Hello world!");
    = help: if this value is `None`, it will panic
 
 error: used `unwrap()` on `a Result` value
-  --> $DIR/unwrap_expect_used.rs:8:5
+  --> $DIR/unwrap_expect_used.rs:31:5
    |
 LL |     a.unwrap();
    |     ^^^^^^^^^^
@@ -25,12 +25,28 @@ LL |     a.unwrap();
    = help: if this value is an `Err`, it will panic
 
 error: used `expect()` on `a Result` value
-  --> $DIR/unwrap_expect_used.rs:9:5
+  --> $DIR/unwrap_expect_used.rs:32:5
    |
 LL |     a.expect("Hello world!");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: if this value is an `Err`, it will panic
 
-error: aborting due to 4 previous errors
+error: used `unwrap_err()` on `a Result` value
+  --> $DIR/unwrap_expect_used.rs:33:5
+   |
+LL |     a.unwrap_err();
+   |     ^^^^^^^^^^^^^^
+   |
+   = help: if this value is an `Ok`, it will panic
+
+error: used `expect_err()` on `a Result` value
+  --> $DIR/unwrap_expect_used.rs:34:5
+   |
+LL |     a.expect_err("Hello error!");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if this value is an `Ok`, it will panic
+
+error: aborting due to 6 previous errors
 
index 39f54c27bee1a6356e40ad4628a9b6f10e086e14..4acf5b5fa2d1ba8ab717631f61210aa301ff90c5 100644 (file)
@@ -29,10 +29,10 @@ fn main() {
     let _ = String::try_from("foo".to_string()).unwrap();
     let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
     let _: String = format!("Hello {}", "world").try_into().unwrap();
-    let _: String = "".to_owned().try_into().unwrap();
+    let _: String = String::new().try_into().unwrap();
     let _: String = match String::from("_").try_into() {
         Ok(a) => a,
-        Err(_) => "".into(),
+        Err(_) => String::new(),
     };
     // FIXME this is a false negative
     #[allow(clippy::cmp_owned)]
index b691c13f7dbb747ac540e2258a071b53419a186e..12e74d614717db6ee5c6d8068f894958d53e7dad 100644 (file)
@@ -62,7 +62,7 @@ LL |     let _: String = format!("Hello {}", "world").try_into().unwrap();
 error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion_try.rs:32:21
    |
-LL |     let _: String = "".to_owned().try_into().unwrap();
+LL |     let _: String = String::new().try_into().unwrap();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider removing `.try_into()`
index 7ed27439ec6e4432372224dd696d485024918b96..a8307e741cf17b0c8ff64fd53cf2096b22c7f068 100644 (file)
@@ -1,15 +1,19 @@
 #![warn(clippy::vec_resize_to_zero)]
 
 fn main() {
+    let mut v = vec![1, 2, 3, 4, 5];
+
     // applicable here
-    vec![1, 2, 3, 4, 5].resize(0, 5);
+    v.resize(0, 5);
 
     // not applicable
-    vec![1, 2, 3, 4, 5].resize(2, 5);
+    v.resize(2, 5);
+
+    let mut v = vec!["foo", "bar", "baz"];
 
     // applicable here, but only implemented for integer literals for now
-    vec!["foo", "bar", "baz"].resize(0, "bar");
+    v.resize(0, "bar");
 
     // not applicable
-    vec!["foo", "bar", "baz"].resize(2, "bar")
+    v.resize(2, "bar")
 }
index feb846298c656878246193fd8968b2c67127b260..7428cf62d6c429554d8a54e37cc641dfb93f8536 100644 (file)
@@ -1,10 +1,10 @@
 error: emptying a vector with `resize`
-  --> $DIR/vec_resize_to_zero.rs:5:5
+  --> $DIR/vec_resize_to_zero.rs:7:5
    |
-LL |     vec![1, 2, 3, 4, 5].resize(0, 5);
-   |     ^^^^^^^^^^^^^^^^^^^^------------
-   |                         |
-   |                         help: ...or you can empty the vector with: `clear()`
+LL |     v.resize(0, 5);
+   |     ^^------------
+   |       |
+   |       help: ...or you can empty the vector with: `clear()`
    |
    = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings`
    = help: the arguments may be inverted...
index e0065e05ade62d31ba9c21f7fe8b75f2b368d973..df267e9872a0a0a6bdd6984c7aeca3e77c4e4d88 100644 (file)
@@ -18,7 +18,7 @@ fn main() -> std::io::Result<()> {
     s.read_to_end();
     s.read_to_string();
     // Should catch this
-    let mut f = File::open(&path)?;
+    let mut f = File::open(path)?;
     let mut buffer = Vec::new();
     f.read_to_end(&mut buffer)?;
     // ...and this
index e13efb3e0164b638f96b05861b818fdc70b23ed0..95325e06037829c2fb9a519f5b3a50e10ddee1e1 100644 (file)
@@ -20,8 +20,8 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
         .current_dir(&cwd)
         .env("CARGO_TARGET_DIR", &target_dir)
         .arg("clean")
-        .args(&["-p", "subcrate"])
-        .args(&["-p", "path_dep"])
+        .args(["-p", "subcrate"])
+        .args(["-p", "path_dep"])
         .output()
         .unwrap();
 
@@ -32,11 +32,11 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
         .env("CARGO_INCREMENTAL", "0")
         .env("CARGO_TARGET_DIR", &target_dir)
         .arg("clippy")
-        .args(&["-p", "subcrate"])
+        .args(["-p", "subcrate"])
         .arg("--no-deps")
         .arg("--")
         .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
-        .args(&["--cfg", r#"feature="primary_package_test""#])
+        .args(["--cfg", r#"feature="primary_package_test""#])
         .output()
         .unwrap();
     println!("status: {}", output.status);
@@ -52,10 +52,10 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
             .env("CARGO_INCREMENTAL", "0")
             .env("CARGO_TARGET_DIR", &target_dir)
             .arg("clippy")
-            .args(&["-p", "subcrate"])
+            .args(["-p", "subcrate"])
             .arg("--")
             .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
-            .args(&["--cfg", r#"feature="primary_package_test""#])
+            .args(["--cfg", r#"feature="primary_package_test""#])
             .output()
             .unwrap();
         println!("status: {}", output.status);
@@ -79,7 +79,7 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
             .env("CARGO_INCREMENTAL", "0")
             .env("CARGO_TARGET_DIR", &target_dir)
             .arg("clippy")
-            .args(&["-p", "subcrate"])
+            .args(["-p", "subcrate"])
             .arg("--")
             .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
             .output()
index 52eb7ce17d6aeb482d41846a7781b0601ff8131a..e4e43e97dde82eee29821dfb138cc75d14f7f297 100644 (file)
@@ -37,23 +37,6 @@ pub struct SourceFile {
 type Level = super::proc_macro::Level;
 type LineColumn = super::proc_macro::LineColumn;
 
-/// A structure representing a diagnostic message and associated children
-/// messages.
-#[derive(Clone, Debug)]
-pub struct Diagnostic {
-    level: Level,
-    message: String,
-    spans: Vec<Span>,
-    children: Vec<Diagnostic>,
-}
-
-impl Diagnostic {
-    /// Creates a new diagnostic with the given `level` and `message`.
-    pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
-        Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
-    }
-}
-
 pub struct FreeFunctions;
 
 #[derive(Default)]
@@ -65,8 +48,6 @@ impl server::Types for RustAnalyzer {
     type FreeFunctions = FreeFunctions;
     type TokenStream = TokenStream;
     type SourceFile = SourceFile;
-    type MultiSpan = Vec<Span>;
-    type Diagnostic = Diagnostic;
     type Span = Span;
     type Symbol = Symbol;
 }
@@ -90,6 +71,10 @@ fn literal_from_str(
             span: tt::TokenId::unspecified(),
         })
     }
+
+    fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {
+        // FIXME handle diagnostic
+    }
 }
 
 impl server::TokenStream for RustAnalyzer {
@@ -282,30 +267,6 @@ fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
     }
 }
 
-impl server::Diagnostic for RustAnalyzer {
-    fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
-        let mut diag = Diagnostic::new(level, msg);
-        diag.spans = spans;
-        diag
-    }
-
-    fn sub(
-        &mut self,
-        _diag: &mut Self::Diagnostic,
-        _level: Level,
-        _msg: &str,
-        _spans: Self::MultiSpan,
-    ) {
-        // FIXME handle diagnostic
-        //
-    }
-
-    fn emit(&mut self, _diag: Self::Diagnostic) {
-        // FIXME handle diagnostic
-        // diag.emit()
-    }
-}
-
 impl server::Span for RustAnalyzer {
     fn debug(&mut self, span: Self::Span) -> String {
         format!("{:?}", span.0)
@@ -372,18 +333,6 @@ fn before(&mut self, _self_: Self::Span) -> Self::Span {
     }
 }
 
-impl server::MultiSpan for RustAnalyzer {
-    fn new(&mut self) -> Self::MultiSpan {
-        // FIXME handle span
-        vec![]
-    }
-
-    fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) {
-        //TODP
-        other.push(span)
-    }
-}
-
 impl server::Symbol for RustAnalyzer {
     fn normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> {
         // FIXME: nfc-normalize and validate idents
index 2fe17e979c87fc004bbdb9367da7376eb1307146..05e1eb2bfb00695a6f981f917b20eeaa83880329 100644 (file)
     "time",
     "tinystr",
     "tinyvec",
+    "thin-vec",
     "tracing",
     "tracing-attributes",
     "tracing-core",