]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #75892 - ArekPiekarz:unstable_book_tls_model_typo, r=petrochenkov
authorDylan DPC <dylan.dpc@gmail.com>
Sat, 29 Aug 2020 23:43:46 +0000 (01:43 +0200)
committerGitHub <noreply@github.com>
Sat, 29 Aug 2020 23:43:46 +0000 (01:43 +0200)
Fix typo in TLS Model in Unstable Book

459 files changed:
.github/workflows/ci.yml
Cargo.lock
README.md
RELEASES.md
config.toml.example
library/alloc/src/alloc.rs
library/alloc/src/raw_vec.rs
library/alloc/src/vec.rs
library/core/src/alloc/mod.rs
library/core/src/array/mod.rs
library/core/src/convert/mod.rs
library/core/src/future/future.rs
library/core/src/future/mod.rs
library/core/src/hash/mod.rs
library/core/src/hint.rs
library/core/src/iter/traits/collect.rs
library/core/src/iter/traits/iterator.rs
library/core/src/lib.rs
library/core/src/macros/mod.rs
library/core/src/macros/panic.md
library/core/src/marker.rs
library/core/src/mem/manually_drop.rs
library/core/src/mem/mod.rs
library/core/src/num/dec2flt/mod.rs
library/core/src/ops/range.rs
library/core/src/ops/try.rs
library/core/src/option.rs
library/core/src/pin.rs
library/core/src/ptr/const_ptr.rs
library/core/src/ptr/mut_ptr.rs
library/core/src/result.rs
library/core/src/task/poll.rs
library/core/tests/lib.rs
library/panic_abort/src/lib.rs
library/panic_unwind/src/dwarf/eh.rs
library/panic_unwind/src/emcc.rs
library/panic_unwind/src/gcc.rs
library/panic_unwind/src/lib.rs
library/panic_unwind/src/seh.rs
library/profiler_builtins/build.rs
library/std/Cargo.toml
library/std/build.rs
library/std/src/alloc.rs
library/std/src/error.rs
library/std/src/io/buffered.rs
library/std/src/lazy.rs
library/std/src/lib.rs
library/std/src/net/addr.rs
library/std/src/net/ip.rs
library/std/src/os/linux/raw.rs
library/std/src/panic.rs
library/std/src/panicking.rs
library/std/src/primitive_docs.rs
library/std/src/sys/vxworks/fd.rs
library/std/src/sys/vxworks/process/process_common.rs
library/std/src/sys/vxworks/thread_local_dtor.rs
library/std/src/sys/wasi/ext/fs.rs
library/std/src/sys/windows/mod.rs
library/std/src/sys_common/net.rs
library/std/src/sys_common/os_str_bytes.rs
library/std/src/sys_common/poison.rs
library/std/src/time.rs
src/bootstrap/builder/tests.rs
src/bootstrap/cc_detect.rs
src/bootstrap/channel.rs
src/bootstrap/config.rs
src/bootstrap/dist.rs
src/bootstrap/lib.rs
src/bootstrap/native.rs
src/bootstrap/sanity.rs
src/bootstrap/test.rs
src/ci/azure-pipelines/auto.yml
src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile
src/ci/docker/host-x86_64/armhf-gnu/Dockerfile
src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile
src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile
src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile
src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile
src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile
src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile
src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile
src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile
src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile
src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile
src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile
src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile
src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile
src/ci/docker/host-x86_64/dist-various-1/Dockerfile
src/ci/docker/host-x86_64/dist-various-2/build-cloudabi-toolchain.sh
src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile
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/mingw-check/Dockerfile
src/ci/docker/host-x86_64/test-various/Dockerfile
src/ci/docker/host-x86_64/wasm32/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile
src/ci/docker/scripts/android-base-apt-get.sh
src/ci/docker/scripts/cross-apt-packages.sh
src/ci/github-actions/ci.yml
src/ci/scripts/install-clang.sh
src/doc/rustc/src/platform-support.md
src/librustc_ast_lowering/lib.rs
src/librustc_codegen_llvm/Cargo.toml
src/librustc_codegen_llvm/asm.rs
src/librustc_codegen_llvm/base.rs
src/librustc_codegen_llvm/context.rs
src/librustc_codegen_llvm/intrinsic.rs
src/librustc_codegen_ssa/base.rs
src/librustc_codegen_ssa/mir/block.rs
src/librustc_codegen_ssa/mir/rvalue.rs
src/librustc_error_codes/error_codes/E0761.md
src/librustc_hir/def.rs
src/librustc_hir/lang_items.rs
src/librustc_hir/weak_lang_items.rs
src/librustc_infer/infer/error_reporting/mod.rs
src/librustc_infer/lib.rs
src/librustc_lexer/Cargo.toml
src/librustc_lexer/src/lib.rs
src/librustc_lexer/src/tests.rs
src/librustc_lint/builtin.rs
src/librustc_lint/types.rs
src/librustc_llvm/build.rs
src/librustc_metadata/Cargo.toml
src/librustc_metadata/dynamic_lib.rs
src/librustc_metadata/lib.rs
src/librustc_metadata/locator.rs
src/librustc_middle/lib.rs
src/librustc_middle/middle/lang_items.rs
src/librustc_middle/mir/interpret/value.rs
src/librustc_middle/mir/mod.rs
src/librustc_middle/query/mod.rs
src/librustc_middle/ty/adjustment.rs
src/librustc_middle/ty/context.rs
src/librustc_middle/ty/instance.rs
src/librustc_middle/ty/layout.rs
src/librustc_middle/ty/mod.rs
src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
src/librustc_mir/borrow_check/diagnostics/mod.rs
src/librustc_mir/borrow_check/diagnostics/move_errors.rs
src/librustc_mir/borrow_check/mod.rs
src/librustc_mir/borrow_check/type_check/mod.rs
src/librustc_mir/borrow_check/universal_regions.rs
src/librustc_mir/dataflow/move_paths/builder.rs
src/librustc_mir/interpret/intrinsics/caller_location.rs
src/librustc_mir/interpret/place.rs
src/librustc_mir/lib.rs
src/librustc_mir/monomorphize/collector.rs
src/librustc_mir/monomorphize/mod.rs
src/librustc_mir/monomorphize/partitioning.rs [deleted file]
src/librustc_mir/monomorphize/partitioning/default.rs [new file with mode: 0644]
src/librustc_mir/monomorphize/partitioning/merging.rs [new file with mode: 0644]
src/librustc_mir/monomorphize/partitioning/mod.rs [new file with mode: 0644]
src/librustc_mir/shim.rs
src/librustc_mir/transform/check_consts/validation.rs
src/librustc_mir/transform/elaborate_drops.rs
src/librustc_mir/transform/generator.rs
src/librustc_mir/transform/mod.rs
src/librustc_mir/transform/simplify_comparison_integral.rs [new file with mode: 0644]
src/librustc_mir/util/aggregate.rs
src/librustc_mir/util/elaborate_drops.rs
src/librustc_mir_build/build/matches/mod.rs
src/librustc_mir_build/build/matches/test.rs
src/librustc_mir_build/build/matches/util.rs
src/librustc_mir_build/build/mod.rs
src/librustc_mir_build/thir/cx/expr.rs
src/librustc_mir_build/thir/pattern/const_to_pat.rs
src/librustc_parse_format/lib.rs
src/librustc_passes/liveness.rs
src/librustc_passes/weak_lang_items.rs
src/librustc_session/options.rs
src/librustc_span/symbol.rs
src/librustc_target/spec/aarch64_unknown_hermit.rs
src/librustc_target/spec/avr_gnu_base.rs [new file with mode: 0644]
src/librustc_target/spec/avr_unknown_gnu_atmega328.rs [new file with mode: 0644]
src/librustc_target/spec/avr_unknown_unknown.rs [deleted file]
src/librustc_target/spec/freestanding_base.rs [deleted file]
src/librustc_target/spec/hermit_base.rs
src/librustc_target/spec/hermit_kernel_base.rs
src/librustc_target/spec/mod.rs
src/librustc_trait_selection/infer.rs
src/librustc_trait_selection/traits/error_reporting/suggestions.rs
src/librustc_trait_selection/traits/project.rs
src/librustc_trait_selection/traits/select/confirmation.rs
src/librustc_trait_selection/traits/structural_match.rs
src/librustc_trait_selection/traits/wf.rs
src/librustc_ty/common_traits.rs
src/librustc_typeck/astconv/mod.rs
src/librustc_typeck/check/_match.rs
src/librustc_typeck/check/cast.rs
src/librustc_typeck/check/closure.rs
src/librustc_typeck/check/demand.rs
src/librustc_typeck/check/expr.rs
src/librustc_typeck/check/method/suggest.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/check/pat.rs
src/librustc_typeck/check/regionck.rs
src/librustc_typeck/check/upvar.rs
src/librustc_typeck/check/wfcheck.rs
src/librustc_typeck/check/writeback.rs
src/librustc_typeck/coherence/builtin.rs
src/librustc_typeck/collect.rs
src/librustc_typeck/expr_use_visitor.rs
src/librustdoc/clean/auto_trait.rs
src/librustdoc/clean/cfg.rs
src/librustdoc/clean/types.rs
src/librustdoc/config.rs
src/librustdoc/core.rs
src/librustdoc/doctest.rs [new file with mode: 0644]
src/librustdoc/doctest/tests.rs [new file with mode: 0644]
src/librustdoc/html/markdown.rs
src/librustdoc/html/render/mod.rs
src/librustdoc/html/static/main.js
src/librustdoc/html/static/rustdoc.css
src/librustdoc/html/static/themes/ayu.css
src/librustdoc/html/static/themes/dark.css
src/librustdoc/lib.rs
src/librustdoc/markdown.rs
src/librustdoc/passes/collect_intra_doc_links.rs
src/librustdoc/passes/doc_test_lints.rs
src/librustdoc/test.rs [deleted file]
src/librustdoc/test/tests.rs [deleted file]
src/stage0.txt
src/test/assembly/asm/aarch64-types.rs
src/test/codegen/avr/avr-func-addrspace.rs
src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs
src/test/mir-opt/if-condition-int.rs [new file with mode: 0644]
src/test/mir-opt/if_condition_int.dont_opt_bool.SimplifyComparisonIntegral.diff [new file with mode: 0644]
src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff [new file with mode: 0644]
src/test/mir-opt/if_condition_int.dont_remove_comparison.SimplifyComparisonIntegral.diff [new file with mode: 0644]
src/test/mir-opt/if_condition_int.opt_char.SimplifyComparisonIntegral.diff [new file with mode: 0644]
src/test/mir-opt/if_condition_int.opt_i8.SimplifyComparisonIntegral.diff [new file with mode: 0644]
src/test/mir-opt/if_condition_int.opt_multiple_ifs.SimplifyComparisonIntegral.diff [new file with mode: 0644]
src/test/mir-opt/if_condition_int.opt_negative.SimplifyComparisonIntegral.diff [new file with mode: 0644]
src/test/mir-opt/if_condition_int.opt_u32.SimplifyComparisonIntegral.diff [new file with mode: 0644]
src/test/run-make-fulldeps/foreign-exceptions/foo.cpp
src/test/run-make-fulldeps/foreign-exceptions/foo.rs
src/test/run-make-fulldeps/issue-69368/a.rs
src/test/rustdoc-ui/intra-link-prim-conflict.rs
src/test/rustdoc-ui/intra-link-prim-conflict.stderr
src/test/rustdoc-ui/intra-links-ambiguity.stderr
src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs
src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr
src/test/rustdoc-ui/unknown-renamed-lints.rs [new file with mode: 0644]
src/test/rustdoc-ui/unknown-renamed-lints.stderr [new file with mode: 0644]
src/test/rustdoc/duplicate-cfg.rs
src/test/ui/async-await/issue-61076.rs
src/test/ui/async-await/issue-61076.stderr
src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.rs
src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.stderr
src/test/ui/binding/issue-53114-borrow-checks.stderr
src/test/ui/borrowck/borrowck-move-out-from-array-match.rs
src/test/ui/borrowck/borrowck-move-out-from-array-match.stderr
src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.rs
src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.stderr
src/test/ui/borrowck/borrowck-move-out-from-array-use-match.stderr
src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.rs
src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.stderr
src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr
src/test/ui/borrowck/borrowck-move-out-from-array.stderr
src/test/ui/borrowck/borrowck-uninit-field-access.stderr
src/test/ui/borrowck/move-in-pattern-mut.rs
src/test/ui/borrowck/move-in-pattern-mut.stderr
src/test/ui/borrowck/move-in-pattern.fixed
src/test/ui/borrowck/move-in-pattern.rs
src/test/ui/borrowck/move-in-pattern.stderr
src/test/ui/consts/const-eval/const_panic_libcore_main.rs
src/test/ui/issues/issue-70381.rs [new file with mode: 0644]
src/test/ui/issues/issue-70381.stderr [new file with mode: 0644]
src/test/ui/lint/clashing-extern-fn.rs
src/test/ui/lint/clashing-extern-fn.stderr
src/test/ui/macros/macro-comma-behavior.core.stderr
src/test/ui/macros/macro-comma-behavior.rs
src/test/ui/macros/macro-comma-behavior.std.stderr
src/test/ui/moves/issue-75904-move-closure-loop.rs [new file with mode: 0644]
src/test/ui/moves/issue-75904-move-closure-loop.stderr [new file with mode: 0644]
src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs
src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr
src/test/ui/moves/moves-based-on-type-match-bindings.rs
src/test/ui/moves/moves-based-on-type-match-bindings.stderr
src/test/ui/nll/move-subpaths-moves-root.rs
src/test/ui/nll/move-subpaths-moves-root.stderr
src/test/ui/no_owned_box_lang_item.rs
src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs
src/test/ui/range/issue-54505-no-std.rs
src/test/ui/range/issue-54505-no-std.stderr
src/test/ui/range_inclusive.rs
src/test/ui/realloc-16687.rs
src/test/ui/ref-suggestion.rs
src/test/ui/ref-suggestion.stderr
src/test/ui/unsized-locals/borrow-after-move.stderr
src/test/ui/unsized-locals/double-move.stderr
src/tools/clippy/.cargo/config
src/tools/clippy/.github/workflows/clippy.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/Cargo.toml
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/assign_ops.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/booleans.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/double_comparison.rs
src/tools/clippy/clippy_lints/src/duration_subsec.rs
src/tools/clippy/clippy_lints/src/enum_clike.rs
src/tools/clippy/clippy_lints/src/enum_variants.rs
src/tools/clippy/clippy_lints/src/eq_op.rs
src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
src/tools/clippy/clippy_lints/src/if_let_some_result.rs
src/tools/clippy/clippy_lints/src/if_not_else.rs
src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
src/tools/clippy/clippy_lints/src/inherent_impl.rs
src/tools/clippy/clippy_lints/src/int_plus_one.rs
src/tools/clippy/clippy_lints/src/len_zero.rs
src/tools/clippy/clippy_lints/src/let_and_return.rs [deleted file]
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops.rs
src/tools/clippy/clippy_lints/src/map_clone.rs
src/tools/clippy/clippy_lints/src/match_on_vec_items.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/mut_reference.rs
src/tools/clippy/clippy_lints/src/mutex_atomic.rs
src/tools/clippy/clippy_lints/src/non_copy_const.rs
src/tools/clippy/clippy_lints/src/precedence.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
src/tools/clippy/clippy_lints/src/repeat_once.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/self_assignment.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
src/tools/clippy/clippy_lints/src/swap.rs
src/tools/clippy/clippy_lints/src/to_string_in_display.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/transmute.rs
src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs
src/tools/clippy/clippy_lints/src/try_err.rs
src/tools/clippy/clippy_lints/src/types.rs
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
src/tools/clippy/clippy_lints/src/unused_unit.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/unwrap_in_result.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/useless_conversion.rs
src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
src/tools/clippy/clippy_lints/src/utils/author.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/higher.rs
src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/mod.rs
src/tools/clippy/clippy_lints/src/utils/paths.rs
src/tools/clippy/clippy_lints/src/vec.rs
src/tools/clippy/clippy_lints/src/wildcard_imports.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/src/lintlist/mod.rs
src/tools/clippy/tests/fmt.rs
src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs
src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs
src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr
src/tools/clippy/tests/ui/crashes/ice-5944.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/duration_subsec.stderr
src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr
src/tools/clippy/tests/ui/enum_variants.stderr
src/tools/clippy/tests/ui/float_equality_without_abs.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/float_equality_without_abs.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/if_let_some_result.stderr
src/tools/clippy/tests/ui/if_not_else.stderr
src/tools/clippy/tests/ui/impl.stderr
src/tools/clippy/tests/ui/implicit_saturating_sub.stderr
src/tools/clippy/tests/ui/int_plus_one.stderr
src/tools/clippy/tests/ui/iter_next_slice.stderr
src/tools/clippy/tests/ui/len_zero.fixed
src/tools/clippy/tests/ui/len_zero.rs
src/tools/clippy/tests/ui/len_zero_ranges.fixed
src/tools/clippy/tests/ui/len_zero_ranges.rs
src/tools/clippy/tests/ui/len_zero_ranges.stderr
src/tools/clippy/tests/ui/let_and_return.rs
src/tools/clippy/tests/ui/let_and_return.stderr
src/tools/clippy/tests/ui/map_clone.stderr
src/tools/clippy/tests/ui/methods.rs
src/tools/clippy/tests/ui/methods.stderr
src/tools/clippy/tests/ui/mut_reference.stderr
src/tools/clippy/tests/ui/mutex_atomic.stderr
src/tools/clippy/tests/ui/needless_doc_main.rs
src/tools/clippy/tests/ui/needless_doc_main.stderr
src/tools/clippy/tests/ui/needless_return.fixed
src/tools/clippy/tests/ui/needless_return.rs
src/tools/clippy/tests/ui/needless_return.stderr
src/tools/clippy/tests/ui/new_ret_no_self.rs
src/tools/clippy/tests/ui/new_ret_no_self.stderr
src/tools/clippy/tests/ui/option_as_ref_deref.fixed
src/tools/clippy/tests/ui/option_as_ref_deref.rs
src/tools/clippy/tests/ui/option_as_ref_deref.stderr
src/tools/clippy/tests/ui/precedence.fixed
src/tools/clippy/tests/ui/precedence.rs
src/tools/clippy/tests/ui/precedence.stderr
src/tools/clippy/tests/ui/redundant_allocation.fixed
src/tools/clippy/tests/ui/redundant_allocation.stderr
src/tools/clippy/tests/ui/redundant_closure_call_early.stderr
src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
src/tools/clippy/tests/ui/redundant_closure_call_late.rs
src/tools/clippy/tests/ui/same_item_push.rs
src/tools/clippy/tests/ui/self_assignment.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/self_assignment.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/single_char_push_str.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/single_char_push_str.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/single_char_push_str.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/stable_sort_primitive.stderr
src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs
src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr
src/tools/clippy/tests/ui/to_string_in_display.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/to_string_in_display.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/transmute.rs
src/tools/clippy/tests/ui/transmute.stderr
src/tools/clippy/tests/ui/transmute_float_to_int.rs
src/tools/clippy/tests/ui/transmute_float_to_int.stderr
src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
src/tools/clippy/tests/ui/unnecessary_clone.rs
src/tools/clippy/tests/ui/unnecessary_clone.stderr
src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/unwrap_in_result.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/unwrap_in_result.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/useless_conversion.fixed
src/tools/clippy/tests/ui/useless_conversion.rs
src/tools/clippy/tests/ui/useless_conversion.stderr
src/tools/clippy/tests/ui/vec.fixed
src/tools/clippy/tests/ui/vec.rs
src/tools/clippy/tests/ui/wildcard_imports.fixed
src/tools/clippy/tests/ui/wildcard_imports.rs
src/tools/clippy/tests/ui/wildcard_imports.stderr
src/tools/clippy/tests/ui/wrong_self_convention.rs
src/tools/clippy/tests/ui/wrong_self_convention.stderr
src/tools/compiletest/src/runtest.rs
src/tools/miri
src/tools/publish_toolstate.py
src/tools/tidy/Cargo.toml
src/tools/tidy/src/deps.rs

index f6f181612c92627d71b71f678e5b4db0384fb637..6c6c816eec622e626458b7b3164d488670bacfe6 100644 (file)
@@ -59,11 +59,6 @@ jobs:
         uses: actions/checkout@v1
         with:
           fetch-depth: 2
-      - name: configure GitHub Actions to kill the build when outdated
-        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
-        with:
-          github_token: "${{ secrets.github_token }}"
-        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'"
       - name: configure the PR in which the error message will be posted
         run: "echo \"[CI_PR_NUMBER=$num]\""
         env:
@@ -77,6 +72,11 @@ jobs:
       - name: decide whether to skip this job
         run: src/ci/scripts/should-skip-this.sh
         if: success() && !env.SKIP_JOB
+      - name: configure GitHub Actions to kill the build when outdated
+        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+        with:
+          github_token: "${{ secrets.github_token }}"
+        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED"
       - name: collect CPU statistics
         run: src/ci/scripts/collect-cpu-stats.sh
         if: success() && !env.SKIP_JOB
@@ -163,11 +163,6 @@ jobs:
         uses: actions/checkout@v1
         with:
           fetch-depth: 2
-      - name: configure GitHub Actions to kill the build when outdated
-        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
-        with:
-          github_token: "${{ secrets.github_token }}"
-        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'"
       - name: configure the PR in which the error message will be posted
         run: "echo \"[CI_PR_NUMBER=$num]\""
         env:
@@ -181,6 +176,11 @@ jobs:
       - name: decide whether to skip this job
         run: src/ci/scripts/should-skip-this.sh
         if: success() && !env.SKIP_JOB
+      - name: configure GitHub Actions to kill the build when outdated
+        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+        with:
+          github_token: "${{ secrets.github_token }}"
+        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED"
       - name: collect CPU statistics
         run: src/ci/scripts/collect-cpu-stats.sh
         if: success() && !env.SKIP_JOB
@@ -482,11 +482,6 @@ jobs:
         uses: actions/checkout@v1
         with:
           fetch-depth: 2
-      - name: configure GitHub Actions to kill the build when outdated
-        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
-        with:
-          github_token: "${{ secrets.github_token }}"
-        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'"
       - name: configure the PR in which the error message will be posted
         run: "echo \"[CI_PR_NUMBER=$num]\""
         env:
@@ -500,6 +495,11 @@ jobs:
       - name: decide whether to skip this job
         run: src/ci/scripts/should-skip-this.sh
         if: success() && !env.SKIP_JOB
+      - name: configure GitHub Actions to kill the build when outdated
+        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+        with:
+          github_token: "${{ secrets.github_token }}"
+        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED"
       - name: collect CPU statistics
         run: src/ci/scripts/collect-cpu-stats.sh
         if: success() && !env.SKIP_JOB
@@ -583,31 +583,34 @@ jobs:
           - name: dist-x86_64-apple
             env:
               SCRIPT: "./x.py dist"
-              RUST_CONFIGURE_ARGS: "--target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc"
+              RUST_CONFIGURE_ARGS: "--target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
               RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
               MACOSX_DEPLOYMENT_TARGET: 10.7
               NO_LLVM_ASSERTIONS: 1
               NO_DEBUG_ASSERTIONS: 1
               DIST_REQUIRE_ALL_TOOLS: 1
+              RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1
             os: macos-latest
           - name: dist-x86_64-apple-alt
             env:
               SCRIPT: "./x.py dist"
-              RUST_CONFIGURE_ARGS: "--enable-extended --enable-profiler --set rust.jemalloc"
+              RUST_CONFIGURE_ARGS: "--enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
               RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
               MACOSX_DEPLOYMENT_TARGET: 10.7
               NO_LLVM_ASSERTIONS: 1
               NO_DEBUG_ASSERTIONS: 1
+              RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1
             os: macos-latest
           - name: x86_64-apple
             env:
               SCRIPT: "./x.py --stage 2 test"
-              RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc"
+              RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
               RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
               MACOSX_DEPLOYMENT_TARGET: 10.8
               MACOSX_STD_DEPLOYMENT_TARGET: 10.7
               NO_LLVM_ASSERTIONS: 1
               NO_DEBUG_ASSERTIONS: 1
+              RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1
             os: macos-latest
     timeout-minutes: 600
     runs-on: "${{ matrix.os }}"
@@ -618,11 +621,6 @@ jobs:
         uses: actions/checkout@v1
         with:
           fetch-depth: 2
-      - name: configure GitHub Actions to kill the build when outdated
-        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
-        with:
-          github_token: "${{ secrets.github_token }}"
-        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'"
       - name: configure the PR in which the error message will be posted
         run: "echo \"[CI_PR_NUMBER=$num]\""
         env:
@@ -636,6 +634,11 @@ jobs:
       - name: decide whether to skip this job
         run: src/ci/scripts/should-skip-this.sh
         if: success() && !env.SKIP_JOB
+      - name: configure GitHub Actions to kill the build when outdated
+        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+        with:
+          github_token: "${{ secrets.github_token }}"
+        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED"
       - name: collect CPU statistics
         run: src/ci/scripts/collect-cpu-stats.sh
         if: success() && !env.SKIP_JOB
index f8fa2971b49d8fced1cebb6e8c0042c6fdba29fa..494b7b5963eb0cb1e303e9ccd49be0df00dd278d 100644 (file)
@@ -342,7 +342,7 @@ dependencies = [
 name = "cargo-miri"
 version = "0.1.0"
 dependencies = [
- "cargo_metadata 0.9.1",
+ "cargo_metadata 0.11.1",
  "directories",
  "rustc-workspace-hack",
  "rustc_version",
@@ -393,13 +393,12 @@ dependencies = [
 
 [[package]]
 name = "cargo_metadata"
-version = "0.9.1"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202"
+checksum = "89fec17b16f1ac67908af82e47d0a90a7afd0e1827b181cd77504323d3263d35"
 dependencies = [
- "semver 0.9.0",
+ "semver 0.10.0",
  "serde",
- "serde_derive",
  "serde_json",
 ]
 
@@ -507,7 +506,7 @@ dependencies = [
 name = "clippy"
 version = "0.0.212"
 dependencies = [
- "cargo_metadata 0.9.1",
+ "cargo_metadata 0.11.1",
  "clippy-mini-macro-test",
  "clippy_lints",
  "compiletest_rs",
@@ -515,7 +514,7 @@ dependencies = [
  "lazy_static",
  "rustc-workspace-hack",
  "rustc_tools_util 0.2.0",
- "semver 0.9.0",
+ "semver 0.10.0",
  "serde",
  "tempfile",
  "tester",
@@ -529,7 +528,7 @@ version = "0.2.0"
 name = "clippy_lints"
 version = "0.0.212"
 dependencies = [
- "cargo_metadata 0.9.1",
+ "cargo_metadata 0.11.1",
  "if_chain",
  "itertools 0.9.0",
  "lazy_static",
@@ -537,7 +536,7 @@ dependencies = [
  "quine-mc_cluskey",
  "quote",
  "regex-syntax",
- "semver 0.9.0",
+ "semver 0.10.0",
  "serde",
  "smallvec 1.4.2",
  "syn",
@@ -604,9 +603,9 @@ dependencies = [
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.32"
+version = "0.1.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bc4ac2c824d2bfc612cba57708198547e9a26943af0632aff033e0693074d5c"
+checksum = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455"
 dependencies = [
  "cc",
  "rustc-std-workspace-core",
@@ -997,6 +996,16 @@ dependencies = [
  "yaml-rust 0.4.4",
 ]
 
+[[package]]
+name = "expect-test"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e383741ea1982866572109d1a8c807bd36aad91fca701489fdca56ef92b3b8"
+dependencies = [
+ "difference",
+ "once_cell",
+]
+
 [[package]]
 name = "failure"
 version = "0.1.8"
@@ -1970,7 +1979,6 @@ dependencies = [
 name = "miri"
 version = "0.1.0"
 dependencies = [
- "byteorder",
  "colored",
  "compiletest_rs",
  "env_logger 0.7.1",
@@ -3344,7 +3352,6 @@ name = "rustc_codegen_llvm"
 version = "0.0.0"
 dependencies = [
  "bitflags",
- "flate2",
  "libc",
  "measureme",
  "rustc-demangle",
@@ -3365,6 +3372,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "smallvec 1.4.2",
+ "snap",
  "tracing",
 ]
 
@@ -3642,6 +3650,7 @@ dependencies = [
 name = "rustc_lexer"
 version = "0.1.0"
 dependencies = [
+ "expect-test",
  "unicode-xid",
 ]
 
@@ -3689,7 +3698,6 @@ dependencies = [
 name = "rustc_metadata"
 version = "0.0.0"
 dependencies = [
- "flate2",
  "libc",
  "memmap",
  "rustc_ast",
@@ -3707,6 +3715,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "smallvec 1.4.2",
+ "snap",
  "stable_deref_trait",
  "tracing",
  "winapi 0.3.9",
@@ -4377,6 +4386,12 @@ version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
 
+[[package]]
+name = "snap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da73c8f77aebc0e40c300b93f0a5f1bece7a248a36eee287d4e095f35c7b7d6e"
+
 [[package]]
 name = "socket2"
 version = "0.3.12"
@@ -4680,7 +4695,7 @@ dependencies = [
 name = "tidy"
 version = "0.1.0"
 dependencies = [
- "cargo_metadata 0.9.1",
+ "cargo_metadata 0.11.1",
  "lazy_static",
  "regex",
  "walkdir",
index 3399f7fe6c8b1463b4c432986a7f90f68561f154..fe9776bc5f8fe28c83102f85efc51b32bd26d5f8 100644 (file)
--- a/README.md
+++ b/README.md
@@ -36,6 +36,7 @@ by running `./x.py --help` or reading the [rustc dev guide][rustcguidebuild].
    * `python` 3 or 2.7
    * GNU `make` 3.81 or later
    * `cmake` 3.4.3 or later
+   * `ninja`
    * `curl`
    * `git`
    * `ssl` which comes in `libssl-dev` or `openssl-devel`
index b3d8c2f65f63377b32c5afd3958a6bf842d4cae0..64fe2df3c496c9fa3952183aa156abf3b18a73f5 100644 (file)
@@ -51,7 +51,7 @@ Compatibility Notes
 -------------------
 - [The target configuration option `abi_blacklist` has been renamed
   to `unsupported_abis`.][74150] The old name will still continue to work.
-- [Rustc will now warn if you have a C-like enum that implements `Drop`.][72331]
+- [Rustc will now warn if you cast a C-like enum that implements `Drop`.][72331]
   This was previously accepted but will become a hard error in a future release.
 - [Rustc will fail to compile if you have a struct with
   `#[repr(i128)]` or `#[repr(u128)]`.][74109] This representation is currently only
@@ -68,7 +68,19 @@ Compatibility Notes
 - [Rustc now correctly relates the lifetime of an existential associated
   type.][71896] This fixes some edge cases where `rustc` would erroneously allow
   you to pass a shorter lifetime than expected.
-
+- [Rustc now dynamically links to `libz` (also called `zlib`) on Linux.][74420]
+  The library will need to be installed for `rustc` to work, even though we
+  expect it to be already available on most systems.
+- [Tests annotated with `#[should_panic]` are broken on ARMv7 while running
+  under QEMU.][74820]
+- [Pretty printing of some tokens in procedural macros changed.][75453] The
+  exact output returned by rustc's pretty printing is an unstable
+  implementation detail: we recommend any macro relying on it to switch to a
+  more robust parsing system.
+
+[75453]: https://github.com/rust-lang/rust/issues/75453/
+[74820]: https://github.com/rust-lang/rust/issues/74820/
+[74420]: https://github.com/rust-lang/rust/issues/74420/
 [74109]: https://github.com/rust-lang/rust/pull/74109/
 [74150]: https://github.com/rust-lang/rust/pull/74150/
 [73862]: https://github.com/rust-lang/rust/pull/73862/
index 36587cc07844147be96b41801e9f6961fc7d5398..9abb8add785a9352cb46bb4ef49009b3838c4724 100644 (file)
 # dynamic version to be available.
 #static-libstdcpp = false
 
-# Tell the LLVM build system to use Ninja instead of the platform default for
-# the generated build system. This can sometimes be faster than make, for
-# example.
-#ninja = false
+# Whether to use Ninja to build LLVM. This runs much faster than make.
+#ninja = true
 
 # LLVM targets to build support for.
 # Note: this is NOT related to Rust compilation targets. However, as Rust is
index 87b86e590a4c82b23e5d4d5a030eef7c53057728..5f09f8def4d0a1eb6a7a76ae832617d14bd73a17 100644 (file)
@@ -3,7 +3,7 @@
 #![stable(feature = "alloc_module", since = "1.28.0")]
 
 use core::intrinsics::{self, min_align_of_val, size_of_val};
-use core::ptr::{NonNull, Unique};
+use core::ptr::{self, NonNull, Unique};
 
 #[stable(feature = "alloc_module", since = "1.28.0")]
 #[doc(inline)]
@@ -162,36 +162,45 @@ fn alloc_impl(&mut self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>,
     unsafe fn grow_impl(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
         zeroed: bool,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         debug_assert!(
-            new_size >= layout.size(),
-            "`new_size` must be greater than or equal to `layout.size()`"
+            new_layout.size() >= old_layout.size(),
+            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
         );
 
-        match layout.size() {
-            // SAFETY: the caller must ensure that the `new_size` does not overflow.
-            // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-            0 => unsafe {
-                let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
-                self.alloc_impl(new_layout, zeroed)
-            },
+        match old_layout.size() {
+            0 => self.alloc_impl(new_layout, zeroed),
 
             // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size`
             // as required by safety conditions. Other conditions must be upheld by the caller
-            old_size => unsafe {
-                // `realloc` probably checks for `new_size >= size` or something similar.
-                intrinsics::assume(new_size >= layout.size());
+            old_size if old_layout.align() == new_layout.align() => unsafe {
+                let new_size = new_layout.size();
+
+                // `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
+                intrinsics::assume(new_size >= old_layout.size());
 
-                let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
+                let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
                 if zeroed {
                     raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
                 }
                 Ok(NonNull::slice_from_raw_parts(ptr, new_size))
             },
+
+            // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
+            // both the old and new memory allocation are valid for reads and writes for `old_size`
+            // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+            // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+            // for `dealloc` must be upheld by the caller.
+            old_size => unsafe {
+                let new_ptr = self.alloc_impl(new_layout, zeroed)?;
+                ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
+                self.dealloc(ptr, old_layout);
+                Ok(new_ptr)
+            },
         }
     }
 }
@@ -221,52 +230,64 @@ unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
     unsafe fn grow(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: all conditions must be upheld by the caller
-        unsafe { self.grow_impl(ptr, layout, new_size, false) }
+        unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
     }
 
     #[inline]
     unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: all conditions must be upheld by the caller
-        unsafe { self.grow_impl(ptr, layout, new_size, true) }
+        unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
     }
 
     #[inline]
     unsafe fn shrink(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         debug_assert!(
-            new_size <= layout.size(),
-            "`new_size` must be smaller than or equal to `layout.size()`"
+            new_layout.size() <= old_layout.size(),
+            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
         );
 
-        match new_size {
+        match new_layout.size() {
             // SAFETY: conditions must be upheld by the caller
             0 => unsafe {
-                self.dealloc(ptr, layout);
-                Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0))
+                self.dealloc(ptr, old_layout);
+                Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0))
             },
 
             // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
-            new_size => unsafe {
-                // `realloc` probably checks for `new_size <= size` or something similar.
-                intrinsics::assume(new_size <= layout.size());
+            new_size if old_layout.align() == new_layout.align() => unsafe {
+                // `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
+                intrinsics::assume(new_size <= old_layout.size());
 
-                let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
+                let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
                 Ok(NonNull::slice_from_raw_parts(ptr, new_size))
             },
+
+            // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
+            // both the old and new memory allocation are valid for reads and writes for `new_size`
+            // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+            // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+            // for `dealloc` must be upheld by the caller.
+            new_size => unsafe {
+                let new_ptr = self.alloc(new_layout)?;
+                ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
+                self.dealloc(ptr, old_layout);
+                Ok(new_ptr)
+            },
         }
     }
 }
@@ -279,7 +300,7 @@ unsafe fn shrink(
 unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
     let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
     match Global.alloc(layout) {
-        Ok(ptr) => ptr.as_non_null_ptr().as_ptr(),
+        Ok(ptr) => ptr.as_mut_ptr(),
         Err(_) => handle_alloc_error(layout),
     }
 }
index 247b636c808acf5e15a2b16acf212860872c73b3..05382d0b5594edfece3d193fb5d1698b8fdcdab0 100644 (file)
@@ -3,6 +3,7 @@
 
 use core::alloc::LayoutErr;
 use core::cmp;
+use core::intrinsics;
 use core::mem::{self, ManuallyDrop, MaybeUninit};
 use core::ops::Drop;
 use core::ptr::{NonNull, Unique};
@@ -465,8 +466,9 @@ fn shrink(&mut self, amount: usize) -> Result<(), TryReserveError> {
         let new_size = amount * mem::size_of::<T>();
 
         let ptr = unsafe {
-            self.alloc.shrink(ptr, layout, new_size).map_err(|_| TryReserveError::AllocError {
-                layout: Layout::from_size_align_unchecked(new_size, layout.align()),
+            let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
+            self.alloc.shrink(ptr, layout, new_layout).map_err(|_| TryReserveError::AllocError {
+                layout: new_layout,
                 non_exhaustive: (),
             })?
         };
@@ -494,13 +496,16 @@ fn finish_grow<A>(
 
     let memory = if let Some((ptr, old_layout)) = current_memory {
         debug_assert_eq!(old_layout.align(), new_layout.align());
-        unsafe { alloc.grow(ptr, old_layout, new_layout.size()) }
+        unsafe {
+            // The allocator checks for alignment equality
+            intrinsics::assume(old_layout.align() == new_layout.align());
+            alloc.grow(ptr, old_layout, new_layout)
+        }
     } else {
         alloc.alloc(new_layout)
-    }
-    .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?;
+    };
 
-    Ok(memory)
+    memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })
 }
 
 unsafe impl<#[may_dangle] T, A: AllocRef> Drop for RawVec<T, A> {
index 058a06e1326f82152318ca3b77e490609f0629c7..61e1245f0735c32dbbda6b2845492798c98ceaeb 100644 (file)
@@ -9,7 +9,7 @@
 //!
 //! # Examples
 //!
-//! You can explicitly create a [`Vec<T>`] with [`new`]:
+//! You can explicitly create a [`Vec`] with [`Vec::new`]:
 //!
 //! ```
 //! let v: Vec<i32> = Vec::new();
@@ -50,8 +50,6 @@
 //! v[1] = v[1] + 5;
 //! ```
 //!
-//! [`Vec<T>`]: Vec
-//! [`new`]: Vec::new
 //! [`push`]: Vec::push
 
 #![stable(feature = "rust1", since = "1.0.0")]
@@ -1456,9 +1454,9 @@ fn assert_failed(at: usize, len: usize) -> ! {
     /// If `new_len` is less than `len`, the `Vec` is simply truncated.
     ///
     /// This method uses a closure to create new values on every push. If
-    /// you'd rather [`Clone`] a given value, use [`resize`]. If you want
-    /// to use the [`Default`] trait to generate values, you can pass
-    /// [`Default::default()`] as the second argument.
+    /// you'd rather [`Clone`] a given value, use [`Vec::resize`]. If you
+    /// want to use the [`Default`] trait to generate values, you can
+    /// pass [`Default::default`] as the second argument.
     ///
     /// # Examples
     ///
@@ -1472,8 +1470,6 @@ fn assert_failed(at: usize, len: usize) -> ! {
     /// vec.resize_with(4, || { p *= 2; p });
     /// assert_eq!(vec, [2, 4, 8, 16]);
     /// ```
-    ///
-    /// [`resize`]: Vec::resize
     #[stable(feature = "vec_resize_with", since = "1.33.0")]
     pub fn resize_with<F>(&mut self, new_len: usize, f: F)
     where
index ad4f8bf1397c2de696b727dc4ab927204ac3cd4d..c1fda2fce641fead20d505a0e293dfb4d10d9b01 100644 (file)
@@ -147,9 +147,8 @@ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
     /// Attempts to extend the memory block.
     ///
     /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
-    /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s
-    /// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the
-    /// allocation referenced by `ptr` to fit the new layout.
+    /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
+    /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout.
     ///
     /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
     /// transferred to this allocator. The memory may or may not have been freed, and should be
@@ -163,11 +162,9 @@ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
     ///
     /// # Safety
     ///
-    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
-    /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.),
-    /// * `new_size` must be greater than or equal to `layout.size()`, and
-    /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow
-    ///   (i.e., the rounded value must be less than or equal to `usize::MAX`).
+    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
+    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
+    /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
     ///
     /// [*currently allocated*]: #currently-allocated-memory
     /// [*fit*]: #memory-fitting
@@ -188,28 +185,24 @@ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
     unsafe fn grow(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
-        let size = layout.size();
         debug_assert!(
-            new_size >= size,
-            "`new_size` must be greater than or equal to `layout.size()`"
+            new_layout.size() >= old_layout.size(),
+            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
         );
 
-        // SAFETY: the caller must ensure that the `new_size` does not overflow.
-        // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-        let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
         let new_ptr = self.alloc(new_layout)?;
 
-        // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
-        // memory allocation are valid for reads and writes for `size` bytes. Also, because the old
-        // allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
-        // `copy_nonoverlapping` is safe.
-        // The safety contract for `dealloc` must be upheld by the caller.
+        // SAFETY: because `new_layout.size()` must be greater than or equal to
+        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
+        // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
+        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
+        // safe. The safety contract for `dealloc` must be upheld by the caller.
         unsafe {
-            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
-            self.dealloc(ptr, layout);
+            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
+            self.dealloc(ptr, old_layout);
         }
 
         Ok(new_ptr)
@@ -220,21 +213,19 @@ unsafe fn grow(
     ///
     /// The memory block will contain the following contents after a successful call to
     /// `grow_zeroed`:
-    ///   * Bytes `0..layout.size()` are preserved from the original allocation.
-    ///   * Bytes `layout.size()..old_size` will either be preserved or zeroed, depending on the
-    ///     allocator implementation. `old_size` refers to the size of the memory block prior to
-    ///     the `grow_zeroed` call, which may be larger than the size that was originally requested
-    ///     when it was allocated.
+    ///   * Bytes `0..old_layout.size()` are preserved from the original allocation.
+    ///   * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on
+    ///     the allocator implementation. `old_size` refers to the size of the memory block prior
+    ///     to the `grow_zeroed` call, which may be larger than the size that was originally
+    ///     requested when it was allocated.
     ///   * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory
-    ///     block returned by the `grow` call.
+    ///     block returned by the `grow_zeroed` call.
     ///
     /// # Safety
     ///
-    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
-    /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.),
-    /// * `new_size` must be greater than or equal to `layout.size()`, and
-    /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow
-    ///   (i.e., the rounded value must be less than or equal to `usize::MAX`).
+    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
+    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
+    /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
     ///
     /// [*currently allocated*]: #currently-allocated-memory
     /// [*fit*]: #memory-fitting
@@ -255,28 +246,24 @@ unsafe fn grow(
     unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
-        let size = layout.size();
         debug_assert!(
-            new_size >= size,
-            "`new_size` must be greater than or equal to `layout.size()`"
+            new_layout.size() >= old_layout.size(),
+            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
         );
 
-        // SAFETY: the caller must ensure that the `new_size` does not overflow.
-        // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-        let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
         let new_ptr = self.alloc_zeroed(new_layout)?;
 
-        // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
-        // memory allocation are valid for reads and writes for `size` bytes. Also, because the old
-        // allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
-        // `copy_nonoverlapping` is safe.
-        // The safety contract for `dealloc` must be upheld by the caller.
+        // SAFETY: because `new_layout.size()` must be greater than or equal to
+        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
+        // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
+        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
+        // safe. The safety contract for `dealloc` must be upheld by the caller.
         unsafe {
-            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
-            self.dealloc(ptr, layout);
+            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
+            self.dealloc(ptr, old_layout);
         }
 
         Ok(new_ptr)
@@ -285,9 +272,8 @@ unsafe fn grow_zeroed(
     /// Attempts to shrink the memory block.
     ///
     /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
-    /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s
-    /// alignment and a size given by `new_size`. To accomplish this, the allocator may shrink the
-    /// allocation referenced by `ptr` to fit the new layout.
+    /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
+    /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout.
     ///
     /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
     /// transferred to this allocator. The memory may or may not have been freed, and should be
@@ -301,9 +287,9 @@ unsafe fn grow_zeroed(
     ///
     /// # Safety
     ///
-    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
-    /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), and
-    /// * `new_size` must be smaller than or equal to `layout.size()`.
+    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
+    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
+    /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`.
     ///
     /// [*currently allocated*]: #currently-allocated-memory
     /// [*fit*]: #memory-fitting
@@ -324,28 +310,24 @@ unsafe fn grow_zeroed(
     unsafe fn shrink(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
-        let size = layout.size();
         debug_assert!(
-            new_size <= size,
-            "`new_size` must be smaller than or equal to `layout.size()`"
+            new_layout.size() <= old_layout.size(),
+            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
         );
 
-        // SAFETY: the caller must ensure that the `new_size` does not overflow.
-        // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-        let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
         let new_ptr = self.alloc(new_layout)?;
 
-        // SAFETY: because `new_size` must be lower than or equal to `size`, both the old and new
-        // memory allocation are valid for reads and writes for `new_size` bytes. Also, because the
-        // old allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
-        // `copy_nonoverlapping` is safe.
-        // The safety contract for `dealloc` must be upheld by the caller.
+        // SAFETY: because `new_layout.size()` must be lower than or equal to
+        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
+        // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet
+        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
+        // safe. The safety contract for `dealloc` must be upheld by the caller.
         unsafe {
-            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
-            self.dealloc(ptr, layout);
+            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size());
+            self.dealloc(ptr, old_layout);
         }
 
         Ok(new_ptr)
@@ -385,32 +367,32 @@ unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
     unsafe fn grow(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: the safety contract must be upheld by the caller
-        unsafe { (**self).grow(ptr, layout, new_size) }
+        unsafe { (**self).grow(ptr, old_layout, new_layout) }
     }
 
     #[inline]
     unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: the safety contract must be upheld by the caller
-        unsafe { (**self).grow_zeroed(ptr, layout, new_size) }
+        unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
     }
 
     #[inline]
     unsafe fn shrink(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: the safety contract must be upheld by the caller
-        unsafe { (**self).shrink(ptr, layout, new_size) }
+        unsafe { (**self).shrink(ptr, old_layout, new_layout) }
     }
 }
index 88795d8429d29ff57b91e63f32890936edc47827..f85be5584e3f197d76bd003af81612a15b1be0a0 100644 (file)
@@ -362,7 +362,6 @@ macro_rules! array_impl_default {
 
 array_impl_default! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T}
 
-#[cfg(not(bootstrap))]
 #[lang = "array"]
 impl<T, const N: usize> [T; N] {
     /// Returns an array of the same size as `self`, with function `f` applied to each element
index fcd07befae50458dcc8661a1a79617394265f500..5f10a2eb023a060905ce641fc622fb55c382e18d 100644 (file)
@@ -385,7 +385,7 @@ pub trait Into<T>: Sized {
 ))]
 pub trait From<T>: Sized {
     /// Performs the conversion.
-    #[cfg_attr(not(bootstrap), lang = "from")]
+    #[lang = "from"]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn from(_: T) -> Self;
 }
index 8169c146137c224b4ee8016c69e3e3b76dfd5ee4..e9a99ddb6b1bdd90910a110cfd3904b1ed60aac3 100644 (file)
@@ -23,7 +23,7 @@
 /// When using a future, you generally won't call `poll` directly, but instead
 /// `.await` the value.
 ///
-/// [`Waker`]: ../task/struct.Waker.html
+/// [`Waker`]: crate::task::Waker
 #[doc(spotlight)]
 #[must_use = "futures do nothing unless you `.await` or poll them"]
 #[stable(feature = "futures_api", since = "1.36.0")]
@@ -91,12 +91,10 @@ pub trait Future {
     /// (memory corruption, incorrect use of `unsafe` functions, or the like),
     /// regardless of the future's state.
     ///
-    /// [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending
-    /// [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready
-    /// [`Context`]: ../task/struct.Context.html
-    /// [`Waker`]: ../task/struct.Waker.html
-    /// [`Waker::wake`]: ../task/struct.Waker.html#method.wake
-    #[cfg_attr(not(bootstrap), lang = "poll")]
+    /// [`Poll::Ready(val)`]: Poll::Ready
+    /// [`Waker`]: crate::task::Waker
+    /// [`Waker::wake`]: crate::task::Waker::wake
+    #[lang = "poll"]
     #[stable(feature = "futures_api", since = "1.36.0")]
     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
 }
index d44ef857c133a298e09c7b74e845335b8e9705da..8b3df76f71fdda130f7fde6fc14dbb5b1a69de36 100644 (file)
@@ -53,7 +53,7 @@ unsafe impl Sync for ResumeTy {}
 /// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give
 /// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
 // This is `const` to avoid extra errors after we recover from `const async fn`
-#[cfg_attr(not(bootstrap), lang = "from_generator")]
+#[lang = "from_generator"]
 #[doc(hidden)]
 #[unstable(feature = "gen_future", issue = "50547")]
 #[inline]
@@ -86,7 +86,7 @@ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
     GenFuture(gen)
 }
 
-#[cfg_attr(not(bootstrap), lang = "get_context")]
+#[lang = "get_context"]
 #[doc(hidden)]
 #[unstable(feature = "gen_future", issue = "50547")]
 #[inline]
index 6abe19dc155d1d96bedc14946942f6604dacff51..f53ba98143842d5f50ca9f40918a8f9f8e6ecb8a 100644 (file)
@@ -39,8 +39,6 @@
 //! If you need more control over how a value is hashed, you need to implement
 //! the [`Hash`] trait:
 //!
-//! [`Hash`]: trait.Hash.html
-//!
 //! ```rust
 //! use std::collections::hash_map::DefaultHasher;
 //! use std::hash::{Hash, Hasher};
 /// Thankfully, you won't need to worry about upholding this property when
 /// deriving both [`Eq`] and `Hash` with `#[derive(PartialEq, Eq, Hash)]`.
 ///
-/// [`Eq`]: ../../std/cmp/trait.Eq.html
-/// [`Hasher`]: trait.Hasher.html
 /// [`HashMap`]: ../../std/collections/struct.HashMap.html
 /// [`HashSet`]: ../../std/collections/struct.HashSet.html
-/// [`hash`]: #tymethod.hash
+/// [`hash`]: Hash::hash
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Hash {
     /// Feeds this value into the given [`Hasher`].
@@ -168,8 +164,6 @@ pub trait Hash {
     /// 7920.hash(&mut hasher);
     /// println!("Hash is {:x}!", hasher.finish());
     /// ```
-    ///
-    /// [`Hasher`]: trait.Hasher.html
     #[stable(feature = "rust1", since = "1.0.0")]
     fn hash<H: Hasher>(&self, state: &mut H);
 
@@ -186,8 +180,6 @@ pub trait Hash {
     /// Hash::hash_slice(&numbers, &mut hasher);
     /// println!("Hash is {:x}!", hasher.finish());
     /// ```
-    ///
-    /// [`Hasher`]: trait.Hasher.html
     #[stable(feature = "hash_slice", since = "1.3.0")]
     fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
     where
@@ -239,10 +231,9 @@ pub(crate) mod macros {
 /// println!("Hash is {:x}!", hasher.finish());
 /// ```
 ///
-/// [`Hash`]: trait.Hash.html
-/// [`finish`]: #tymethod.finish
-/// [`write`]: #tymethod.write
-/// [`write_u8`]: #method.write_u8
+/// [`finish`]: Hasher::finish
+/// [`write`]: Hasher::write
+/// [`write_u8`]: Hasher::write_u8
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Hasher {
     /// Returns the hash value for the values written so far.
@@ -264,7 +255,7 @@ pub trait Hasher {
     /// println!("Hash is {:x}!", hasher.finish());
     /// ```
     ///
-    /// [`write`]: #tymethod.write
+    /// [`write`]: Hasher::write
     #[stable(feature = "rust1", since = "1.0.0")]
     fn finish(&self) -> u64;
 
@@ -433,8 +424,7 @@ fn write_isize(&mut self, i: isize) {
 /// assert_eq!(hasher_1.finish(), hasher_2.finish());
 /// ```
 ///
-/// [`build_hasher`]: #tymethod.build_hasher
-/// [`Hasher`]: trait.Hasher.html
+/// [`build_hasher`]: BuildHasher::build_hasher
 /// [`HashMap`]: ../../std/collections/struct.HashMap.html
 #[stable(since = "1.7.0", feature = "build_hasher")]
 pub trait BuildHasher {
@@ -456,8 +446,6 @@ pub trait BuildHasher {
     /// let s = RandomState::new();
     /// let new_s = s.build_hasher();
     /// ```
-    ///
-    /// [`Hasher`]: trait.Hasher.html
     #[stable(since = "1.7.0", feature = "build_hasher")]
     fn build_hasher(&self) -> Self::Hasher;
 }
@@ -470,7 +458,7 @@ pub trait BuildHasher {
 /// defined.
 ///
 /// Any `BuildHasherDefault` is [zero-sized]. It can be created with
-/// [`default`][method.Default]. When using `BuildHasherDefault` with [`HashMap`] or
+/// [`default`][method.default]. When using `BuildHasherDefault` with [`HashMap`] or
 /// [`HashSet`], this doesn't need to be done, since they implement appropriate
 /// [`Default`] instances themselves.
 ///
@@ -503,10 +491,7 @@ pub trait BuildHasher {
 /// let hash_map = HashMap::<u32, u32, MyBuildHasher>::default();
 /// ```
 ///
-/// [`BuildHasher`]: trait.BuildHasher.html
-/// [`Default`]: ../default/trait.Default.html
-/// [method.default]: #method.default
-/// [`Hasher`]: trait.Hasher.html
+/// [method.default]: BuildHasherDefault::default
 /// [`HashMap`]: ../../std/collections/struct.HashMap.html
 /// [`HashSet`]: ../../std/collections/struct.HashSet.html
 /// [zero-sized]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts
index 461b4c79a1d1c12b8faf03978f2619d19b91f50b..d40a3802867624dd8d3dcd425ff2eaf3662f7956 100644 (file)
@@ -101,7 +101,7 @@ pub fn spin_loop() {
 /// [`std::convert::identity`]: https://doc.rust-lang.org/core/convert/fn.identity.html
 ///
 /// Unlike [`std::convert::identity`], a Rust compiler is encouraged to assume that `black_box` can
-/// use `x` in any possible valid way that Rust code is allowed to without introducing undefined
+/// use `dummy` in any possible valid way that Rust code is allowed to without introducing undefined
 /// behavior in the calling code. This property makes `black_box` useful for writing code in which
 /// certain optimizations are not desired, such as benchmarks.
 ///
index 84c7787a18fd17393d372c721708834ed34fb714..75827d785e10efe413c2db030db5fc61a99ca08b 100644 (file)
@@ -235,7 +235,7 @@ pub trait IntoIterator {
     /// assert_eq!(Some(3), iter.next());
     /// assert_eq!(None, iter.next());
     /// ```
-    #[cfg_attr(not(bootstrap), lang = "into_iter")]
+    #[lang = "into_iter"]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn into_iter(self) -> Self::IntoIter;
 }
index 32e43ed42f385d9c3bc7ae461246cceb464c0a8b..aca6699b9efbb1e451b508316271f073001914ec 100644 (file)
@@ -130,7 +130,7 @@ pub trait Iterator {
     /// assert_eq!(None, iter.next());
     /// assert_eq!(None, iter.next());
     /// ```
-    #[cfg_attr(not(bootstrap), lang = "next")]
+    #[lang = "next"]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn next(&mut self) -> Option<Self::Item>;
 
index 99f8cc66638f3f7ff084fa4f3815a9491ba02f4d..aef82a5aec5d0f1863c70f997837fb7a71991ad8 100644 (file)
@@ -98,7 +98,7 @@
 #![feature(custom_inner_attributes)]
 #![feature(decl_macro)]
 #![feature(doc_cfg)]
-#![cfg_attr(not(bootstrap), feature(doc_spotlight))]
+#![feature(doc_spotlight)]
 #![feature(duration_consts_2)]
 #![feature(extern_types)]
 #![feature(fundamental)]
index d26f2124f15fd46ccb83d054919f83e02d709bc8..4e0da1fc4a6a38c77ba4cfccef3589478ec5a9a8 100644 (file)
@@ -333,16 +333,16 @@ macro_rules! r#try {
 /// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be
 /// formatted according to the specified format string and the result will be passed to the writer.
 /// The writer may be any value with a `write_fmt` method; generally this comes from an
-/// implementation of either the [`std::fmt::Write`] or the [`std::io::Write`] trait. The macro
-/// returns whatever the `write_fmt` method returns; commonly a [`std::fmt::Result`], or an
+/// implementation of either the [`fmt::Write`] or the [`io::Write`] trait. The macro
+/// returns whatever the `write_fmt` method returns; commonly a [`fmt::Result`], or an
 /// [`io::Result`].
 ///
 /// See [`std::fmt`] for more information on the format string syntax.
 ///
 /// [`std::fmt`]: crate::fmt
-/// [`std::fmt::Write`]: crate::fmt::Write
-/// [`std::io::Write`]: ../std/io/trait.Write.html
-/// [`std::fmt::Result`]: crate::fmt::Result
+/// [`fmt::Write`]: crate::fmt::Write
+/// [`io::Write`]: ../std/io/trait.Write.html
+/// [`fmt::Result`]: crate::fmt::Result
 /// [`io::Result`]: ../std/io/type.Result.html
 ///
 /// # Examples
index 3ecfc43be049b534478bb5bdaf58dfac63f31a94..a02e74d5e5a4d3980c1c08b4facf83dd9908a8c1 100644 (file)
@@ -5,12 +5,12 @@ to the caller of the program. `panic!` should be used when a program reaches
 an unrecoverable state.
 
 This macro is the perfect way to assert conditions in example code and in
-tests. `panic!` is closely tied with the `unwrap` method of both [`Option`]
-and [`Result`][runwrap] enums. Both implementations call `panic!` when they are set
-to None or Err variants.
+tests. `panic!` is closely tied with the `unwrap` method of both
+[`Option`][ounwrap] and [`Result`][runwrap] enums. Both implementations call
+`panic!` when they are set to [`None`] or [`Err`] variants.
 
 This macro is used to inject panic into a Rust thread, causing the thread to
-panic entirely. Each thread's panic can be reaped as the `Box<Any>` type,
+panic entirely. Each thread's panic can be reaped as the [`Box`]`<`[`Any`]`>` type,
 and the single-argument form of the `panic!` macro will be the value which
 is transmitted.
 
@@ -24,11 +24,11 @@ The multi-argument form of this macro panics with a string and has the
 
 See also the macro [`compile_error!`], for raising errors during compilation.
 
-[runwrap]: ../std/result/enum.Result.html#method.unwrap
-[`Option`]: ../std/option/enum.Option.html#method.unwrap
-[`Result`]: ../std/result/enum.Result.html
+[ounwrap]: Option::unwrap
+[runwrap]: Result::unwrap
+[`Box`]: ../std/boxed/struct.Box.html
+[`Any`]: crate::any::Any
 [`format!`]: ../std/macro.format.html
-[`compile_error!`]: ../std/macro.compile_error.html
 [book]: ../book/ch09-00-error-handling.html
 
 # Current implementation
index 9326aaf56847c577f59e2e86b299f9c67bf6082f..a02ee9c5ba965c3a2b3a6643d5c2aabd3648d171 100644 (file)
@@ -708,7 +708,7 @@ unsafe impl<T: Send + ?Sized> Send for &mut T {}
 pub trait DiscriminantKind {
     /// The type of the discriminant, which must satisfy the trait
     /// bounds required by `mem::Discriminant`.
-    #[cfg_attr(not(bootstrap), lang = "discriminant_type")]
+    #[lang = "discriminant_type"]
     type Discriminant: Clone + Copy + Debug + Eq + PartialEq + Hash + Send + Sync + Unpin;
 }
 
@@ -728,23 +728,23 @@ unsafe impl<T: ?Sized> Freeze for &mut T {}
 
 /// Types that can be safely moved after being pinned.
 ///
-/// Since Rust itself has no notion of immovable types, and considers moves
-/// (e.g., through assignment or [`mem::replace`]) to always be safe,
-/// this trait cannot prevent types from moving by itself.
+/// Rust itself has no notion of immovable types, and considers moves (e.g.,
+/// through assignment or [`mem::replace`]) to always be safe.
 ///
-/// Instead it is used to prevent moves through the type system,
-/// by controlling the behavior of pointers `P` wrapped in the [`Pin<P>`] wrapper,
-/// which "pin" the type in place by not allowing it to be moved out of them.
-/// See the [`pin module`] documentation for more information on pinning.
+/// The [`Pin`][Pin] type is used instead to prevent moves through the type
+/// system. Pointers `P<T>` wrapped in the [`Pin<P<T>>`][Pin] wrapper can't be
+/// moved out of. See the [`pin module`] documentation for more information on
+/// pinning.
 ///
-/// Implementing this trait lifts the restrictions of pinning off a type,
-/// which then allows it to move out with functions such as [`mem::replace`].
+/// Implementing the `Unpin` trait for `T` lifts the restrictions of pinning off
+/// the type, which then allows moving `T` out of [`Pin<P<T>>`][Pin] with
+/// functions such as [`mem::replace`].
 ///
 /// `Unpin` has no consequence at all for non-pinned data. In particular,
 /// [`mem::replace`] happily moves `!Unpin` data (it works for any `&mut T`, not
-/// just when `T: Unpin`). However, you cannot use
-/// [`mem::replace`] on data wrapped inside a [`Pin<P>`] because you cannot get the
-/// `&mut T` you need for that, and *that* is what makes this system work.
+/// just when `T: Unpin`). However, you cannot use [`mem::replace`] on data
+/// wrapped inside a [`Pin<P<T>>`][Pin] because you cannot get the `&mut T` you
+/// need for that, and *that* is what makes this system work.
 ///
 /// So this, for example, can only be done on types implementing `Unpin`:
 ///
@@ -765,8 +765,8 @@ unsafe impl<T: ?Sized> Freeze for &mut T {}
 /// This trait is automatically implemented for almost every type.
 ///
 /// [`mem::replace`]: ../../std/mem/fn.replace.html
-/// [`Pin<P>`]: ../pin/struct.Pin.html
-/// [`pin module`]: ../../std/pin/index.html
+/// [Pin]: crate::pin::Pin
+/// [`pin module`]: crate::pin
 #[stable(feature = "pin", since = "1.33.0")]
 #[rustc_on_unimplemented(
     on(_Self = "std::future::Future", note = "consider using `Box::pin`",),
index e45aa86c0795a915d7f5b079e4189d8a7999e3a7..aab0e96d83ab98c6a5b6faf6635bc4bf16580c69 100644 (file)
@@ -74,8 +74,12 @@ impl<T> ManuallyDrop<T> {
     ///
     /// ```rust
     /// use std::mem::ManuallyDrop;
-    /// ManuallyDrop::new(Box::new(()));
+    /// let mut x = ManuallyDrop::new(String::from("Hello World!"));
+    /// x.truncate(5); // You can still safely operate on the value
+    /// assert_eq!(*x, "Hello");
+    /// // But `Drop` will not be run here
     /// ```
+    #[must_use = "if you don't need the wrapper, you can use `mem::forget` instead"]
     #[stable(feature = "manually_drop", since = "1.20.0")]
     #[rustc_const_stable(feature = "const_manually_drop", since = "1.36.0")]
     #[inline(always)]
index 9107c570a897097bf542773ae5c58c402a020805..6d8ed2f4ffb1a158e9e62f845259d185e57f11d5 100644 (file)
 #[rustc_const_stable(feature = "const_forget", since = "1.46.0")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub const fn forget<T>(t: T) {
-    ManuallyDrop::new(t);
+    let _ = ManuallyDrop::new(t);
 }
 
 /// Like [`forget`], but also accepts unsized values.
index c83c6b0eccbc74a1d608bbc21f2aa86410c754ed..ed6202bb82f0fc45e7c4a9605353f7436f2e7a13 100644 (file)
@@ -166,10 +166,6 @@ fn from_str(src: &str) -> Result<Self, ParseFloatError> {
 ///
 /// This error is used as the error type for the [`FromStr`] implementation
 /// for [`f32`] and [`f64`].
-///
-/// [`FromStr`]: ../str/trait.FromStr.html
-/// [`f32`]: ../../std/primitive.f32.html
-/// [`f64`]: ../../std/primitive.f64.html
 #[derive(Debug, Clone, PartialEq, Eq)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct ParseFloatError {
index ccabd66aaf6ebd738c13b9770dce99f299f84d91..2eaf7601e54dece53b4e4b4171fc5938f750c7e0 100644 (file)
@@ -36,7 +36,7 @@
 /// ```
 ///
 /// [slicing index]: crate::slice::SliceIndex
-#[cfg_attr(not(bootstrap), lang = "RangeFull")]
+#[lang = "RangeFull"]
 #[doc(alias = "..")]
 #[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -69,7 +69,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 /// assert_eq!(arr[1.. 3], [  1,2    ]);  // Range
 /// assert_eq!(arr[1..=3], [  1,2,3  ]);
 /// ```
-#[cfg_attr(not(bootstrap), lang = "Range")]
+#[lang = "Range"]
 #[doc(alias = "..")]
 #[derive(Clone, Default, PartialEq, Eq, Hash)] // not Copy -- see #27186
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -125,8 +125,6 @@ pub fn contains<U>(&self, item: &U) -> bool
     /// # Examples
     ///
     /// ```
-    /// #![feature(range_is_empty)]
-    ///
     /// assert!(!(3..5).is_empty());
     /// assert!( (3..3).is_empty());
     /// assert!( (3..2).is_empty());
@@ -135,13 +133,11 @@ pub fn contains<U>(&self, item: &U) -> bool
     /// The range is empty if either side is incomparable:
     ///
     /// ```
-    /// #![feature(range_is_empty)]
-    ///
     /// assert!(!(3.0..5.0).is_empty());
     /// assert!( (3.0..f32::NAN).is_empty());
     /// assert!( (f32::NAN..5.0).is_empty());
     /// ```
-    #[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")]
+    #[stable(feature = "range_is_empty", since = "1.47.0")]
     pub fn is_empty(&self) -> bool {
         !(self.start < self.end)
     }
@@ -176,7 +172,7 @@ pub fn is_empty(&self) -> bool {
 /// assert_eq!(arr[1.. 3], [  1,2    ]);
 /// assert_eq!(arr[1..=3], [  1,2,3  ]);
 /// ```
-#[cfg_attr(not(bootstrap), lang = "RangeFrom")]
+#[lang = "RangeFrom"]
 #[doc(alias = "..")]
 #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -257,7 +253,7 @@ pub fn contains<U>(&self, item: &U) -> bool
 /// ```
 ///
 /// [slicing index]: crate::slice::SliceIndex
-#[cfg_attr(not(bootstrap), lang = "RangeTo")]
+#[lang = "RangeTo"]
 #[doc(alias = "..")]
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -326,7 +322,7 @@ pub fn contains<U>(&self, item: &U) -> bool
 /// assert_eq!(arr[1.. 3], [  1,2    ]);
 /// assert_eq!(arr[1..=3], [  1,2,3  ]);  // RangeInclusive
 /// ```
-#[cfg_attr(not(bootstrap), lang = "RangeInclusive")]
+#[lang = "RangeInclusive"]
 #[doc(alias = "..=")]
 #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
 #[stable(feature = "inclusive_range", since = "1.26.0")]
@@ -358,7 +354,7 @@ impl<Idx> RangeInclusive<Idx> {
     ///
     /// assert_eq!(3..=5, RangeInclusive::new(3, 5));
     /// ```
-    #[cfg_attr(not(bootstrap), lang = "range_inclusive_new")]
+    #[lang = "range_inclusive_new"]
     #[stable(feature = "inclusive_range_methods", since = "1.27.0")]
     #[inline]
     #[rustc_promotable]
@@ -481,8 +477,6 @@ pub fn contains<U>(&self, item: &U) -> bool
     /// # Examples
     ///
     /// ```
-    /// #![feature(range_is_empty)]
-    ///
     /// assert!(!(3..=5).is_empty());
     /// assert!(!(3..=3).is_empty());
     /// assert!( (3..=2).is_empty());
@@ -491,8 +485,6 @@ pub fn contains<U>(&self, item: &U) -> bool
     /// The range is empty if either side is incomparable:
     ///
     /// ```
-    /// #![feature(range_is_empty)]
-    ///
     /// assert!(!(3.0..=5.0).is_empty());
     /// assert!( (3.0..=f32::NAN).is_empty());
     /// assert!( (f32::NAN..=5.0).is_empty());
@@ -501,14 +493,12 @@ pub fn contains<U>(&self, item: &U) -> bool
     /// This method returns `true` after iteration has finished:
     ///
     /// ```
-    /// #![feature(range_is_empty)]
-    ///
     /// let mut r = 3..=5;
     /// for _ in r.by_ref() {}
     /// // Precise field values are unspecified here
     /// assert!(r.is_empty());
     /// ```
-    #[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")]
+    #[stable(feature = "range_is_empty", since = "1.47.0")]
     #[inline]
     pub fn is_empty(&self) -> bool {
         self.exhausted || !(self.start <= self.end)
@@ -553,7 +543,7 @@ pub fn is_empty(&self) -> bool {
 /// ```
 ///
 /// [slicing index]: crate::slice::SliceIndex
-#[cfg_attr(not(bootstrap), lang = "RangeToInclusive")]
+#[lang = "RangeToInclusive"]
 #[doc(alias = "..=")]
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 #[stable(feature = "inclusive_range", since = "1.26.0")]
index e6b05cc641e35308b4a2e4b119ccb52ae8f46fc6..3bede5699781c0eb464f163e221bb8c8c22f9bdb 100644 (file)
@@ -43,19 +43,19 @@ pub trait Try {
     /// in the return type of the enclosing scope (which must itself implement
     /// `Try`). Specifically, the value `X::from_error(From::from(e))`
     /// is returned, where `X` is the return type of the enclosing function.
-    #[cfg_attr(not(bootstrap), lang = "into_result")]
+    #[lang = "into_result"]
     #[unstable(feature = "try_trait", issue = "42327")]
     fn into_result(self) -> Result<Self::Ok, Self::Error>;
 
     /// Wrap an error value to construct the composite result. For example,
     /// `Result::Err(x)` and `Result::from_error(x)` are equivalent.
-    #[cfg_attr(not(bootstrap), lang = "from_error")]
+    #[lang = "from_error"]
     #[unstable(feature = "try_trait", issue = "42327")]
     fn from_error(v: Self::Error) -> Self;
 
     /// Wrap an OK value to construct the composite result. For example,
     /// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent.
-    #[cfg_attr(not(bootstrap), lang = "from_ok")]
+    #[lang = "from_ok"]
     #[unstable(feature = "try_trait", issue = "42327")]
     fn from_ok(v: Self::Ok) -> Self;
 }
index b6aa2c6697123ec6562e8fdf6ce5f77b224200e1..dd7556758be7d17347b7fbc880f591d7f8298100 100644 (file)
 #[stable(feature = "rust1", since = "1.0.0")]
 pub enum Option<T> {
     /// No value
-    #[cfg_attr(not(bootstrap), lang = "None")]
+    #[lang = "None"]
     #[stable(feature = "rust1", since = "1.0.0")]
     None,
     /// Some value `T`
-    #[cfg_attr(not(bootstrap), lang = "Some")]
+    #[lang = "Some"]
     #[stable(feature = "rust1", since = "1.0.0")]
     Some(#[stable(feature = "rust1", since = "1.0.0")] T),
 }
index 290aa797fd964971df6a2aebfbe3160b366a9978..8f60c4787d4590e23b40cd10d72eb3c734f2bb6e 100644 (file)
@@ -555,7 +555,7 @@ impl<P: Deref> Pin<P> {
     ///  ```
     ///
     /// [`mem::swap`]: crate::mem::swap
-    #[cfg_attr(not(bootstrap), lang = "new_unchecked")]
+    #[lang = "new_unchecked"]
     #[stable(feature = "pin", since = "1.33.0")]
     #[inline(always)]
     pub unsafe fn new_unchecked(pointer: P) -> Pin<P> {
index fc70dec16f618aeea80e257ebc4e5ae4055c49f6..d09cdb44e083760bff48bb60ce51605a0327c934 100644 (file)
@@ -836,7 +836,7 @@ pub unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize)
     /// # use std::mem::align_of;
     /// # unsafe {
     /// let x = [5u8, 6u8, 7u8, 8u8, 9u8];
-    /// let ptr = &x[n] as *const u8;
+    /// let ptr = x.as_ptr().add(n) as *const u8;
     /// let offset = ptr.align_offset(align_of::<u16>());
     /// if offset < x.len() - n - 1 {
     ///     let u16_ptr = ptr.add(offset) as *const u16;
index 2d25f21e55c761a5321765d8712fedabf2e2be38..537aa20bf1dbcb13652ad67a351cc00bb72fe654 100644 (file)
@@ -1094,7 +1094,7 @@ pub unsafe fn swap(self, with: *mut T)
     /// # use std::mem::align_of;
     /// # unsafe {
     /// let x = [5u8, 6u8, 7u8, 8u8, 9u8];
-    /// let ptr = &x[n] as *const u8;
+    /// let ptr = x.as_ptr().add(n) as *const u8;
     /// let offset = ptr.align_offset(align_of::<u16>());
     /// if offset < x.len() - n - 1 {
     ///     let u16_ptr = ptr.add(offset) as *const u16;
index ade5472717dde4d9c9228321bacc8cdebba8d549..ce0fc628e111442bd486dc98bb7085ab8a05bd53 100644 (file)
 #[stable(feature = "rust1", since = "1.0.0")]
 pub enum Result<T, E> {
     /// Contains the success value
-    #[cfg_attr(not(bootstrap), lang = "Ok")]
+    #[lang = "Ok"]
     #[stable(feature = "rust1", since = "1.0.0")]
     Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
 
     /// Contains the error value
-    #[cfg_attr(not(bootstrap), lang = "Err")]
+    #[lang = "Err"]
     #[stable(feature = "rust1", since = "1.0.0")]
     Err(#[stable(feature = "rust1", since = "1.0.0")] E),
 }
index fea396d20ff4b8259fa66e6d1a6633ba4d0c8382..9383e7c45fa55b6c29b1ebd56346842b4b844b09 100644 (file)
@@ -10,7 +10,7 @@
 #[stable(feature = "futures_api", since = "1.36.0")]
 pub enum Poll<T> {
     /// Represents that a value is immediately ready.
-    #[cfg_attr(not(bootstrap), lang = "Ready")]
+    #[lang = "Ready"]
     #[stable(feature = "futures_api", since = "1.36.0")]
     Ready(#[stable(feature = "futures_api", since = "1.36.0")] T),
 
@@ -19,7 +19,7 @@ pub enum Poll<T> {
     /// When a function returns `Pending`, the function *must* also
     /// ensure that the current task is scheduled to be awoken when
     /// progress can be made.
-    #[cfg_attr(not(bootstrap), lang = "Pending")]
+    #[lang = "Pending"]
     #[stable(feature = "futures_api", since = "1.36.0")]
     Pending,
 }
index 904e3f7284049b982ae68af411759276dc6eb3b4..81e621318e14184b3903e7ea6011338ae4a2a4e9 100644 (file)
@@ -17,7 +17,6 @@
 #![feature(try_find)]
 #![feature(is_sorted)]
 #![feature(pattern)]
-#![feature(range_is_empty)]
 #![feature(raw)]
 #![feature(sort_internals)]
 #![feature(slice_partition_at_index)]
index cf52091f609edc90498e9e75bd5dd2954d485d1c..8ca25da54a6c8ae7ec7be60e6e7090bd3a17abfd 100644 (file)
@@ -17,6 +17,7 @@
 #![feature(panic_runtime)]
 #![feature(staged_api)]
 #![feature(rustc_attrs)]
+#![feature(llvm_asm)]
 
 use core::any::Any;
 
     unreachable!()
 }
 
-// "Leak" the payload and shim to the relevant abort on the platform in
-// question.
-//
-// For Unix we just use `abort` from libc as it'll trigger debuggers, core
-// dumps, etc, as one might expect. On Windows, however, the best option we have
-// is the `__fastfail` intrinsics, but that's unfortunately not defined in LLVM,
-// and the `RaiseFailFastException` function isn't available until Windows 7
-// which would break compat with XP. For now just use `intrinsics::abort` which
-// will kill us with an illegal instruction, which will do a good enough job for
-// now hopefully.
+// "Leak" the payload and shim to the relevant abort on the platform in question.
 #[rustc_std_internal_symbol]
 pub unsafe extern "C" fn __rust_start_panic(_payload: usize) -> u32 {
     abort();
@@ -55,6 +47,21 @@ unsafe fn abort() -> ! {
                 }
                 __rust_abort();
             }
+        } else if #[cfg(all(windows, any(target_arch = "x86", target_arch = "x86_64")))] {
+            // On Windows, use the processor-specific __fastfail mechanism. In Windows 8
+            // and later, this will terminate the process immediately without running any
+            // in-process exception handlers. In earlier versions of Windows, this
+            // sequence of instructions will be treated as an access violation,
+            // terminating the process but without necessarily bypassing all exception
+            // handlers.
+            //
+            // https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail
+            //
+            // Note: this is the same implementation as in libstd's `abort_internal`
+            unsafe fn abort() -> ! {
+                llvm_asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT
+                core::intrinsics::unreachable();
+            }
         } else {
             unsafe fn abort() -> ! {
                 core::intrinsics::abort();
@@ -110,6 +117,17 @@ pub extern "C" fn rust_eh_personality(
         1 // `ExceptionContinueSearch`
     }
 
+    // Similar to above, this corresponds to the `eh_catch_typeinfo` lang item
+    // that's only used on Emscripten currently.
+    //
+    // Since panics don't generate exceptions and foreign exceptions are
+    // currently UB with -C panic=abort (although this may be subject to
+    // change), any catch_unwind calls will never use this typeinfo.
+    #[rustc_std_internal_symbol]
+    #[allow(non_upper_case_globals)]
+    #[cfg(target_os = "emscripten")]
+    static rust_eh_catch_typeinfo: [usize; 2] = [0; 2];
+
     // These two are called by our startup objects on i686-pc-windows-gnu, but
     // they don't need to do anything so the bodies are nops.
     #[rustc_std_internal_symbol]
index 302478cfac8f5ebf1fffc3516235a3d79d9e2861..8ce4dcd2acd9c02f6d4259adfbf0aaef8b223cce 100644 (file)
@@ -51,11 +51,7 @@ pub enum EHAction {
 
 pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
 
-pub unsafe fn find_eh_action(
-    lsda: *const u8,
-    context: &EHContext<'_>,
-    foreign_exception: bool,
-) -> Result<EHAction, ()> {
+pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
     if lsda.is_null() {
         return Ok(EHAction::None);
     }
@@ -98,7 +94,7 @@ pub unsafe fn find_eh_action(
                     return Ok(EHAction::None);
                 } else {
                     let lpad = lpad_base + cs_lpad;
-                    return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
+                    return Ok(interpret_cs_action(cs_action, lpad));
                 }
             }
         }
@@ -123,21 +119,17 @@ pub unsafe fn find_eh_action(
                 // Can never have null landing pad for sjlj -- that would have
                 // been indicated by a -1 call site index.
                 let lpad = (cs_lpad + 1) as usize;
-                return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
+                return Ok(interpret_cs_action(cs_action, lpad));
             }
         }
     }
 }
 
-fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction {
+fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction {
     if cs_action == 0 {
         // If cs_action is 0 then this is a cleanup (Drop::drop). We run these
         // for both Rust panics and foreign exceptions.
         EHAction::Cleanup(lpad)
-    } else if foreign_exception {
-        // catch_unwind should not catch foreign exceptions, only Rust panics.
-        // Instead just continue unwinding.
-        EHAction::None
     } else {
         // Stop unwinding Rust panics at catch_unwind.
         EHAction::Catch(lpad)
index a0bdb1481c6b2460c2c3eb2140adb46ccedce5c5..e428f2fdaaa39a6d9db9add819b59ccf50ff7857 100644 (file)
@@ -8,8 +8,10 @@
 
 use alloc::boxed::Box;
 use core::any::Any;
+use core::intrinsics;
 use core::mem;
 use core::ptr;
+use core::sync::atomic::{AtomicBool, Ordering};
 use libc::{self, c_int};
 use unwind as uw;
 
@@ -47,6 +49,11 @@ unsafe impl Sync for TypeInfo {}
 };
 
 struct Exception {
+    // This is necessary because C++ code can capture our execption with
+    // std::exception_ptr and rethrow it multiple times, possibly even in
+    // another thread.
+    caught: AtomicBool,
+
     // This needs to be an Option because the object's lifetime follows C++
     // semantics: when catch_unwind moves the Box out of the exception it must
     // still leave the exception object in a valid state because its destructor
@@ -55,11 +62,27 @@ struct Exception {
 }
 
 pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
-    assert!(!ptr.is_null());
-    let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void) as *mut Exception;
-    let ex = (*adjusted_ptr).data.take();
+    // intrinsics::try actually gives us a pointer to this structure.
+    #[repr(C)]
+    struct CatchData {
+        ptr: *mut u8,
+        is_rust_panic: bool,
+    }
+    let catch_data = &*(ptr as *mut CatchData);
+
+    let adjusted_ptr = __cxa_begin_catch(catch_data.ptr as *mut libc::c_void) as *mut Exception;
+    let out = if catch_data.is_rust_panic {
+        let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst);
+        if was_caught {
+            // Since cleanup() isn't allowed to panic, we just abort instead.
+            intrinsics::abort();
+        }
+        (*adjusted_ptr).data.take().unwrap()
+    } else {
+        super::__rust_foreign_exception();
+    };
     __cxa_end_catch();
-    ex.unwrap()
+    out
 }
 
 pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
@@ -68,25 +91,16 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
     if exception.is_null() {
         return uw::_URC_FATAL_PHASE1_ERROR as u32;
     }
-    ptr::write(exception, Exception { data: Some(data) });
+    ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) });
     __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup);
 }
 
-// On WASM and ARM, the destructor returns the pointer to the object.
-cfg_if::cfg_if! {
-    if #[cfg(any(target_arch = "arm", target_arch = "wasm32"))] {
-        type DestructorRet = *mut libc::c_void;
-    } else {
-        type DestructorRet = ();
-    }
-}
-extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> DestructorRet {
+extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void {
     unsafe {
         if let Some(b) = (ptr as *mut Exception).read().data {
             drop(b);
             super::__rust_drop_panic();
         }
-        #[cfg(any(target_arch = "arm", target_arch = "wasm32"))]
         ptr
     }
 }
@@ -109,7 +123,7 @@ extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> DestructorRet {
     fn __cxa_throw(
         thrown_exception: *mut libc::c_void,
         tinfo: *const TypeInfo,
-        dest: extern "C" fn(*mut libc::c_void) -> DestructorRet,
+        dest: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void,
     ) -> !;
     fn __gxx_personality_v0(
         version: c_int,
index f5d83c21da0684170d40bd13eb85669dda70fb86..85a2a18947db211345d438824dac13e254f7b2fc 100644 (file)
@@ -73,8 +73,14 @@ extern "C" fn exception_cleanup(
 }
 
 pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
-    let exception = Box::from_raw(ptr as *mut Exception);
-    exception.cause
+    let exception = ptr as *mut uw::_Unwind_Exception;
+    if (*exception).exception_class != rust_exception_class() {
+        uw::_Unwind_DeleteException(exception);
+        super::__rust_foreign_exception();
+    } else {
+        let exception = Box::from_raw(exception as *mut Exception);
+        exception.cause
+    }
 }
 
 // Rust's exception class identifier.  This is used by personality routines to
@@ -164,9 +170,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
             // _Unwind_Context in our libunwind bindings and fetch the required data from there
             // directly, bypassing DWARF compatibility functions.
 
-            let exception_class = (*exception_object).exception_class;
-            let foreign_exception = exception_class != rust_exception_class();
-            let eh_action = match find_eh_action(context, foreign_exception) {
+            let eh_action = match find_eh_action(context) {
                 Ok(action) => action,
                 Err(_) => return uw::_URC_FAILURE,
             };
@@ -221,15 +225,14 @@ fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception,
         // and indirectly on Windows x86_64 via SEH.
         unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
                                                       actions: uw::_Unwind_Action,
-                                                      exception_class: uw::_Unwind_Exception_Class,
+                                                      _exception_class: uw::_Unwind_Exception_Class,
                                                       exception_object: *mut uw::_Unwind_Exception,
                                                       context: *mut uw::_Unwind_Context)
                                                       -> uw::_Unwind_Reason_Code {
             if version != 1 {
                 return uw::_URC_FATAL_PHASE1_ERROR;
             }
-            let foreign_exception = exception_class != rust_exception_class();
-            let eh_action = match find_eh_action(context, foreign_exception) {
+            let eh_action = match find_eh_action(context) {
                 Ok(action) => action,
                 Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
             };
@@ -293,10 +296,7 @@ fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception,
     }
 }
 
-unsafe fn find_eh_action(
-    context: *mut uw::_Unwind_Context,
-    foreign_exception: bool,
-) -> Result<EHAction, ()> {
+unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
     let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
     let mut ip_before_instr: c_int = 0;
     let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
@@ -308,7 +308,7 @@ unsafe fn find_eh_action(
         get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
         get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
     };
-    eh::find_eh_action(lsda, &eh_context, foreign_exception)
+    eh::find_eh_action(lsda, &eh_context)
 }
 
 // Frame unwind info registration
index 7d14893c4cc2e45a1e2c908dce780f8582eceb3a..6f31e6dcae70d7b40f406b2c863034ffbd77a8a2 100644 (file)
@@ -65,7 +65,7 @@
         // - os=none ("bare metal" targets)
         // - os=uefi
         // - nvptx64-nvidia-cuda
-        // - avr-unknown-unknown
+        // - arch=avr
         #[path = "dummy.rs"]
         mod real_imp;
     }
@@ -88,6 +88,9 @@
     /// Handler in libstd called when a panic object is dropped outside of
     /// `catch_unwind`.
     fn __rust_drop_panic() -> !;
+
+    /// Handler in libstd called when a foreign exception is caught.
+    fn __rust_foreign_exception() -> !;
 }
 
 mod dwarf;
index 1f812f8df6122092ceeb2026bb1ed045ac1889a6..eca169373f39fbbf09793785ce804b7832bfc23a 100644 (file)
@@ -309,15 +309,21 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
 
     extern "system" {
         #[unwind(allowed)]
-        pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
+        fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
     }
 
     _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
 }
 
 pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
-    let exception = &mut *(payload as *mut Exception);
-    exception.data.take().unwrap()
+    // A NULL payload here means that we got here from the catch (...) of
+    // __rust_try. This happens when a non-Rust foreign exception is caught.
+    if payload.is_null() {
+        super::__rust_foreign_exception();
+    } else {
+        let exception = &mut *(payload as *mut Exception);
+        exception.data.take().unwrap()
+    }
 }
 
 // This is required by the compiler to exist (e.g., it's a lang item), but
index 0605ca4d94f31ac01e237e99fea31885e0bffa5f..2a5d5853fec64b4c93c3bcb73fa89185419adb13 100644 (file)
@@ -20,6 +20,7 @@ fn main() {
         "InstrProfilingMergeFile.c",
         "InstrProfilingNameVar.c",
         "InstrProfilingPlatformDarwin.c",
+        "InstrProfilingPlatformFuchsia.c",
         "InstrProfilingPlatformLinux.c",
         "InstrProfilingPlatformOther.c",
         "InstrProfilingPlatformWindows.c",
@@ -65,7 +66,7 @@ fn main() {
     // This should be a pretty good heuristic for when to set
     // COMPILER_RT_HAS_ATOMICS
     if env::var_os("CARGO_CFG_TARGET_HAS_ATOMIC")
-        .map(|features| features.to_string_lossy().to_lowercase().contains("cas"))
+        .map(|features| features.to_string_lossy().to_lowercase().contains("ptr"))
         .unwrap_or(false)
     {
         cfg.define("COMPILER_RT_HAS_ATOMICS", Some("1"));
index ef0ef415b4cbab4bbc00d7dbf5e8b86b00c627fd..9cc69287080083a71a552b9e70570b164225d357 100644 (file)
@@ -17,7 +17,7 @@ panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core" }
 libc = { version = "0.2.74", default-features = false, features = ['rustc-dep-of-std'] }
-compiler_builtins = { version = "0.1.32" }
+compiler_builtins = { version = "0.1.35" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
 hashbrown = { version = "0.8.1", default-features = false, features = ['rustc-dep-of-std'] }
index 04bfed12153ec42a3f3e69893fd2a2ffe80a3a0e..a787e6d43fc99d74b8bd099b734920872a8a7d9d 100644 (file)
@@ -83,7 +83,7 @@ fn main() {
         // - os=none ("bare metal" targets)
         // - mipsel-sony-psp
         // - nvptx64-nvidia-cuda
-        // - avr-unknown-unknown
+        // - arch=avr
         // - tvos (aarch64-apple-tvos, x86_64-apple-tvos)
         // - uefi (x86_64-unknown-uefi, i686-unknown-uefi)
         // - JSON targets
index b4009c86419d5148e3fb6f79a17e6d43b1d692e0..64d8edf33bd3bcf393e9d94e028f3ea07849a8e0 100644 (file)
@@ -154,36 +154,45 @@ fn alloc_impl(&mut self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>,
     unsafe fn grow_impl(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
         zeroed: bool,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         debug_assert!(
-            new_size >= layout.size(),
-            "`new_size` must be greater than or equal to `layout.size()`"
+            new_layout.size() >= old_layout.size(),
+            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
         );
 
-        match layout.size() {
-            // SAFETY: the caller must ensure that the `new_size` does not overflow.
-            // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-            0 => unsafe {
-                let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
-                self.alloc_impl(new_layout, zeroed)
-            },
+        match old_layout.size() {
+            0 => self.alloc_impl(new_layout, zeroed),
 
             // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size`
             // as required by safety conditions. Other conditions must be upheld by the caller
-            old_size => unsafe {
-                // `realloc` probably checks for `new_size >= size` or something similar.
-                intrinsics::assume(new_size >= layout.size());
+            old_size if old_layout.align() == new_layout.align() => unsafe {
+                let new_size = new_layout.size();
+
+                // `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
+                intrinsics::assume(new_size >= old_layout.size());
 
-                let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
+                let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
                 if zeroed {
                     raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
                 }
                 Ok(NonNull::slice_from_raw_parts(ptr, new_size))
             },
+
+            // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
+            // both the old and new memory allocation are valid for reads and writes for `old_size`
+            // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+            // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+            // for `dealloc` must be upheld by the caller.
+            old_size => unsafe {
+                let new_ptr = self.alloc_impl(new_layout, zeroed)?;
+                ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
+                self.dealloc(ptr, old_layout);
+                Ok(new_ptr)
+            },
         }
     }
 }
@@ -215,52 +224,64 @@ unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
     unsafe fn grow(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: all conditions must be upheld by the caller
-        unsafe { self.grow_impl(ptr, layout, new_size, false) }
+        unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
     }
 
     #[inline]
     unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: all conditions must be upheld by the caller
-        unsafe { self.grow_impl(ptr, layout, new_size, true) }
+        unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
     }
 
     #[inline]
     unsafe fn shrink(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         debug_assert!(
-            new_size <= layout.size(),
-            "`new_size` must be smaller than or equal to `layout.size()`"
+            new_layout.size() <= old_layout.size(),
+            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
         );
 
-        match new_size {
+        match new_layout.size() {
             // SAFETY: conditions must be upheld by the caller
             0 => unsafe {
-                self.dealloc(ptr, layout);
-                Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0))
+                self.dealloc(ptr, old_layout);
+                Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0))
             },
 
             // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
-            new_size => unsafe {
-                // `realloc` probably checks for `new_size <= size` or something similar.
-                intrinsics::assume(new_size <= layout.size());
+            new_size if old_layout.align() == new_layout.align() => unsafe {
+                // `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
+                intrinsics::assume(new_size <= old_layout.size());
 
-                let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
+                let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
                 Ok(NonNull::slice_from_raw_parts(ptr, new_size))
             },
+
+            // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
+            // both the old and new memory allocation are valid for reads and writes for `new_size`
+            // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+            // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+            // for `dealloc` must be upheld by the caller.
+            new_size => unsafe {
+                let new_ptr = self.alloc(new_layout)?;
+                ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
+                self.dealloc(ptr, old_layout);
+                Ok(new_ptr)
+            },
         }
     }
 }
index 84e686c2fef818ae7211a3eb5689600db05eaf52..d3b0f8ceb68a64e8b8981cb840f6cf9f0bbd116b 100644 (file)
 /// themselves through the [`Display`] and [`Debug`] traits, and may provide
 /// cause chain information:
 ///
-/// The [`source`] method is generally used when errors cross "abstraction
-/// boundaries". If one module must report an error that is caused by an error
-/// from a lower-level module, it can allow access to that error via the
-/// [`source`] method. This makes it possible for the high-level module to
-/// provide its own errors while also revealing some of the implementation for
-/// debugging via [`source`] chains.
+/// [`Error::source()`] is generally used when errors cross
+/// "abstraction boundaries". If one module must report an error that is caused
+/// by an error from a lower-level module, it can allow accessing that error
+/// via [`Error::source()`]. This makes it possible for the high-level
+/// module to provide its own errors while also revealing some of the
+/// implementation for debugging via `source` chains.
 ///
 /// [`Result<T, E>`]: Result
-/// [`source`]: Error::source
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Error: Debug + Display {
     /// The lower-level source of this error, if any.
@@ -636,7 +635,7 @@ pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn E
     }
 
     /// Returns an iterator starting with the current error and continuing with
-    /// recursively calling [`source`].
+    /// recursively calling [`Error::source`].
     ///
     /// If you want to omit the current error and only use its sources,
     /// use `skip(1)`.
@@ -686,8 +685,6 @@ pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn E
     /// assert!(iter.next().is_none());
     /// assert!(iter.next().is_none());
     /// ```
-    ///
-    /// [`source`]: Error::source
     #[unstable(feature = "error_iter", issue = "58520")]
     #[inline]
     pub fn chain(&self) -> Chain<'_> {
index f3aadf29b2f2b403360ac535cc9c4d3fc0822a97..ec3c69dd6160a21c581011b3eb3d743bf1d1a7e5 100644 (file)
@@ -517,33 +517,81 @@ pub fn with_capacity(capacity: usize, inner: W) -> BufWriter<W> {
         BufWriter { inner: Some(inner), buf: Vec::with_capacity(capacity), panicked: false }
     }
 
+    /// Send data in our local buffer into the inner writer, looping as
+    /// necessary until either it's all been sent or an error occurs.
+    ///
+    /// Because all the data in the buffer has been reported to our owner as
+    /// "successfully written" (by returning nonzero success values from
+    /// `write`), any 0-length writes from `inner` must be reported as i/o
+    /// errors from this method.
     fn flush_buf(&mut self) -> io::Result<()> {
-        let mut written = 0;
-        let len = self.buf.len();
-        let mut ret = Ok(());
-        while written < len {
+        /// Helper struct to ensure the buffer is updated after all the writes
+        /// are complete. It tracks the number of written bytes and drains them
+        /// all from the front of the buffer when dropped.
+        struct BufGuard<'a> {
+            buffer: &'a mut Vec<u8>,
+            written: usize,
+        }
+
+        impl<'a> BufGuard<'a> {
+            fn new(buffer: &'a mut Vec<u8>) -> Self {
+                Self { buffer, written: 0 }
+            }
+
+            /// The unwritten part of the buffer
+            fn remaining(&self) -> &[u8] {
+                &self.buffer[self.written..]
+            }
+
+            /// Flag some bytes as removed from the front of the buffer
+            fn consume(&mut self, amt: usize) {
+                self.written += amt;
+            }
+
+            /// true if all of the bytes have been written
+            fn done(&self) -> bool {
+                self.written >= self.buffer.len()
+            }
+        }
+
+        impl Drop for BufGuard<'_> {
+            fn drop(&mut self) {
+                if self.written > 0 {
+                    self.buffer.drain(..self.written);
+                }
+            }
+        }
+
+        let mut guard = BufGuard::new(&mut self.buf);
+        let inner = self.inner.as_mut().unwrap();
+        while !guard.done() {
             self.panicked = true;
-            let r = self.inner.as_mut().unwrap().write(&self.buf[written..]);
+            let r = inner.write(guard.remaining());
             self.panicked = false;
 
             match r {
                 Ok(0) => {
-                    ret =
-                        Err(Error::new(ErrorKind::WriteZero, "failed to write the buffered data"));
-                    break;
+                    return Err(Error::new(
+                        ErrorKind::WriteZero,
+                        "failed to write the buffered data",
+                    ));
                 }
-                Ok(n) => written += n,
+                Ok(n) => guard.consume(n),
                 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
-                Err(e) => {
-                    ret = Err(e);
-                    break;
-                }
+                Err(e) => return Err(e),
             }
         }
-        if written > 0 {
-            self.buf.drain(..written);
-        }
-        ret
+        Ok(())
+    }
+
+    /// Buffer some data without flushing it, regardless of the size of the
+    /// data. Writes as much as possible without exceeding capacity. Returns
+    /// the number of bytes written.
+    fn write_to_buf(&mut self, buf: &[u8]) -> usize {
+        let available = self.buf.capacity() - self.buf.len();
+        let amt_to_buffer = available.min(buf.len());
+        self.buf.extend_from_slice(&buf[..amt_to_buffer]);
+        amt_to_buffer
     }
 
     /// Gets a reference to the underlying writer.
@@ -656,13 +704,35 @@ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         if self.buf.len() + buf.len() > self.buf.capacity() {
             self.flush_buf()?;
         }
+        // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
         if buf.len() >= self.buf.capacity() {
             self.panicked = true;
             let r = self.get_mut().write(buf);
             self.panicked = false;
             r
         } else {
-            self.buf.write(buf)
+            self.buf.extend_from_slice(buf);
+            Ok(buf.len())
+        }
+    }
+
+    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+        // Normally, `write_all` just calls `write` in a loop. We can do better
+        // by calling `self.get_mut().write_all()` directly, which avoids
+        // round trips through the buffer in the event of a series of partial
+        // writes in some circumstances.
+        if self.buf.len() + buf.len() > self.buf.capacity() {
+            self.flush_buf()?;
+        }
+        // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
+        if buf.len() >= self.buf.capacity() {
+            self.panicked = true;
+            let r = self.get_mut().write_all(buf);
+            self.panicked = false;
+            r
+        } else {
+            self.buf.extend_from_slice(buf);
+            Ok(())
         }
     }
 
@@ -671,13 +741,15 @@ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         if self.buf.len() + total_len > self.buf.capacity() {
             self.flush_buf()?;
         }
+        // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
         if total_len >= self.buf.capacity() {
             self.panicked = true;
             let r = self.get_mut().write_vectored(bufs);
             self.panicked = false;
             r
         } else {
-            self.buf.write_vectored(bufs)
+            bufs.iter().for_each(|b| self.buf.extend_from_slice(b));
+            Ok(total_len)
         }
     }
 
@@ -709,7 +781,8 @@ impl<W: Write + Seek> Seek for BufWriter<W> {
     ///
     /// Seeking always writes out the internal buffer before seeking.
     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
-        self.flush_buf().and_then(|_| self.get_mut().seek(pos))
+        self.flush_buf()?;
+        self.get_mut().seek(pos)
     }
 }
 
@@ -816,6 +889,274 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
+/// Private helper struct for implementing the line-buffered writing logic.
+/// This shim temporarily wraps a BufWriter, and uses its internals to
+/// implement a line-buffered writer (specifically by using the internal
+/// methods like write_to_buf and flush_buf). In this way, a more
+/// efficient abstraction can be created than one that only had access to
+/// `write` and `flush`, without needlessly duplicating a lot of the
+/// implementation details of BufWriter. This also allows existing
+/// `BufWriters` to be temporarily given line-buffering logic; this is what
+/// enables Stdout to be alternately in line-buffered or block-buffered mode.
+#[derive(Debug)]
+pub(super) struct LineWriterShim<'a, W: Write> {
+    buffer: &'a mut BufWriter<W>,
+}
+
+impl<'a, W: Write> LineWriterShim<'a, W> {
+    pub fn new(buffer: &'a mut BufWriter<W>) -> Self {
+        Self { buffer }
+    }
+
+    /// Get a mutable reference to the inner writer (that is, the writer
+    /// wrapped by the BufWriter). Be careful with this writer, as writes to
+    /// it will bypass the buffer.
+    fn inner_mut(&mut self) -> &mut W {
+        self.buffer.get_mut()
+    }
+
+    /// Get the content currently buffered in self.buffer
+    fn buffered(&self) -> &[u8] {
+        self.buffer.buffer()
+    }
+
+    /// Flush the buffer iff the last byte is a newline (indicating that an
+    /// earlier write only succeeded partially, and we want to retry flushing
+    /// the buffered line before continuing with a subsequent write)
+    fn flush_if_completed_line(&mut self) -> io::Result<()> {
+        match self.buffered().last().copied() {
+            Some(b'\n') => self.buffer.flush_buf(),
+            _ => Ok(()),
+        }
+    }
+}
+
+impl<'a, W: Write> Write for LineWriterShim<'a, W> {
+    /// Write some data into this BufReader with line buffering. This means
+    /// that, if any newlines are present in the data, the data up to the last
+    /// newline is sent directly to the underlying writer, and data after it
+    /// is buffered. Returns the number of bytes written.
+    ///
+    /// This function operates on a "best effort basis"; in keeping with the
+    /// convention of `Write::write`, it makes at most one attempt to write
+    /// new data to the underlying writer. If that write only reports a partial
+    /// success, the remaining data will be buffered.
+    ///
+    /// Because this function attempts to send completed lines to the underlying
+    /// writer, it will also flush the existing buffer if it ends with a
+    /// newline, even if the incoming data does not contain any newlines.
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        let newline_idx = match memchr::memrchr(b'\n', buf) {
+            // If there are no new newlines (that is, if this write is less than
+            // one line), just do a regular buffered write (which may flush if
+            // we exceed the inner buffer's size)
+            None => {
+                self.flush_if_completed_line()?;
+                return self.buffer.write(buf);
+            }
+            // Otherwise, arrange for the lines to be written directly to the
+            // inner writer.
+            Some(newline_idx) => newline_idx + 1,
+        };
+
+        // Flush existing content to prepare for our write. We have to do this
+        // before attempting to write `buf` in order to maintain consistency;
+        // if we add `buf` to the buffer then try to flush it all at once,
+        // we're obligated to return Ok(), which would mean suppressing any
+        // errors that occur during flush.
+        self.buffer.flush_buf()?;
+
+        // This is what we're going to try to write directly to the inner
+        // writer. The rest will be buffered, if nothing goes wrong.
+        let lines = &buf[..newline_idx];
+
+        // Write `lines` directly to the inner writer. In keeping with the
+        // `write` convention, make at most one attempt to add new (unbuffered)
+        // data. Because this write doesn't touch the BufWriter state directly,
+        // and the buffer is known to be empty, we don't need to worry about
+        // self.buffer.panicked here.
+        let flushed = self.inner_mut().write(lines)?;
+
+        // If buffer returns Ok(0), propagate that to the caller without
+        // doing additional buffering; otherwise we're just guaranteeing
+        // an "ErrorKind::WriteZero" later.
+        if flushed == 0 {
+            return Ok(0);
+        }
+
+        // Now that the write has succeeded, buffer the rest (or as much of
+        // the rest as possible). If there were any unwritten newlines, we
+        // only buffer out to the last unwritten newline that fits in the
+        // buffer; this helps prevent flushing partial lines on subsequent
+        // calls to LineWriterShim::write.
+
+        // Handle the cases in order of most-common to least-common, under
+        // the presumption that most writes succeed in totality, and that most
+        // writes are smaller than the buffer.
+        // - Is this a partial line (ie, no newlines left in the unwritten tail)
+        // - If not, does the data out to the last unwritten newline fit in
+        //   the buffer?
+        // - If not, scan for the last newline that *does* fit in the buffer
+        let tail = if flushed >= newline_idx {
+            &buf[flushed..]
+        } else if newline_idx - flushed <= self.buffer.capacity() {
+            &buf[flushed..newline_idx]
+        } else {
+            let scan_area = &buf[flushed..];
+            let scan_area = &scan_area[..self.buffer.capacity()];
+            match memchr::memrchr(b'\n', scan_area) {
+                Some(newline_idx) => &scan_area[..newline_idx + 1],
+                None => scan_area,
+            }
+        };
+
+        let buffered = self.buffer.write_to_buf(tail);
+        Ok(flushed + buffered)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        self.buffer.flush()
+    }
+
+    /// Write some vectored data into this BufReader with line buffering. This
+    /// means that, if any newlines are present in the data, the data up to
+    /// and including the buffer containing the last newline is sent directly
+    /// to the inner writer, and the data after it is buffered. Returns the
+    /// number of bytes written.
+    ///
+    /// This function operates on a "best effort basis"; in keeping with the
+    /// convention of `Write::write`, it makes at most one attempt to write
+    /// new data to the underlying writer.
+    ///
+    /// Because this function attempts to send completed lines to the underlying
+    /// writer, it will also flush the existing buffer if it contains any
+    /// newlines.
+    ///
+    /// Because sorting through an array of `IoSlice` can be a bit convoluted,
+    /// This method differs from write in the following ways:
+    ///
+    /// - It attempts to write the full content of all the buffers up to and
+    ///   including the one containing the last newline. This means that it
+    ///   may attempt to write a partial line, that buffer has data past the
+    ///   newline.
+    /// - If the write only reports partial success, it does not attempt to
+    ///   find the precise location of the written bytes and buffer the rest.
+    ///
+    /// If the underlying vector doesn't support vectored writing, we instead
+    /// simply write the first non-empty buffer with `write`. This way, we
+    /// get the benefits of more granular partial-line handling without losing
+    /// anything in efficiency
+    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        // If there's no specialized behavior for write_vectored, just use
+        // write. This has the benefit of more granular partial-line handling.
+        if !self.is_write_vectored() {
+            return match bufs.iter().find(|buf| !buf.is_empty()) {
+                Some(buf) => self.write(buf),
+                None => Ok(0),
+            };
+        }
+
+        // Find the buffer containing the last newline
+        let last_newline_buf_idx = bufs
+            .iter()
+            .enumerate()
+            .rev()
+            .find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i));
+
+        // If there are no new newlines (that is, if this write is less than
+        // one line), just do a regular buffered write
+        let last_newline_buf_idx = match last_newline_buf_idx {
+            // No newlines; just do a normal buffered write
+            None => {
+                self.flush_if_completed_line()?;
+                return self.buffer.write_vectored(bufs);
+            }
+            Some(i) => i,
+        };
+
+        // Flush existing content to prepare for our write
+        self.buffer.flush_buf()?;
+
+        // This is what we're going to try to write directly to the inner
+        // writer. The rest will be buffered, if nothing goes wrong.
+        let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1);
+
+        // Write `lines` directly to the inner writer. In keeping with the
+        // `write` convention, make at most one attempt to add new (unbuffered)
+        // data. Because this write doesn't touch the BufWriter state directly,
+        // and the buffer is known to be empty, we don't need to worry about
+        // self.panicked here.
+        let flushed = self.inner_mut().write_vectored(lines)?;
+
+        // If inner returns Ok(0), propagate that to the caller without
+        // doing additional buffering; otherwise we're just guaranteeing
+        // an "ErrorKind::WriteZero" later.
+        if flushed == 0 {
+            return Ok(0);
+        }
+
+        // Don't try to reconstruct the exact amount written; just bail
+        // in the event of a partial write
+        let lines_len = lines.iter().map(|buf| buf.len()).sum();
+        if flushed < lines_len {
+            return Ok(flushed);
+        }
+
+        // Now that the write has succeeded, buffer the rest (or as much of the
+        // rest as possible)
+        let buffered: usize = tail
+            .iter()
+            .filter(|buf| !buf.is_empty())
+            .map(|buf| self.buffer.write_to_buf(buf))
+            .take_while(|&n| n > 0)
+            .sum();
+
+        Ok(flushed + buffered)
+    }
+
+    fn is_write_vectored(&self) -> bool {
+        self.buffer.is_write_vectored()
+    }
+
+    /// Write some data into this BufReader with line buffering. This means
+    /// that, if any newlines are present in the data, the data up to the last
+    /// newline is sent directly to the underlying writer, and data after it
+    /// is buffered.
+    ///
+    /// Because this function attempts to send completed lines to the underlying
+    /// writer, it will also flush the existing buffer if it contains any
+    /// newlines, even if the incoming data does not contain any newlines.
+    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+        match memchr::memrchr(b'\n', buf) {
+            // If there are no new newlines (that is, if this write is less than
+            // one line), just do a regular buffered write (which may flush if
+            // we exceed the inner buffer's size)
+            None => {
+                self.flush_if_completed_line()?;
+                self.buffer.write_all(buf)
+            }
+            Some(newline_idx) => {
+                let (lines, tail) = buf.split_at(newline_idx + 1);
+
+                if self.buffered().is_empty() {
+                    self.inner_mut().write_all(lines)?;
+                } else {
+                    // If there is any buffered data, we add the incoming lines
+                    // to that buffer before flushing, which saves us at least
+                    // one write call. We can't really do this with `write`,
+                    // since we can't do this *and* not suppress errors *and*
+                    // report a consistent state to the caller in a return
+                    // value, but here in write_all it's fine.
+                    self.buffer.write_all(lines)?;
+                    self.buffer.flush_buf()?;
+                }
+
+                self.buffer.write_all(tail)
+            }
+        }
+    }
+}
+
 /// Wraps a writer and buffers output to it, flushing whenever a newline
 /// (`0x0a`, `'\n'`) is detected.
 ///
@@ -881,7 +1222,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct LineWriter<W: Write> {
     inner: BufWriter<W>,
-    need_flush: bool,
 }
 
 impl<W: Write> LineWriter<W> {
@@ -922,7 +1262,7 @@ pub fn new(inner: W) -> LineWriter<W> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn with_capacity(capacity: usize, inner: W) -> LineWriter<W> {
-        LineWriter { inner: BufWriter::with_capacity(capacity, inner), need_flush: false }
+        LineWriter { inner: BufWriter::with_capacity(capacity, inner) }
     }
 
     /// Gets a reference to the underlying writer.
@@ -996,110 +1336,40 @@ pub fn get_mut(&mut self) -> &mut W {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
-        self.inner.into_inner().map_err(|IntoInnerError(buf, e)| {
-            IntoInnerError(LineWriter { inner: buf, need_flush: false }, e)
-        })
+        self.inner
+            .into_inner()
+            .map_err(|IntoInnerError(buf, e)| IntoInnerError(LineWriter { inner: buf }, e))
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<W: Write> Write for LineWriter<W> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        if self.need_flush {
-            self.flush()?;
-        }
-
-        // Find the last newline character in the buffer provided. If found then
-        // we're going to write all the data up to that point and then flush,
-        // otherwise we just write the whole block to the underlying writer.
-        let i = match memchr::memrchr(b'\n', buf) {
-            Some(i) => i,
-            None => return self.inner.write(buf),
-        };
-
-        // Ok, we're going to write a partial amount of the data given first
-        // followed by flushing the newline. After we've successfully written
-        // some data then we *must* report that we wrote that data, so future
-        // errors are ignored. We set our internal `need_flush` flag, though, in
-        // case flushing fails and we need to try it first next time.
-        let n = self.inner.write(&buf[..=i])?;
-        self.need_flush = true;
-        if self.flush().is_err() || n != i + 1 {
-            return Ok(n);
-        }
+        LineWriterShim::new(&mut self.inner).write(buf)
+    }
 
-        // At this point we successfully wrote `i + 1` bytes and flushed it out,
-        // meaning that the entire line is now flushed out on the screen. While
-        // we can attempt to finish writing the rest of the data provided.
-        // Remember though that we ignore errors here as we've successfully
-        // written data, so we need to report that.
-        match self.inner.write(&buf[i + 1..]) {
-            Ok(i) => Ok(n + i),
-            Err(_) => Ok(n),
-        }
+    fn flush(&mut self) -> io::Result<()> {
+        self.inner.flush()
     }
 
-    // Vectored writes are very similar to the writes above, but adjusted for
-    // the list of buffers that we have to write.
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        if self.need_flush {
-            self.flush()?;
-        }
+        LineWriterShim::new(&mut self.inner).write_vectored(bufs)
+    }
 
-        // Find the last newline, and failing that write the whole buffer
-        let last_newline = bufs.iter().enumerate().rev().find_map(|(i, buf)| {
-            let pos = memchr::memrchr(b'\n', buf)?;
-            Some((i, pos))
-        });
-        let (i, j) = match last_newline {
-            Some(pair) => pair,
-            None => return self.inner.write_vectored(bufs),
-        };
-        let (prefix, suffix) = bufs.split_at(i);
-        let (buf, suffix) = suffix.split_at(1);
-        let buf = &buf[0];
-
-        // Write everything up to the last newline, flushing afterwards. Note
-        // that only if we finished our entire `write_vectored` do we try the
-        // subsequent
-        // `write`
-        let mut n = 0;
-        let prefix_amt = prefix.iter().map(|i| i.len()).sum();
-        if prefix_amt > 0 {
-            n += self.inner.write_vectored(prefix)?;
-            self.need_flush = true;
-        }
-        if n == prefix_amt {
-            match self.inner.write(&buf[..=j]) {
-                Ok(m) => n += m,
-                Err(e) if n == 0 => return Err(e),
-                Err(_) => return Ok(n),
-            }
-            self.need_flush = true;
-        }
-        if self.flush().is_err() || n != j + 1 + prefix_amt {
-            return Ok(n);
-        }
+    fn is_write_vectored(&self) -> bool {
+        self.inner.is_write_vectored()
+    }
 
-        // ... and now write out everything remaining
-        match self.inner.write(&buf[j + 1..]) {
-            Ok(i) => n += i,
-            Err(_) => return Ok(n),
-        }
+    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+        LineWriterShim::new(&mut self.inner).write_all(buf)
+    }
 
-        if suffix.iter().map(|s| s.len()).sum::<usize>() == 0 {
-            return Ok(n);
-        }
-        match self.inner.write_vectored(suffix) {
-            Ok(i) => Ok(n + i),
-            Err(_) => Ok(n),
-        }
+    fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+        LineWriterShim::new(&mut self.inner).write_all_vectored(bufs)
     }
 
-    fn flush(&mut self) -> io::Result<()> {
-        self.inner.flush()?;
-        self.need_flush = false;
-        Ok(())
+    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
+        LineWriterShim::new(&mut self.inner).write_fmt(fmt)
     }
 }
 
@@ -1122,7 +1392,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 #[cfg(test)]
 mod tests {
     use crate::io::prelude::*;
-    use crate::io::{self, BufReader, BufWriter, IoSlice, LineWriter, SeekFrom};
+    use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom};
     use crate::sync::atomic::{AtomicUsize, Ordering};
     use crate::thread;
 
@@ -1131,6 +1401,9 @@ pub struct ShortReader {
         lengths: Vec<usize>,
     }
 
+    // FIXME: rustfmt and tidy disagree about the correct formatting of this
+    // function. This leads to issues for users with editors configured to
+    // rustfmt-on-save.
     impl Read for ShortReader {
         fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
             if self.lengths.is_empty() { Ok(0) } else { Ok(self.lengths.remove(0)) }
@@ -1406,34 +1679,6 @@ fn test_read_until() {
         assert_eq!(v, []);
     }
 
-    #[test]
-    fn test_line_buffer_fail_flush() {
-        // Issue #32085
-        struct FailFlushWriter<'a>(&'a mut Vec<u8>);
-
-        impl Write for FailFlushWriter<'_> {
-            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-                self.0.extend_from_slice(buf);
-                Ok(buf.len())
-            }
-            fn flush(&mut self) -> io::Result<()> {
-                Err(io::Error::new(io::ErrorKind::Other, "flush failed"))
-            }
-        }
-
-        let mut buf = Vec::new();
-        {
-            let mut writer = LineWriter::new(FailFlushWriter(&mut buf));
-            let to_write = b"abc\ndef";
-            if let Ok(written) = writer.write(to_write) {
-                assert!(written < to_write.len(), "didn't flush on new line");
-                // PASS
-                return;
-            }
-        }
-        assert!(buf.is_empty(), "write returned an error but wrote data");
-    }
-
     #[test]
     fn test_line_buffer() {
         let mut writer = LineWriter::new(Vec::new());
@@ -1554,41 +1799,104 @@ fn bench_buffered_writer(b: &mut test::Bencher) {
         b.iter(|| BufWriter::new(io::sink()));
     }
 
-    struct AcceptOneThenFail {
-        written: bool,
-        flushed: bool,
+    /// A simple `Write` target, designed to be wrapped by `LineWriter` /
+    /// `BufWriter` / etc, that can have its `write` & `flush` behavior
+    /// configured
+    #[derive(Default, Clone)]
+    struct ProgrammableSink {
+        // Writes append to this slice
+        pub buffer: Vec<u8>,
+
+        // Flush sets this flag
+        pub flushed: bool,
+
+        // If true, writes will always be an error
+        pub always_write_error: bool,
+
+        // If true, flushes will always be an error
+        pub always_flush_error: bool,
+
+        // If set, only up to this number of bytes will be written in a single
+        // call to `write`
+        pub accept_prefix: Option<usize>,
+
+        // If set, counts down with each write, and writes return an error
+        // when it hits 0
+        pub max_writes: Option<usize>,
+
+        // If set, attempting to write when max_writes == Some(0) will be an
+        // error; otherwise, it will return Ok(0).
+        pub error_after_max_writes: bool,
     }
 
-    impl Write for AcceptOneThenFail {
+    impl Write for ProgrammableSink {
         fn write(&mut self, data: &[u8]) -> io::Result<usize> {
-            if !self.written {
-                assert_eq!(data, b"a\nb\n");
-                self.written = true;
-                Ok(data.len())
-            } else {
-                Err(io::Error::new(io::ErrorKind::NotFound, "test"))
+            if self.always_write_error {
+                return Err(io::Error::new(io::ErrorKind::Other, "test - always_write_error"));
             }
+
+            match self.max_writes {
+                Some(0) if self.error_after_max_writes => {
+                    return Err(io::Error::new(io::ErrorKind::Other, "test - max_writes"));
+                }
+                Some(0) => return Ok(0),
+                Some(ref mut count) => *count -= 1,
+                None => {}
+            }
+
+            let len = match self.accept_prefix {
+                None => data.len(),
+                Some(prefix) => data.len().min(prefix),
+            };
+
+            let data = &data[..len];
+            self.buffer.extend_from_slice(data);
+
+            Ok(len)
         }
 
         fn flush(&mut self) -> io::Result<()> {
-            assert!(self.written);
-            assert!(!self.flushed);
-            self.flushed = true;
-            Err(io::Error::new(io::ErrorKind::Other, "test"))
+            if self.always_flush_error {
+                Err(io::Error::new(io::ErrorKind::Other, "test - always_flush_error"))
+            } else {
+                self.flushed = true;
+                Ok(())
+            }
         }
     }
 
+    /// Previously the `LineWriter` could successfully write some bytes but
+    /// then fail to report that it has done so. Additionally, an erroneous
+    /// flush after a successful write was permanently ignored.
+    ///
+    /// Test that a line writer correctly reports the number of written bytes,
+    /// and that it attempts to flush buffered lines from previous writes
+    /// before processing new data
+    ///
+    /// Regression test for #37807
     #[test]
     fn erroneous_flush_retried() {
-        let a = AcceptOneThenFail { written: false, flushed: false };
+        let writer = ProgrammableSink {
+            // Only write up to 4 bytes at a time
+            accept_prefix: Some(4),
 
-        let mut l = LineWriter::new(a);
-        assert_eq!(l.write(b"a\nb\na").unwrap(), 4);
-        assert!(l.get_ref().written);
-        assert!(l.get_ref().flushed);
-        l.get_mut().flushed = false;
+            // Accept the first two writes, then error the others
+            max_writes: Some(2),
+            error_after_max_writes: true,
+
+            ..Default::default()
+        };
 
-        assert_eq!(l.write(b"a").unwrap_err().kind(), io::ErrorKind::Other)
+        // This should write the first 4 bytes. The rest will be buffered, out
+        // to the last newline.
+        let mut writer = LineWriter::new(writer);
+        assert_eq!(writer.write(b"a\nb\nc\nd\ne").unwrap(), 8);
+
+        // This write should attempt to flush "c\nd\n", then buffer "e". No
+        // errors should happen here because no further writes should be
+        // attempted against `writer`.
+        assert_eq!(writer.write(b"e").unwrap(), 1);
+        assert_eq!(&writer.get_ref().buffer, b"a\nb\nc\nd\n");
     }
 
     #[test]
@@ -1633,17 +1941,21 @@ fn line_vectored() {
             0,
         );
         assert_eq!(a.write_vectored(&[IoSlice::new(b"a\nb"),]).unwrap(), 3);
-        assert_eq!(a.get_ref(), b"\nabaca\n");
+        assert_eq!(a.get_ref(), b"\nabaca\nb");
     }
 
     #[test]
     fn line_vectored_partial_and_errors() {
+        use crate::collections::VecDeque;
+
         enum Call {
             Write { inputs: Vec<&'static [u8]>, output: io::Result<usize> },
             Flush { output: io::Result<()> },
         }
+
+        #[derive(Default)]
         struct Writer {
-            calls: Vec<Call>,
+            calls: VecDeque<Call>,
         }
 
         impl Write for Writer {
@@ -1652,19 +1964,23 @@ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
             }
 
             fn write_vectored(&mut self, buf: &[IoSlice<'_>]) -> io::Result<usize> {
-                match self.calls.pop().unwrap() {
+                match self.calls.pop_front().expect("unexpected call to write") {
                     Call::Write { inputs, output } => {
                         assert_eq!(inputs, buf.iter().map(|b| &**b).collect::<Vec<_>>());
                         output
                     }
-                    _ => panic!("unexpected call to write"),
+                    Call::Flush { .. } => panic!("unexpected call to write; expected a flush"),
                 }
             }
 
+            fn is_write_vectored(&self) -> bool {
+                true
+            }
+
             fn flush(&mut self) -> io::Result<()> {
-                match self.calls.pop().unwrap() {
+                match self.calls.pop_front().expect("Unexpected call to flush") {
                     Call::Flush { output } => output,
-                    _ => panic!("unexpected call to flush"),
+                    Call::Write { .. } => panic!("unexpected call to flush; expected a write"),
                 }
             }
         }
@@ -1678,24 +1994,317 @@ fn drop(&mut self) {
         }
 
         // partial writes keep going
-        let mut a = LineWriter::new(Writer { calls: Vec::new() });
+        let mut a = LineWriter::new(Writer::default());
         a.write_vectored(&[IoSlice::new(&[]), IoSlice::new(b"abc")]).unwrap();
-        a.get_mut().calls.push(Call::Flush { output: Ok(()) });
-        a.get_mut().calls.push(Call::Write { inputs: vec![b"bcx\n"], output: Ok(4) });
-        a.get_mut().calls.push(Call::Write { inputs: vec![b"abcx\n"], output: Ok(1) });
+
+        a.get_mut().calls.push_back(Call::Write { inputs: vec![b"abc"], output: Ok(1) });
+        a.get_mut().calls.push_back(Call::Write { inputs: vec![b"bc"], output: Ok(2) });
+        a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\n"], output: Ok(2) });
+
         a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\n")]).unwrap();
-        a.get_mut().calls.push(Call::Flush { output: Ok(()) });
+
+        a.get_mut().calls.push_back(Call::Flush { output: Ok(()) });
         a.flush().unwrap();
 
         // erroneous writes stop and don't write more
-        a.get_mut().calls.push(Call::Write { inputs: vec![b"x\n"], output: Err(err()) });
-        assert_eq!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).unwrap(), 2);
-        a.get_mut().calls.push(Call::Flush { output: Ok(()) });
-        a.get_mut().calls.push(Call::Write { inputs: vec![b"x\n"], output: Ok(2) });
+        a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\na"], output: Err(err()) });
+        a.get_mut().calls.push_back(Call::Flush { output: Ok(()) });
+        assert!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).is_err());
         a.flush().unwrap();
 
         fn err() -> io::Error {
             io::Error::new(io::ErrorKind::Other, "x")
         }
     }
+
+    /// Test that, in cases where vectored writing is not enabled, the
+    /// LineWriter uses the normal `write` call, which more-correctly handles
+    /// partial lines
+    #[test]
+    fn line_vectored_ignored() {
+        let writer = ProgrammableSink::default();
+        let mut writer = LineWriter::new(writer);
+
+        let content = [
+            IoSlice::new(&[]),
+            IoSlice::new(b"Line 1\nLine"),
+            IoSlice::new(b" 2\nLine 3\nL"),
+            IoSlice::new(&[]),
+            IoSlice::new(&[]),
+            IoSlice::new(b"ine 4"),
+            IoSlice::new(b"\nLine 5\n"),
+        ];
+
+        let count = writer.write_vectored(&content).unwrap();
+        assert_eq!(count, 11);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+
+        let count = writer.write_vectored(&content[2..]).unwrap();
+        assert_eq!(count, 11);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n");
+
+        let count = writer.write_vectored(&content[5..]).unwrap();
+        assert_eq!(count, 5);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n");
+
+        let count = writer.write_vectored(&content[6..]).unwrap();
+        assert_eq!(count, 8);
+        assert_eq!(
+            writer.get_ref().buffer.as_slice(),
+            b"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n".as_ref()
+        );
+    }
+
+    /// Test that, given this input:
+    ///
+    /// Line 1\n
+    /// Line 2\n
+    /// Line 3\n
+    /// Line 4
+    ///
+    /// And given a result that only writes to midway through Line 2
+    ///
+    /// That only up to the end of Line 3 is buffered
+    ///
+    /// This behavior is desirable because it prevents flushing partial lines
+    #[test]
+    fn partial_write_buffers_line() {
+        let writer = ProgrammableSink { accept_prefix: Some(13), ..Default::default() };
+        let mut writer = LineWriter::new(writer);
+
+        assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3\nLine4").unwrap(), 21);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2");
+
+        assert_eq!(writer.write(b"Line 4").unwrap(), 6);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n");
+    }
+
+    /// Test that, given this input:
+    ///
+    /// Line 1\n
+    /// Line 2\n
+    /// Line 3
+    ///
+    /// And given that the full write of lines 1 and 2 was successful
+    /// That data up to Line 3 is buffered
+    #[test]
+    fn partial_line_buffered_after_line_write() {
+        let writer = ProgrammableSink::default();
+        let mut writer = LineWriter::new(writer);
+
+        assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3").unwrap(), 20);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\n");
+
+        assert!(writer.flush().is_ok());
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3");
+    }
+
+    /// Test that, given a partial line that exceeds the length of
+    /// LineBuffer's buffer (that is, without a trailing newline), that that
+    /// line is written to the inner writer
+    #[test]
+    fn long_line_flushed() {
+        let writer = ProgrammableSink::default();
+        let mut writer = LineWriter::with_capacity(5, writer);
+
+        assert_eq!(writer.write(b"0123456789").unwrap(), 10);
+        assert_eq!(&writer.get_ref().buffer, b"0123456789");
+    }
+
+    /// Test that, given a very long partial line *after* successfully
+    /// flushing a complete line, that that line is buffered unconditionally,
+    /// and no additional writes take place. This assures the property that
+    /// `write` should make at-most-one attempt to write new data.
+    #[test]
+    fn line_long_tail_not_flushed() {
+        let writer = ProgrammableSink::default();
+        let mut writer = LineWriter::with_capacity(5, writer);
+
+        // Assert that Line 1\n is flushed, and 01234 is buffered
+        assert_eq!(writer.write(b"Line 1\n0123456789").unwrap(), 12);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+
+        // Because the buffer is full, this subsequent write will flush it
+        assert_eq!(writer.write(b"5").unwrap(), 1);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\n01234");
+    }
+
+    /// Test that, if an attempt to pre-flush buffered data returns Ok(0),
+    /// this is propagated as an error.
+    #[test]
+    fn line_buffer_write0_error() {
+        let writer = ProgrammableSink {
+            // Accept one write, then return Ok(0) on subsequent ones
+            max_writes: Some(1),
+
+            ..Default::default()
+        };
+        let mut writer = LineWriter::new(writer);
+
+        // This should write "Line 1\n" and buffer "Partial"
+        assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+
+        // This will attempt to flush "partial", which will return Ok(0), which
+        // needs to be an error, because we've already informed the client
+        // that we accepted the write.
+        let err = writer.write(b" Line End\n").unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::WriteZero);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+    }
+
+    /// Test that, if a write returns Ok(0) after a successful pre-flush, this
+    /// is propagated as Ok(0)
+    #[test]
+    fn line_buffer_write0_normal() {
+        let writer = ProgrammableSink {
+            // Accept two writes, then return Ok(0) on subsequent ones
+            max_writes: Some(2),
+
+            ..Default::default()
+        };
+        let mut writer = LineWriter::new(writer);
+
+        // This should write "Line 1\n" and buffer "Partial"
+        assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+
+        // This will flush partial, which will succeed, but then return Ok(0)
+        // when flushing " Line End\n"
+        assert_eq!(writer.write(b" Line End\n").unwrap(), 0);
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\nPartial");
+    }
+
+    /// LineWriter has a custom `write_all`; make sure it works correctly
+    #[test]
+    fn line_write_all() {
+        let writer = ProgrammableSink {
+            // Only write 5 bytes at a time
+            accept_prefix: Some(5),
+            ..Default::default()
+        };
+        let mut writer = LineWriter::new(writer);
+
+        writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial").unwrap();
+        assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\nLine 4\n");
+        writer.write_all(b" Line 5\n").unwrap();
+        assert_eq!(
+            writer.get_ref().buffer.as_slice(),
+            b"Line 1\nLine 2\nLine 3\nLine 4\nPartial Line 5\n".as_ref(),
+        );
+    }
+
+    #[test]
+    fn line_write_all_error() {
+        let writer = ProgrammableSink {
+            // Only accept up to 3 writes of up to 5 bytes each
+            accept_prefix: Some(5),
+            max_writes: Some(3),
+            ..Default::default()
+        };
+
+        let mut writer = LineWriter::new(writer);
+        let res = writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial");
+        assert!(res.is_err());
+        // An error from write_all leaves everything in an indeterminate state,
+        // so there's nothing else to test here
+    }
+
+    /// Under certain circumstances, the old implementation of LineWriter
+    /// would try to buffer "to the last newline" but be forced to buffer
+    /// less than that, leading to inappropriate partial line writes.
+    /// Regression test for that issue.
+    #[test]
+    fn partial_multiline_buffering() {
+        let writer = ProgrammableSink {
+            // Write only up to 5 bytes at a time
+            accept_prefix: Some(5),
+            ..Default::default()
+        };
+
+        let mut writer = LineWriter::with_capacity(10, writer);
+
+        let content = b"AAAAABBBBB\nCCCCDDDDDD\nEEE";
+
+        // When content is written, LineWriter will try to write blocks A, B,
+        // C, and D. Only block A will succeed. Under the old behavior, LineWriter
+        // would then try to buffer B, C and D, but because its capacity is 10,
+        // it will only be able to buffer B and C. We don't want to buffer
+        // partial lines concurrent with whole lines, so the correct behavior
+        // is to buffer only block B (out to the newline)
+        assert_eq!(writer.write(content).unwrap(), 11);
+        assert_eq!(writer.get_ref().buffer, *b"AAAAA");
+
+        writer.flush().unwrap();
+        assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB\n");
+    }
+
+    /// Same as test_partial_multiline_buffering, but in the event NO full lines
+    /// fit in the buffer, just buffer as much as possible
+    #[test]
+    fn partial_multiline_buffering_without_full_line() {
+        let writer = ProgrammableSink {
+            // Write only up to 5 bytes at a time
+            accept_prefix: Some(5),
+            ..Default::default()
+        };
+
+        let mut writer = LineWriter::with_capacity(5, writer);
+
+        let content = b"AAAAABBBBBBBBBB\nCCCCC\nDDDDD";
+
+        // When content is written, LineWriter will try to write blocks A, B,
+        // and C. Only block A will succeed. Under the old behavior, LineWriter
+        // would then try to buffer B and C, but because its capacity is 5,
+        // it will only be able to buffer part of B. Because it's not possible
+        // for it to buffer any complete lines, it should buffer as much of B as
+        // possible
+        assert_eq!(writer.write(content).unwrap(), 10);
+        assert_eq!(writer.get_ref().buffer, *b"AAAAA");
+
+        writer.flush().unwrap();
+        assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB");
+    }
+
+    #[derive(Debug, Clone, PartialEq, Eq)]
+    enum RecordedEvent {
+        Write(String),
+        Flush,
+    }
+
+    #[derive(Debug, Clone, Default)]
+    struct WriteRecorder {
+        pub events: Vec<RecordedEvent>,
+    }
+
+    impl Write for WriteRecorder {
+        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+            use crate::str::from_utf8;
+
+            self.events.push(RecordedEvent::Write(from_utf8(buf).unwrap().to_string()));
+            Ok(buf.len())
+        }
+
+        fn flush(&mut self) -> io::Result<()> {
+            self.events.push(RecordedEvent::Flush);
+            Ok(())
+        }
+    }
+
+    /// Test that a normal, formatted writeln only results in a single write
+    /// call to the underlying writer. A naive implementation of
+    /// LineWriter::write_all results in two writes: one of the buffered data,
+    /// and another of the final substring in the formatted set
+    #[test]
+    fn single_formatted_write() {
+        let writer = WriteRecorder::default();
+        let mut writer = LineWriter::new(writer);
+
+        // Under a naive implementation of LineWriter, this will result in two
+        // writes: "hello, world" and "!\n", because write() has to flush the
+        // buffer before attempting to write the last "!\n". write_all shouldn't
+        // have this limitation.
+        writeln!(&mut writer, "{}, {}!", "hello", "world").unwrap();
+        assert_eq!(writer.get_ref().events, [RecordedEvent::Write("hello, world!\n".to_string())]);
+    }
 }
index f0548582d2f5d0bab4ee461616ba84ab190779ae..d0f27df51850533dd3578b9352e4e01981154cb1 100644 (file)
@@ -293,7 +293,7 @@ pub fn into_inner(mut self) -> Option<T> {
 
         // Don't drop this `SyncOnceCell`. We just moved out one of the fields, but didn't set
         // the state to uninitialized.
-        mem::ManuallyDrop::new(self);
+        mem::forget(self);
         inner
     }
 
index 1142b74ff0dc4b8a3691766594ad8d205edcea8f..0797585e3df3f99820be07fc7f4a5cafe99d15a9 100644 (file)
 #![feature(doc_cfg)]
 #![feature(doc_keyword)]
 #![feature(doc_masked)]
-#![cfg_attr(not(bootstrap), feature(doc_spotlight))]
+#![feature(doc_spotlight)]
 #![feature(dropck_eyepatch)]
 #![feature(duration_constants)]
 #![feature(exact_size_is_empty)]
index c11049fb98152e0696675f077293c4c3ed287867..d7d96862b2116c0d6706d885b8878c4e3ab21d16 100644 (file)
@@ -268,7 +268,7 @@ pub fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 {
             inner: c::sockaddr_in {
                 sin_family: c::AF_INET as c::sa_family_t,
                 sin_port: htons(port),
-                sin_addr: *ip.as_inner(),
+                sin_addr: ip.into_inner(),
                 ..unsafe { mem::zeroed() }
             },
         }
@@ -286,6 +286,8 @@ pub fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn ip(&self) -> &Ipv4Addr {
+        // SAFETY: `Ipv4Addr` is `#[repr(C)] struct { _: in_addr; }`.
+        // It is safe to cast from `&in_addr` to `&Ipv4Addr`.
         unsafe { &*(&self.inner.sin_addr as *const c::in_addr as *const Ipv4Addr) }
     }
 
@@ -302,7 +304,7 @@ pub fn ip(&self) -> &Ipv4Addr {
     /// ```
     #[stable(feature = "sockaddr_setters", since = "1.9.0")]
     pub fn set_ip(&mut self, new_ip: Ipv4Addr) {
-        self.inner.sin_addr = *new_ip.as_inner()
+        self.inner.sin_addr = new_ip.into_inner()
     }
 
     /// Returns the port number associated with this socket address.
index de28268ea368212ee8593e01a7f087dc20b103c4..85bb6b60e6829da3d872348c3dc78a0904dba746 100644 (file)
@@ -12,7 +12,7 @@
 use crate::io::Write as IoWrite;
 use crate::mem::transmute;
 use crate::sys::net::netc as c;
-use crate::sys_common::{AsInner, FromInner};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 
 /// An IP address, either IPv4 or IPv6.
 ///
@@ -909,7 +909,10 @@ impl Eq for Ipv4Addr {}
 #[stable(feature = "rust1", since = "1.0.0")]
 impl hash::Hash for Ipv4Addr {
     fn hash<H: hash::Hasher>(&self, s: &mut H) {
-        // `inner` is #[repr(packed)], so we need to copy `s_addr`.
+        // NOTE:
+        // * hash in big endian order
+        // * in netbsd, `in_addr` has `repr(packed)`, we need to
+        //   copy `s_addr` to avoid unsafe borrowing
         { self.inner.s_addr }.hash(s)
     }
 }
@@ -944,13 +947,14 @@ fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Ord for Ipv4Addr {
     fn cmp(&self, other: &Ipv4Addr) -> Ordering {
+        // Compare as native endian
         u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr))
     }
 }
 
-impl AsInner<c::in_addr> for Ipv4Addr {
-    fn as_inner(&self) -> &c::in_addr {
-        &self.inner
+impl IntoInner<c::in_addr> for Ipv4Addr {
+    fn into_inner(self) -> c::in_addr {
+        self.inner
     }
 }
 
@@ -2019,6 +2023,7 @@ fn test_from_str_socket_addr() {
 
     #[test]
     fn ipv4_addr_to_string() {
+        assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1");
         // Short address
         assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1");
         // Long address
index eb8589eb58f4757df52d53c6c5c3248bb7f2964c..1950ffcb21b6755187362b71568a692b26d8674f 100644 (file)
@@ -170,63 +170,63 @@ pub struct stat {
 
 #[cfg(target_arch = "hexagon")]
 mod arch {
-    use crate::os::raw::{c_int, c_long, c_longlong, c_ulonglong};
+    use crate::os::raw::{c_int, c_long, c_uint};
 
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type blkcnt_t = c_longlong;
+    pub type blkcnt_t = i64;
     #[stable(feature = "raw_ext", since = "1.1.0")]
     pub type blksize_t = c_long;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type ino_t = c_ulonglong;
+    pub type ino_t = u64;
     #[stable(feature = "raw_ext", since = "1.1.0")]
     pub type nlink_t = c_uint;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type off_t = c_longlong;
+    pub type off_t = i64;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type time_t = c_long;
+    pub type time_t = i64;
 
     #[repr(C)]
     #[derive(Clone)]
     #[stable(feature = "raw_ext", since = "1.1.0")]
     pub struct stat {
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_dev: ::dev_t,
+        pub st_dev: u64,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ino: ::c_ulonglong,
+        pub st_ino: u64,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_mode: ::c_uint,
+        pub st_mode: u32,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_nlink: ::c_uint,
+        pub st_nlink: u32,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_uid: ::c_uint,
+        pub st_uid: u32,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_gid: ::c_uint,
+        pub st_gid: u32,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_rdev: ::c_ulonglong,
+        pub st_rdev: u64,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub __pad1: ::c_ulong,
+        pub __pad1: u32,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_size: ::c_longlong,
+        pub st_size: i64,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_blksize: ::blksize_t,
+        pub st_blksize: i32,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub __pad2: ::c_int,
+        pub __pad2: i32,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_blocks: ::blkcnt_t,
+        pub st_blocks: i64,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_atime: ::time_t,
+        pub st_atime: i64,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_atime_nsec: ::c_long,
+        pub st_atime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_mtime: ::time_t,
+        pub st_mtime: i64,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_mtime_nsec: ::c_long,
+        pub st_mtime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ctime: ::time_t,
+        pub st_ctime: i64,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ctime_nsec: ::c_long,
+        pub st_ctime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub __pad3: [::c_int; 2],
+        pub __pad3: [c_int; 2],
     }
 }
 
index 8fcb24033b1a74819d2b89e2b1a8e15ed72473bf..87493945db6dc804a5b28634c2d1a91f847d02d3 100644 (file)
@@ -359,6 +359,9 @@ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
 /// aborting the process as well. This function *only* catches unwinding panics,
 /// not those that abort the process.
 ///
+/// Also note that unwinding into Rust code with a foreign exception (e.g. a
+/// an exception thrown from C++ code) is undefined behavior.
+///
 /// # Examples
 ///
 /// ```
index 08d363a9a29649d1359f1de996bb8bcd64bc0661..8dceb12de87b894793b373df4400589e4d0344f6 100644 (file)
@@ -60,6 +60,14 @@ extern "C" fn __rust_drop_panic() -> ! {
     rtabort!("Rust panics must be rethrown");
 }
 
+/// This function is called by the panic runtime if it catches an exception
+/// object which does not correspond to a Rust panic.
+#[cfg(not(test))]
+#[rustc_std_internal_symbol]
+extern "C" fn __rust_foreign_exception() -> ! {
+    rtabort!("Rust cannot catch foreign exceptions");
+}
+
 #[derive(Copy, Clone)]
 enum Hook {
     Default,
index be7fd0dd6c4479cbf909ee66b3a78896a1684463..2339ca527bd832f2734496aaf72d3067c2539ad5 100644 (file)
 /// assert!(!bool_val);
 /// ```
 ///
-/// [`assert!`]: macro.assert.html
-/// [`BitAnd`]: ops/trait.BitAnd.html
-/// [`BitOr`]: ops/trait.BitOr.html
-/// [`Not`]: ops/trait.Not.html
+/// [`BitAnd`]: ops::BitAnd
+/// [`BitOr`]: ops::BitOr
+/// [`Not`]: ops::Not
 ///
 /// # Examples
 ///
@@ -46,7 +45,7 @@
 /// }
 /// ```
 ///
-/// Also, since `bool` implements the [`Copy`](marker/trait.Copy.html) trait, we don't
+/// Also, since `bool` implements the [`Copy`] trait, we don't
 /// have to worry about the move semantics (just like the integer and float primitives).
 ///
 /// Now an example of `bool` cast to integer type:
@@ -100,8 +99,8 @@ mod prim_bool {}
 /// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another
 /// behaviour of the `!` type - expressions with type `!` will coerce into any other type.
 ///
-/// [`u32`]: primitive.str.html
-/// [`exit`]: process/fn.exit.html
+/// [`u32`]: prim@u32
+/// [`exit`]: process::exit
 ///
 /// # `!` and generics
 ///
@@ -185,14 +184,12 @@ mod prim_bool {}
 /// ever stops, it means that an error occurred. We don't even have to wrap the loop in an `Ok`
 /// because `!` coerces to `Result<!, ConnectionError>` automatically.
 ///
-/// [`String::from_str`]: str/trait.FromStr.html#tymethod.from_str
-/// [`Result<String, !>`]: result/enum.Result.html
-/// [`Result<T, !>`]: result/enum.Result.html
-/// [`Result<!, E>`]: result/enum.Result.html
-/// [`Ok`]: result/enum.Result.html#variant.Ok
-/// [`String`]: string/struct.String.html
-/// [`Err`]: result/enum.Result.html#variant.Err
-/// [`FromStr`]: str/trait.FromStr.html
+/// [`String::from_str`]: str::FromStr::from_str
+/// [`Result<String, !>`]: Result
+/// [`Result<T, !>`]: Result
+/// [`Result<!, E>`]: Result
+/// [`String`]: string::String
+/// [`FromStr`]: str::FromStr
 ///
 /// # `!` and traits
 ///
@@ -233,11 +230,9 @@ mod prim_bool {}
 /// `impl` for this which simply panics, but the same is true for any type (we could `impl
 /// Default` for (eg.) [`File`] by just making [`default()`] panic.)
 ///
-/// [`fmt::Result`]: fmt/type.Result.html
-/// [`File`]: fs/struct.File.html
-/// [`Debug`]: fmt/trait.Debug.html
-/// [`Default`]: default/trait.Default.html
-/// [`default()`]: default/trait.Default.html#tymethod.default
+/// [`File`]: fs::File
+/// [`Debug`]: fmt::Debug
+/// [`default()`]: Default::default
 ///
 #[unstable(feature = "never_type", issue = "35121")]
 mod prim_never {}
@@ -360,7 +355,7 @@ mod prim_unit {}
 //
 /// Raw, unsafe pointers, `*const T`, and `*mut T`.
 ///
-/// *[See also the `std::ptr` module](ptr/index.html).*
+/// *[See also the `std::ptr` module][`ptr`].*
 ///
 /// Working with raw pointers in Rust is uncommon, typically limited to a few patterns.
 /// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is
@@ -439,13 +434,13 @@ mod prim_unit {}
 /// but C APIs hand out a lot of pointers generally, so are a common source
 /// of raw pointers in Rust.
 ///
-/// [`null`]: ../std/ptr/fn.null.html
-/// [`null_mut`]: ../std/ptr/fn.null_mut.html
+/// [`null`]: ptr::null
+/// [`null_mut`]: ptr::null_mut
 /// [`is_null`]: ../std/primitive.pointer.html#method.is_null
 /// [`offset`]: ../std/primitive.pointer.html#method.offset
-/// [`into_raw`]: ../std/boxed/struct.Box.html#method.into_raw
-/// [`drop`]: ../std/mem/fn.drop.html
-/// [`write`]: ../std/ptr/fn.write.html
+/// [`into_raw`]: Box::into_raw
+/// [`drop`]: mem::drop
+/// [`write`]: ptr::write
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_pointer {}
 
@@ -458,24 +453,24 @@ mod prim_pointer {}
 ///
 /// * A list with each element, i.e., `[x, y, z]`.
 /// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`.
-///   The type of `x` must be [`Copy`][copy].
+///   The type of `x` must be [`Copy`].
 ///
 /// Arrays of *any* size implement the following traits if the element type allows it:
 ///
-/// - [`Debug`][debug]
-/// - [`IntoIterator`][intoiterator] (implemented for `&[T; N]` and `&mut [T; N]`)
-/// - [`PartialEq`][partialeq], [`PartialOrd`][partialord], [`Eq`][eq], [`Ord`][ord]
-/// - [`Hash`][hash]
-/// - [`AsRef`][asref], [`AsMut`][asmut]
-/// - [`Borrow`][borrow], [`BorrowMut`][borrowmut]
+/// - [`Debug`]
+/// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`)
+/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
+/// - [`Hash`]
+/// - [`AsRef`], [`AsMut`]
+/// - [`Borrow`], [`BorrowMut`]
 ///
-/// Arrays of sizes from 0 to 32 (inclusive) implement [`Default`][default] trait
+/// Arrays of sizes from 0 to 32 (inclusive) implement [`Default`] trait
 /// if the element type allows it. As a stopgap, trait implementations are
 /// statically generated up to size 32.
 ///
-/// Arrays of *any* size are [`Copy`][copy] if the element type is [`Copy`][copy]
-/// and [`Clone`][clone] if the element type is [`Clone`][clone]. This works
-/// because [`Copy`][copy] and [`Clone`][clone] traits are specially known
+/// Arrays of *any* size are [`Copy`] if the element type is [`Copy`]
+/// and [`Clone`] if the element type is [`Clone`]. This works
+/// because [`Copy`] and [`Clone`] traits are specially known
 /// to the compiler.
 ///
 /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
@@ -483,7 +478,7 @@ mod prim_pointer {}
 /// Slices have a dynamic size and do not coerce to arrays.
 ///
 /// You can move elements out of an array with a slice pattern. If you want
-/// one element, see [`mem::replace`][replace].
+/// one element, see [`mem::replace`].
 ///
 /// # Examples
 ///
@@ -535,22 +530,10 @@ mod prim_pointer {}
 /// ```
 ///
 /// [slice]: primitive.slice.html
-/// [copy]: marker/trait.Copy.html
-/// [clone]: clone/trait.Clone.html
-/// [debug]: fmt/trait.Debug.html
-/// [intoiterator]: iter/trait.IntoIterator.html
-/// [partialeq]: cmp/trait.PartialEq.html
-/// [partialord]: cmp/trait.PartialOrd.html
-/// [eq]: cmp/trait.Eq.html
-/// [ord]: cmp/trait.Ord.html
-/// [hash]: hash/trait.Hash.html
-/// [asref]: convert/trait.AsRef.html
-/// [asmut]: convert/trait.AsMut.html
-/// [borrow]: borrow/trait.Borrow.html
-/// [borrowmut]: borrow/trait.BorrowMut.html
-/// [default]: default/trait.Default.html
-/// [replace]: mem/fn.replace.html
-/// [`IntoIterator`]: iter/trait.IntoIterator.html
+/// [`Debug`]: fmt::Debug
+/// [`Hash`]: hash::Hash
+/// [`Borrow`]: borrow::Borrow
+/// [`BorrowMut`]: borrow::BorrowMut
 ///
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_array {}
@@ -563,7 +546,7 @@ mod prim_array {}
 /// means that elements are laid out so that every element is the same
 /// distance from its neighbors.
 ///
-/// *[See also the `std::slice` module](slice/index.html).*
+/// *[See also the `std::slice` module][`crate::slice`].*
 ///
 /// Slices are a view into a block of memory represented as a pointer and a
 /// length.
@@ -608,7 +591,7 @@ mod prim_slice {}
 //
 /// String slices.
 ///
-/// *[See also the `std::str` module](str/index.html).*
+/// *[See also the `std::str` module][`crate::str`].*
 ///
 /// The `str` type, also called a 'string slice', is the most primitive string
 /// type. It is usually seen in its borrowed form, `&str`. It is also the type
@@ -660,8 +643,8 @@ mod prim_slice {}
 /// assert_eq!(s, Ok(story));
 /// ```
 ///
-/// [`as_ptr`]: #method.as_ptr
-/// [`len`]: #method.len
+/// [`as_ptr`]: str::as_ptr
+/// [`len`]: str::len
 ///
 /// Note: This example shows the internals of `&str`. `unsafe` should not be
 /// used to get a string slice under normal circumstances. Use `as_str`
@@ -729,15 +712,8 @@ mod prim_str {}
 /// * [`Default`]
 /// * [`Hash`]
 ///
-/// [`Clone`]: clone/trait.Clone.html
-/// [`Copy`]: marker/trait.Copy.html
-/// [`PartialEq`]: cmp/trait.PartialEq.html
-/// [`Eq`]: cmp/trait.Eq.html
-/// [`PartialOrd`]: cmp/trait.PartialOrd.html
-/// [`Ord`]: cmp/trait.Ord.html
-/// [`Debug`]: fmt/trait.Debug.html
-/// [`Default`]: default/trait.Default.html
-/// [`Hash`]: hash/trait.Hash.html
+/// [`Debug`]: fmt::Debug
+/// [`Hash`]: hash::Hash
 ///
 /// Due to a temporary restriction in Rust's type system, these traits are only
 /// implemented on tuples of arity 12 or less. In the future, this may change.
@@ -810,7 +786,7 @@ mod prim_tuple {}
 ///
 /// For more information on floating point numbers, see [Wikipedia][wikipedia].
 ///
-/// *[See also the `std::f32::consts` module](f32/consts/index.html).*
+/// *[See also the `std::f32::consts` module][`crate::f32::consts`].*
 ///
 /// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -819,13 +795,14 @@ mod prim_f32 {}
 #[doc(primitive = "f64")]
 /// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008).
 ///
-/// This type is very similar to [`f32`](primitive.f32.html), but has increased
+/// This type is very similar to [`f32`], but has increased
 /// precision by using twice as many bits. Please see [the documentation for
-/// `f32`](primitive.f32.html) or [Wikipedia on double precision
+/// `f32`][`f32`] or [Wikipedia on double precision
 /// values][wikipedia] for more information.
 ///
-/// *[See also the `std::f64::consts` module](f64/consts/index.html).*
+/// *[See also the `std::f64::consts` module][`crate::f64::consts`].*
 ///
+/// [`f32`]: prim@f32
 /// [wikipedia]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_f64 {}
@@ -945,9 +922,6 @@ mod prim_usize {}
 /// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while
 /// [`PartialEq`] compares values.
 ///
-/// [`ptr::eq`]: ptr/fn.eq.html
-/// [`PartialEq`]: cmp/trait.PartialEq.html
-///
 /// ```
 /// use std::ptr;
 ///
@@ -979,11 +953,9 @@ mod prim_usize {}
 /// * [`Borrow`]
 /// * [`Pointer`]
 ///
-/// [`Copy`]: marker/trait.Copy.html
-/// [`Clone`]: clone/trait.Clone.html
-/// [`Deref`]: ops/trait.Deref.html
-/// [`Borrow`]: borrow/trait.Borrow.html
-/// [`Pointer`]: fmt/trait.Pointer.html
+/// [`Deref`]: ops::Deref
+/// [`Borrow`]: borrow::Borrow
+/// [`Pointer`]: fmt::Pointer
 ///
 /// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating
 /// multiple simultaneous mutable borrows), plus the following, regardless of the type of its
@@ -992,8 +964,8 @@ mod prim_usize {}
 /// * [`DerefMut`]
 /// * [`BorrowMut`]
 ///
-/// [`DerefMut`]: ops/trait.DerefMut.html
-/// [`BorrowMut`]: borrow/trait.BorrowMut.html
+/// [`DerefMut`]: ops::DerefMut
+/// [`BorrowMut`]: borrow::BorrowMut
 ///
 /// The following traits are implemented on `&T` references if the underlying `T` also implements
 /// that trait:
@@ -1008,18 +980,10 @@ mod prim_usize {}
 /// * [`Hash`]
 /// * [`ToSocketAddrs`]
 ///
-/// [`std::fmt`]: fmt/index.html
-/// [`fmt::Write`]: fmt/trait.Write.html
-/// [`PartialOrd`]: cmp/trait.PartialOrd.html
-/// [`Ord`]: cmp/trait.Ord.html
-/// [`PartialEq`]: cmp/trait.PartialEq.html
-/// [`Eq`]: cmp/trait.Eq.html
-/// [`AsRef`]: convert/trait.AsRef.html
-/// [`Fn`]: ops/trait.Fn.html
-/// [`FnMut`]: ops/trait.FnMut.html
-/// [`FnOnce`]: ops/trait.FnOnce.html
-/// [`Hash`]: hash/trait.Hash.html
-/// [`ToSocketAddrs`]: net/trait.ToSocketAddrs.html
+/// [`std::fmt`]: fmt
+/// ['Pointer`]: fmt::Pointer
+/// [`Hash`]: hash::Hash
+/// [`ToSocketAddrs`]: net::ToSocketAddrs
 ///
 /// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T`
 /// implements that trait:
@@ -1038,17 +1002,11 @@ mod prim_usize {}
 /// * [`Seek`]
 /// * [`BufRead`]
 ///
-/// [`AsMut`]: convert/trait.AsMut.html
-/// [`Iterator`]: iter/trait.Iterator.html
-/// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html
-/// [`ExactSizeIterator`]: iter/trait.ExactSizeIterator.html
-/// [`FusedIterator`]: iter/trait.FusedIterator.html
-/// [`TrustedLen`]: iter/trait.TrustedLen.html
-/// [`Send`]: marker/trait.Send.html
-/// [`io::Write`]: io/trait.Write.html
-/// [`Read`]: io/trait.Read.html
-/// [`Seek`]: io/trait.Seek.html
-/// [`BufRead`]: io/trait.BufRead.html
+/// [`FusedIterator`]: iter::FusedIterator
+/// [`TrustedLen`]: iter::TrustedLen
+/// [`Seek`]: io::Seek
+/// [`BufRead`]: io::BufRead
+/// [`Read`]: io::Read
 ///
 /// Note that due to method call deref coercion, simply calling a trait method will act like they
 /// work on references as well as they do on owned values! The implementations described here are
@@ -1063,9 +1021,9 @@ mod prim_ref {}
 ///
 /// *See also the traits [`Fn`], [`FnMut`], and [`FnOnce`].*
 ///
-/// [`Fn`]: ops/trait.Fn.html
-/// [`FnMut`]: ops/trait.FnMut.html
-/// [`FnOnce`]: ops/trait.FnOnce.html
+/// [`Fn`]: ops::Fn
+/// [`FnMut`]: ops::FnMut
+/// [`FnOnce`]: ops::FnOnce
 ///
 /// Function pointers are pointers that point to *code*, not data. They can be called
 /// just like functions. Like references, function pointers are, among other things, assumed to
@@ -1177,14 +1135,8 @@ mod prim_ref {}
 /// * [`Pointer`]
 /// * [`Debug`]
 ///
-/// [`Clone`]: clone/trait.Clone.html
-/// [`PartialEq`]: cmp/trait.PartialEq.html
-/// [`Eq`]: cmp/trait.Eq.html
-/// [`PartialOrd`]: cmp/trait.PartialOrd.html
-/// [`Ord`]: cmp/trait.Ord.html
-/// [`Hash`]: hash/trait.Hash.html
-/// [`Pointer`]: fmt/trait.Pointer.html
-/// [`Debug`]: fmt/trait.Debug.html
+/// [`Hash`]: hash::Hash
+/// [`Pointer`]: fmt::Pointer
 ///
 /// Due to a temporary restriction in Rust's type system, these traits are only implemented on
 /// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this
@@ -1193,7 +1145,5 @@ mod prim_ref {}
 /// In addition, function pointers of *any* signature, ABI, or safety are [`Copy`], and all *safe*
 /// function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`]. This works because these traits
 /// are specially known to the compiler.
-///
-/// [`Copy`]: marker/trait.Copy.html
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_fn {}
index ea186846929be135479736277e31ece1035b4960..d58468ad539ff92ca0a9d4d96fd260c01d7329b2 100644 (file)
@@ -53,7 +53,7 @@ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
     }
 
     #[inline]
-    fn is_read_vectored(&self) -> bool {
+    pub fn is_read_vectored(&self) -> bool {
         true
     }
 
index bbbd5eda7731411ee6a7ddf39aeebe35ac927426..6473a0c3cec41aa764d824f02a81f1765773b781 100644 (file)
@@ -351,8 +351,7 @@ pub fn new(status: c_int) -> ExitStatus {
     }
 
     fn exited(&self) -> bool {
-        /*unsafe*/
-        { libc::WIFEXITED(self.0) }
+        libc::WIFEXITED(self.0)
     }
 
     pub fn success(&self) -> bool {
@@ -360,19 +359,11 @@ pub fn success(&self) -> bool {
     }
 
     pub fn code(&self) -> Option<i32> {
-        if self.exited() {
-            Some(/*unsafe*/ { libc::WEXITSTATUS(self.0) })
-        } else {
-            None
-        }
+        if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
     }
 
     pub fn signal(&self) -> Option<i32> {
-        if !self.exited() {
-            Some(/*unsafe*/ { libc::WTERMSIG(self.0) })
-        } else {
-            None
-        }
+        if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
     }
 }
 
index 3f73f6c490326fb6d4e8c6c8fd94b55abdc36f08..5391ed83ebc36267e5932ba1723411bdc3e722a6 100644 (file)
@@ -2,6 +2,6 @@
 #![unstable(feature = "thread_local_internals", issue = "none")]
 
 pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
-    use crate::sys_common::thread_local::register_dtor_fallback;
+    use crate::sys_common::thread_local_dtor::register_dtor_fallback;
     register_dtor_fallback(t, dtor);
 }
index f41c6626ccf120824fb683d8a5d850d471886a9d..501ad8ee7d6b3c390afc75128232db04752c973f 100644 (file)
@@ -9,8 +9,6 @@
 use crate::sys_common::{AsInner, AsInnerMut, FromInner};
 
 /// WASI-specific extensions to [`File`].
-///
-/// [`File`]: ../../../../std/fs/struct.File.html
 pub trait FileExt {
     /// Reads a number of bytes starting from a given offset.
     ///
@@ -23,8 +21,6 @@ pub trait FileExt {
     ///
     /// Note that similar to [`File::read`], it is not an error to return with a
     /// short read.
-    ///
-    /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read
     fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
         let bufs = &mut [IoSliceMut::new(buf)];
         self.read_vectored_at(bufs, offset)
@@ -41,8 +37,6 @@ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
     ///
     /// Note that similar to [`File::read_vectored`], it is not an error to
     /// return with a short read.
-    ///
-    /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read_vectored
     fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
 
     /// Reads the exact number of byte required to fill `buf` from the given offset.
@@ -54,8 +48,7 @@ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
     ///
     /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`.
     ///
-    /// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact
-    /// [`read_at`]: #tymethod.read_at
+    /// [`read_at`]: FileExt::read_at
     ///
     /// # Errors
     ///
@@ -73,9 +66,6 @@ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
     /// If this function returns an error, it is unspecified how many bytes it
     /// has read, but it will never read more than would be necessary to
     /// completely fill the buffer.
-    ///
-    /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
-    /// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof
     #[stable(feature = "rw_exact_all_at", since = "1.33.0")]
     fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
         while !buf.is_empty() {
@@ -111,8 +101,6 @@ fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
     ///
     /// Note that similar to [`File::write`], it is not an error to return a
     /// short write.
-    ///
-    /// [`File::write`]: ../../../../std/fs/struct.File.html#write.v
     fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         let bufs = &[IoSlice::new(buf)];
         self.write_vectored_at(bufs, offset)
@@ -132,8 +120,6 @@ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
     ///
     /// Note that similar to [`File::write_vectored`], it is not an error to return a
     /// short write.
-    ///
-    /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write_vectored
     fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;
 
     /// Attempts to write an entire buffer starting from a given offset.
@@ -155,8 +141,7 @@ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
     /// This function will return the first error of
     /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns.
     ///
-    /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
-    /// [`write_at`]: #tymethod.write_at
+    /// [`write_at`]: FileExt::write_at
     #[stable(feature = "rw_exact_all_at", since = "1.33.0")]
     fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
         while !buf.is_empty() {
@@ -289,8 +274,6 @@ fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
 }
 
 /// WASI-specific extensions to [`fs::OpenOptions`].
-///
-/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
 pub trait OpenOptionsExt {
     /// Pass custom `dirflags` argument to `path_open`.
     ///
@@ -406,8 +389,6 @@ fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File> {
 }
 
 /// WASI-specific extensions to [`fs::Metadata`].
-///
-/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
 pub trait MetadataExt {
     /// Returns the `st_dev` field of the internal `filestat_t`
     fn dev(&self) -> u64;
@@ -448,8 +429,6 @@ fn ctim(&self) -> u64 {
 ///
 /// Adds support for special WASI file types such as block/character devices,
 /// pipes, and sockets.
-///
-/// [`FileType`]: ../../../../std/fs/struct.FileType.html
 pub trait FileTypeExt {
     /// Returns `true` if this file type is a block device.
     fn is_block_device(&self) -> bool;
@@ -477,8 +456,6 @@ fn is_socket_stream(&self) -> bool {
 }
 
 /// WASI-specific extension methods for [`fs::DirEntry`].
-///
-/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html
 pub trait DirEntryExt {
     /// Returns the underlying `d_ino` field of the `dirent_t`
     fn ino(&self) -> u64;
index 982ec912c44b710eebdb7d78559e9ea314e6f95b..a0d5a7471d8af67a4a64c94d2a4477db249d7ac2 100644 (file)
@@ -300,14 +300,10 @@ pub fn dur2timeout(dur: Duration) -> c::DWORD {
         .unwrap_or(c::INFINITE)
 }
 
-// On Windows, use the processor-specific __fastfail mechanism.  In Windows 8
-// and later, this will terminate the process immediately without running any
-// in-process exception handlers.  In earlier versions of Windows, this
-// sequence of instructions will be treated as an access violation,
-// terminating the process but without necessarily bypassing all exception
-// handlers.
-//
-// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail
+/// Use `__fastfail` to abort the process
+///
+/// This is the same implementation as in libpanic_abort's `__rust_start_panic`. See
+/// that function for more information on `__fastfail`
 #[allow(unreachable_code)]
 pub fn abort_internal() -> ! {
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
index 81a5ef95e82dcdc7cc33a230c715385304fdbf84..0bb136078bc95c6539c449adb9bf6ae5be233691 100644 (file)
@@ -585,8 +585,8 @@ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
 
     pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
         let mreq = c::ip_mreq {
-            imr_multiaddr: *multiaddr.as_inner(),
-            imr_interface: *interface.as_inner(),
+            imr_multiaddr: multiaddr.into_inner(),
+            imr_interface: interface.into_inner(),
         };
         setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq)
     }
@@ -601,8 +601,8 @@ pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Res
 
     pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
         let mreq = c::ip_mreq {
-            imr_multiaddr: *multiaddr.as_inner(),
-            imr_interface: *interface.as_inner(),
+            imr_multiaddr: multiaddr.into_inner(),
+            imr_interface: interface.into_inner(),
         };
         setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq)
     }
index 984c032e2a388829d5f0c0b13d6cd35c9d380a90..323165cda6bd5387f7cdf2bd9f4f1d8adff42070 100644 (file)
@@ -232,23 +232,17 @@ pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
 }
 
 /// Platform-specific extensions to [`OsString`].
-///
-/// [`OsString`]: ../../../../std/ffi/struct.OsString.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait OsStringExt {
     /// Creates an [`OsString`] from a byte vector.
     ///
     /// See the module documentation for an example.
-    ///
-    /// [`OsString`]: ../../../ffi/struct.OsString.html
     #[stable(feature = "rust1", since = "1.0.0")]
     fn from_vec(vec: Vec<u8>) -> Self;
 
     /// Yields the underlying byte vector of this [`OsString`].
     ///
     /// See the module documentation for an example.
-    ///
-    /// [`OsString`]: ../../../ffi/struct.OsString.html
     #[stable(feature = "rust1", since = "1.0.0")]
     fn into_vec(self) -> Vec<u8>;
 }
@@ -264,23 +258,17 @@ fn into_vec(self) -> Vec<u8> {
 }
 
 /// Platform-specific extensions to [`OsStr`].
-///
-/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait OsStrExt {
     #[stable(feature = "rust1", since = "1.0.0")]
     /// Creates an [`OsStr`] from a byte slice.
     ///
     /// See the module documentation for an example.
-    ///
-    /// [`OsStr`]: ../../../ffi/struct.OsStr.html
     fn from_bytes(slice: &[u8]) -> &Self;
 
     /// Gets the underlying byte view of the [`OsStr`] slice.
     ///
     /// See the module documentation for an example.
-    ///
-    /// [`OsStr`]: ../../../ffi/struct.OsStr.html
     #[stable(feature = "rust1", since = "1.0.0")]
     fn as_bytes(&self) -> &[u8];
 }
index 285851d631ae76b942a17249a1a1c2fc0a23b363..3f079da9098c7ef89f809f8d9b0f4ac757cdcf7f 100644 (file)
@@ -3,6 +3,9 @@
 use crate::sync::atomic::{AtomicBool, Ordering};
 use crate::thread;
 
+#[allow(unused_imports)] // for intra-doc links
+use crate::sync::{Mutex, RwLock};
+
 pub struct Flag {
     failed: AtomicBool,
 }
@@ -77,9 +80,6 @@ pub struct Guard {
 ///     }
 /// };
 /// ```
-///
-/// [`Mutex`]: ../../std/sync/struct.Mutex.html
-/// [`RwLock`]: ../../std/sync/struct.RwLock.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct PoisonError<T> {
     guard: T,
@@ -89,12 +89,9 @@ pub struct PoisonError<T> {
 /// can occur while trying to acquire a lock, from the [`try_lock`] method on a
 /// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`].
 ///
-/// [`Mutex`]: struct.Mutex.html
-/// [`RwLock`]: struct.RwLock.html
-/// [`TryLockResult`]: type.TryLockResult.html
-/// [`try_lock`]: struct.Mutex.html#method.try_lock
-/// [`try_read`]: struct.RwLock.html#method.try_read
-/// [`try_write`]: struct.RwLock.html#method.try_write
+/// [`try_lock`]: Mutex::try_lock
+/// [`try_read`]: RwLock::try_read
+/// [`try_write`]: RwLock::try_write
 #[stable(feature = "rust1", since = "1.0.0")]
 pub enum TryLockError<T> {
     /// The lock could not be acquired because another thread failed while holding
@@ -115,9 +112,7 @@ pub enum TryLockError<T> {
 /// the associated guard, and it can be acquired through the [`into_inner`]
 /// method.
 ///
-/// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok
-/// [`Err`]: ../../std/result/enum.Result.html#variant.Err
-/// [`into_inner`]: ../../std/sync/struct.PoisonError.html#method.into_inner
+/// [`into_inner`]: PoisonError::into_inner
 #[stable(feature = "rust1", since = "1.0.0")]
 pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
 
@@ -126,9 +121,6 @@ pub enum TryLockError<T> {
 /// For more information, see [`LockResult`]. A `TryLockResult` doesn't
 /// necessarily hold the associated guard in the [`Err`] type as the lock may not
 /// have been acquired for other reasons.
-///
-/// [`LockResult`]: ../../std/sync/type.LockResult.html
-/// [`Err`]: ../../std/result/enum.Result.html#variant.Err
 #[stable(feature = "rust1", since = "1.0.0")]
 pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
 
@@ -158,9 +150,6 @@ impl<T> PoisonError<T> {
     /// Creates a `PoisonError`.
     ///
     /// This is generally created by methods like [`Mutex::lock`] or [`RwLock::read`].
-    ///
-    /// [`Mutex::lock`]: ../../std/sync/struct.Mutex.html#method.lock
-    /// [`RwLock::read`]: ../../std/sync/struct.RwLock.html#method.read
     #[stable(feature = "sync_poison", since = "1.2.0")]
     pub fn new(guard: T) -> PoisonError<T> {
         PoisonError { guard }
index 02161ecb4c8d21adeb0ea2cb68c763fc21506fcc..c8aee1da39b394dffa7d6ba7e233ea93f4cac5b5 100644 (file)
 /// | DARWIN    | [gettimeofday]                                                       |
 /// | VXWorks   | [clock_gettime (Realtime Clock)]                                     |
 /// | WASI      | [__wasi_clock_time_get (Realtime Clock)]                             |
-/// | Windows   | [GetSystemTimeAsFileTime]                                            |
+/// | Windows   | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime]         |
 ///
 /// [clock_time_get (Realtime Clock)]: https://nuxi.nl/cloudabi/#clock_time_get
 /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
 /// [gettimeofday]: http://man7.org/linux/man-pages/man2/gettimeofday.2.html
 /// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime
 /// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#clock_time_get
+/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
 /// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime
 ///
 /// **Disclaimer:** These system calls might change over time.
index 111971534ba5e903026bb9b1b60a973727cfde43..5b6c327896ba85e5c389fe8456425a1f31c328ad 100644 (file)
@@ -8,6 +8,7 @@ fn configure(host: &[&str], target: &[&str]) -> Config {
     config.save_toolstates = None;
     config.skip_only_host_steps = false;
     config.dry_run = true;
+    config.ninja = false;
     // try to avoid spurious failures in dist where we create/delete each others file
     let dir = config
         .out
index 7ff00d85dd2f24a0ea0d9bc8552e4d6960aadc11..d50e4cf52697a30bd96338ba12c11e01a852f8cf 100644 (file)
@@ -132,7 +132,8 @@ pub fn find(build: &mut Build) {
             false
         };
 
-        if cxx_configured {
+        // for VxWorks, record CXX compiler which will be used in lib.rs:linker()
+        if cxx_configured || target.contains("vxworks") {
             let compiler = cfg.get_compiler();
             build.cxx.insert(target, compiler);
         }
index 51a9b0e0a52e14a853dd6b6edf32668da86b229b..2a461170b5cce8d9ef8d397fef38bfa938117d3a 100644 (file)
@@ -13,7 +13,7 @@
 use crate::Build;
 
 // The version number
-pub const CFG_RELEASE_NUM: &str = "1.47.0";
+pub const CFG_RELEASE_NUM: &str = "1.48.0";
 
 pub struct GitInfo {
     inner: Option<Info>,
index 8b8b01b11532731342b1413a0adcf86fb219252a..f549de6570ffb9b0b01c6ae047af5025929f0ef9 100644 (file)
@@ -450,6 +450,7 @@ fn normalize_python_path(path: OsString) -> PathBuf {
     pub fn default_opts() -> Config {
         let mut config = Config::default();
         config.llvm_optimize = true;
+        config.ninja = true;
         config.llvm_version_check = true;
         config.backtrace = true;
         config.rust_optimize = true;
index 01121977f130b66d678070bc1a13b491ead6faa6..f0b2254be9ee9159a057c793aea00e4cd0d9e557 100644 (file)
@@ -226,7 +226,7 @@ fn make_win_dist(
         let idx = line.find(':').unwrap();
         let key = &line[..idx];
         let trim_chars: &[_] = &[' ', '='];
-        let value = line[(idx + 1)..].trim_start_matches(trim_chars).split(';').map(PathBuf::from);
+        let value = env::split_paths(line[(idx + 1)..].trim_start_matches(trim_chars));
 
         if key == "programs" {
             bin_path.extend(value);
index a42ee11bd6fe50df3f8dd0f8797b7ad17c39a54a..191cc5b0b64ca85db834c46305059b9de884ab44 100644 (file)
@@ -854,6 +854,10 @@ fn linker(&self, target: TargetSelection, can_use_lld: bool) -> Option<&Path> {
         if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.as_ref())
         {
             Some(linker)
+        } else if target.contains("vxworks") {
+            // need to use CXX compiler as linker to resolve the exception functions
+            // that are only existed in CXX libraries
+            Some(self.cxx[&target].path())
         } else if target != self.config.build
             && util::use_host_linker(target)
             && !target.contains("msvc")
index 97d9dbdd63fb2f53809da3824b753c7f1fb74bd7..e9d0c017c7b46c34c6b4b4a7f32592c7145e36eb 100644 (file)
@@ -178,11 +178,9 @@ fn run(self, builder: &Builder<'_>) -> PathBuf {
             .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap())
             .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native);
 
-        if !target.contains("netbsd") && target != "aarch64-apple-darwin" {
+        if target != "aarch64-apple-darwin" {
             cfg.define("LLVM_ENABLE_ZLIB", "ON");
         } else {
-            // FIXME: Enable zlib on NetBSD too
-            // https://github.com/rust-lang/rust/pull/72696#issuecomment-641517185
             cfg.define("LLVM_ENABLE_ZLIB", "OFF");
         }
 
index f89bef50de982374352a826c163a0b3d70a27542..533b5c79777fcce704e55657e652f61e82c994cc 100644 (file)
@@ -100,8 +100,16 @@ pub fn check(build: &mut Build) {
         if build.config.ninja {
             // Some Linux distros rename `ninja` to `ninja-build`.
             // CMake can work with either binary name.
-            if cmd_finder.maybe_have("ninja-build").is_none() {
-                cmd_finder.must_have("ninja");
+            if cmd_finder.maybe_have("ninja-build").is_none()
+                && cmd_finder.maybe_have("ninja").is_none()
+            {
+                eprintln!(
+                    "
+Couldn't find required command: ninja
+You should install ninja, or set ninja=false in config.toml
+"
+                );
+                std::process::exit(1);
             }
         }
 
index ac833a55d4c53bbf2224fa0adf7fffba6b76de73..afa72b5d58c14e8744d5d43e10fe015deeba8b83 100644 (file)
@@ -1754,6 +1754,11 @@ fn run(self, builder: &Builder<'_>) {
             cargo.arg("--quiet");
         }
 
+        if builder.config.cmd.bless() {
+            // Bless `expect!` tests.
+            cargo.env("UPDATE_EXPECT", "1");
+        }
+
         if target.contains("emscripten") {
             cargo.env(
                 format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)),
index 2185b0d30dbde350c3ae25708d9388970b408768..d19682c669157997fdc694f084f7021adfced43c 100644 (file)
@@ -37,7 +37,7 @@ jobs:
       # version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
       x86_64-apple:
         SCRIPT: ./x.py --stage 2 test
-        INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
+        INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false
         RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
         MACOSX_DEPLOYMENT_TARGET: 10.8
         MACOSX_STD_DEPLOYMENT_TARGET: 10.7
@@ -46,7 +46,7 @@ jobs:
 
       dist-x86_64-apple:
         SCRIPT: ./x.py dist
-        INITIAL_RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc
+        INITIAL_RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false
         RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
         MACOSX_DEPLOYMENT_TARGET: 10.7
         NO_LLVM_ASSERTIONS: 1
@@ -55,7 +55,7 @@ jobs:
 
       dist-x86_64-apple-alt:
         SCRIPT: ./x.py dist
-        INITIAL_RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc
+        INITIAL_RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false
         RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
         MACOSX_DEPLOYMENT_TARGET: 10.7
         NO_LLVM_ASSERTIONS: 1
index b6cf60a5e15a7b11aa31b4a60fb241ab4d4df2f9..e54d0eafb4003abb4575ad858734cffa5540d2bd 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:20.04
 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 1f3092c5513d956c21c9885a9d81c30dbbb30900..f1ccbc928a71423148c6e169e0b729cb900150e5 100644 (file)
@@ -14,6 +14,7 @@ RUN apt-get update -y && apt-get install -y --no-install-recommends \
       libc6-dev \
       libc6-dev-armhf-cross \
       make \
+      ninja-build \
       python3 \
       qemu-system-arm \
       xz-utils
index a4d9f53ebabef1c3d32a4b07329f1de6e5abd9eb..6335dc089b238ea30544f684b76301811dced9da 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 19df9d6cf6231e4712b79422c22cdf93d295340c..34c487412145b16908969ae4361b2188c80ca966 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 62d0bfc71b2fa76a0cdf5cb635b9aa366804c703..a8e7583ccb01720b26ee5b20df377d5c4239fd9a 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 5e743f0818a4f43ce26245efe3958f67db591381..5d594a80581bb60cd64f757406e73d7c126c81b8 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 61b3179d55fd879453901b23faa6aea1dd731cdc..5ddd3f180396466223ee2e6bf71c0947b3cbc126 100644 (file)
@@ -16,6 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
   libcurl4-openssl-dev \
   libssl-dev \
   make \
+  ninja-build \
   nasm \
   pkg-config \
   python3 \
index e3c35000eb818086cc67c4cd0e77dc83bbc46c91..3c39a63849640a1e14f7a3231747c2ee2193b546 100644 (file)
@@ -20,6 +20,7 @@ RUN apt-get update -y && apt-get install -y --no-install-recommends \
     libc6-dev \
     libc6-dev-riscv64-cross \
     make \
+    ninja-build \
     patch \
     python3 \
     qemu-system-misc \
index 162d7a1345ce1cb64f9b192b292da68f8d76abaf..995f7c301f8cb4c3b7b3c36d47c86b753ce2b440 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++-multilib \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index bbbd632c6bb8206a0a851bb61f9e6106cb4bebe0..7db6e58c4d688a018b2fe9fa9a26c02ba7b53e7b 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:18.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   clang \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 9a051caa0667881dc49ee425bf295a4cde1d3c5f..22d7cbb0d14d8eba7ed4d6ea398c63b601221d54 100644 (file)
@@ -93,6 +93,7 @@ ENV RUST_CONFIGURE_ARGS \
       --enable-profiler \
       --set target.i686-unknown-linux-gnu.linker=clang \
       --build=i686-unknown-linux-gnu \
+      --set llvm.ninja=false \
       --set rust.jemalloc
 ENV SCRIPT python2.7 ../x.py dist --build $HOSTS --host $HOSTS --target $HOSTS
 ENV CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_LINKER=clang
index 57a7fc25b5c13ead0cf11168a452b053c94dd58c..cb5c17c25ab9f9f9e4c0a30f99392c98866d9b71 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 63f1028e2be593db9db9c0e533f9ae6c2f0a5f6c..31146e3ead43ee08bbdca35b13010866014618c8 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index a51edbc9c7923a49d0684efe54e5df4d0096f595..dadd50a7e66d9aad705ba0388318e899a749a9d9 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 908cef90cef6900c801a8fab567b0a1f5e8587c5..d2d65565b10f2213353fdb06ffff3f0eaeb9cf02 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index fdd777e824ba89c5433e50ea6ff4d85837d6de59..4e48d9b543361bb31af6990c463890f81f4a6af0 100644 (file)
@@ -15,6 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
   libncurses-dev \
   gawk \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 3354a796c357e51672845828d83e4a0e94160722..b7ff6cd4e4feb07eb476a7c07ca56e1746749064 100755 (executable)
@@ -16,6 +16,7 @@ apt-get install -y --no-install-recommends \
   git \
   lld-5.0 \
   make \
+  ninja-build \
   python \
   sudo \
   xz-utils
index bfc768f9935126d63ab7e15813133e19c5809096..1075947c9cfa7510bc4d9428868911e8f2b5f135 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:18.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   clang \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 1f0978c0082c5898166955a5dba385dc8deb62f4..58e2567a58f08955867869f922b4206d3f63d011 100644 (file)
@@ -96,6 +96,7 @@ ENV RUST_CONFIGURE_ARGS \
       --set target.x86_64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \
       --set target.x86_64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \
       --set llvm.thin-lto=true \
+      --set llvm.ninja=false \
       --set rust.jemalloc
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
 ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang
index d6ce2806e390f62849ce8e3ee69da0a5dc8d9418..969443ac0949bf6ab837373bccc03dea2bb6de58 100755 (executable)
@@ -4,7 +4,7 @@ set -ex
 
 source shared.sh
 
-LLVM=llvmorg-9.0.0
+LLVM=llvmorg-10.0.0
 
 mkdir llvm-project
 cd llvm-project
index ab6515cd1fa955daf10a57117643ea9adf1eddfd..904ee64db3b7e7b277952d1a3ec0c1841b9e3725 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   wget \
   curl \
@@ -33,6 +34,7 @@ ENV HOSTS=x86_64-unknown-linux-musl
 ENV RUST_CONFIGURE_ARGS \
       --musl-root-x86_64=/usr/local/x86_64-linux-musl \
       --enable-extended \
+      --enable-profiler \
       --enable-lld \
       --set target.x86_64-unknown-linux-musl.crt-static=false \
       --build $HOSTS
index cb507dced4248c22c87c95be88e528b938038432..930061fca6f8fe4a492bca86b50dc8b0a9d2ad2b 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++-multilib \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 9d319017d7967cbc0c9ccdc18846b561a333a74e..ea178bcf4f2704c4d411c8e5f056ee1aec258286 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++-multilib \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index b902eda87bc6ec9bd3c2d7b6b23a129be73f3b31..8fc9a009dce011a4622f433387751c37f09294a4 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:18.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index c55a284e137b34454e4c0914b6ea103167216254..8c606d88d68200ab1ad63ca733dad47b827971ca 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:18.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index e00177b4a67c9a979d104d956f98449f5bac7622..b4c783c781bd402f55384844fef08188514235ab 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 86ac0256d28201cd90cf9977171adf8e7fc9365f..a109b36066ee2bb9a14079b110975037a797b221 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index c1cb20b631d319d20a4a35845860366ae577588a..fe956b9c7b23a1de25bfa3cef9a70972c2692d5b 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:19.10
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 68e89a7bade342b09d849945c053d00f44b95731..5f98edf617174071caefbca1cc2e3d8703df1041 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 5c971c73c97d3485ae5ce4811b603b26f5dfea87..0c9d6ed442e9452380d7ed8bcd74564eb6c3cf8d 100644 (file)
@@ -4,6 +4,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   g++-arm-linux-gnueabi \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 41e83c69e549f4809ecdeffb54351356101cfb1f..f4071961f8e18736d17d0f68fea51bb93f511677 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index ef17f0507ab11a0dfc0010288c3dfe20729e457f..89171a6f2153b972d11347b6741ece5ef4cbaf0f 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:16.04
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index f8bacf79ac0dccea249b20a2aeaa55ed14e38f9e..527b539f682dbdfcca8b08faf9130f3ac4f2243c 100644 (file)
@@ -3,6 +3,7 @@ FROM ubuntu:19.10
 RUN apt-get update && apt-get install -y --no-install-recommends \
   g++ \
   make \
+  ninja-build \
   file \
   curl \
   ca-certificates \
index 391b68ea637b0e4f4d1162fae217203a7a7ec4cc..1795b1696d323ef6e80d375bb591b62ac451135c 100644 (file)
@@ -10,6 +10,7 @@ apt-get install -y --no-install-recommends \
   git \
   libssl-dev \
   make \
+  ninja-build \
   pkg-config \
   python3 \
   sudo \
index 7030cd74cae232c5e4a2aec35ae8d13ec0261b45..2de376443ad567c8dbdd8f995e9dd60fb73e7109 100644 (file)
@@ -17,6 +17,7 @@ apt-get update && apt-get install -y --no-install-recommends \
   libssl-dev \
   libtool-bin \
   make \
+  ninja-build \
   patch \
   pkg-config \
   python3 \
index db2def483ac309fe663088451a2ec81adde8a338..522d5bd3d9c36c9edd448d81326c5ac64978a8eb 100644 (file)
@@ -103,13 +103,6 @@ x--expand-yaml-anchors--remove:
         with:
           fetch-depth: 2
 
-      - name: configure GitHub Actions to kill the build when outdated
-        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
-        with:
-          github_token: "${{ secrets.github_token }}"
-        if: success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'
-        <<: *step
-
       # Rust Log Analyzer can't currently detect the PR number of a GitHub
       # Actions build on its own, so a hint in the log message is needed to
       # point it in the right direction.
@@ -133,6 +126,15 @@ x--expand-yaml-anchors--remove:
         run: src/ci/scripts/should-skip-this.sh
         <<: *step
 
+      - name: configure GitHub Actions to kill the build when outdated
+        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+        with:
+          github_token: "${{ secrets.github_token }}"
+        # TODO: remove the condition on RUST_CI_TEMP_SKIP_CANCEL_OUTDATED once
+        # we remove the `auto-fallible` job.
+        if: success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED
+        <<: *step
+
       - name: collect CPU statistics
         run: src/ci/scripts/collect-cpu-stats.sh
         <<: *step
@@ -603,33 +605,45 @@ jobs:
           - name: dist-x86_64-apple
             env:
               SCRIPT: ./x.py dist
-              RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc
+              RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false
               RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
               MACOSX_DEPLOYMENT_TARGET: 10.7
               NO_LLVM_ASSERTIONS: 1
               NO_DEBUG_ASSERTIONS: 1
               DIST_REQUIRE_ALL_TOOLS: 1
+
+              # TODO: remove once we move this job away from auto-fallible.
+              # Also, remove the variable from the cancel-outdated-builds step
+              RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1
             <<: *job-macos-xl
 
           - name: dist-x86_64-apple-alt
             env:
               SCRIPT: ./x.py dist
-              RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc
+              RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false
               RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
               MACOSX_DEPLOYMENT_TARGET: 10.7
               NO_LLVM_ASSERTIONS: 1
               NO_DEBUG_ASSERTIONS: 1
+
+              # TODO: remove once we move this job away from auto-fallible.
+              # Also, remove the variable from the cancel-outdated-builds step
+              RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1
             <<: *job-macos-xl
 
           - name: x86_64-apple
             env:
               SCRIPT: ./x.py --stage 2 test
-              RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
+              RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false
               RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
               MACOSX_DEPLOYMENT_TARGET: 10.8
               MACOSX_STD_DEPLOYMENT_TARGET: 10.7
               NO_LLVM_ASSERTIONS: 1
               NO_DEBUG_ASSERTIONS: 1
+
+              # TODO: remove once we move this job away from auto-fallible.
+              # Also, remove the variable from the cancel-outdated-builds step
+              RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1
             <<: *job-macos-xl
 
   master:
index c242f5d4562690ba8f0c310bd86df305cee523ef..a1481f22f509de74548a95cc1041d34dc862649b 100755 (executable)
@@ -8,11 +8,14 @@ IFS=$'\n\t'
 
 source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
 
+# Update both macOS's and Windows's tarballs when bumping the version here.
+LLVM_VERSION="10.0.0"
+
 if isMacOS; then
-    curl -f "${MIRRORS_BASE}/clang%2Bllvm-9.0.0-x86_64-darwin-apple.tar.xz" | tar xJf -
+    curl -f "${MIRRORS_BASE}/clang%2Bllvm-${LLVM_VERSION}-x86_64-apple-darwin.tar.xz" | tar xJf -
 
-    ciCommandSetEnv CC "$(pwd)/clang+llvm-9.0.0-x86_64-darwin-apple/bin/clang"
-    ciCommandSetEnv CXX "$(pwd)/clang+llvm-9.0.0-x86_64-darwin-apple/bin/clang++"
+    ciCommandSetEnv CC "$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin/bin/clang"
+    ciCommandSetEnv CXX "$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin/bin/clang++"
 
     # macOS 10.15 onwards doesn't have libraries in /usr/include anymore: those
     # are now located deep into the filesystem, under Xcode's own files. The
@@ -33,8 +36,10 @@ elif isWindows && [[ ${CUSTOM_MINGW-0} -ne 1 ]]; then
     #
     # Note that the LLVM installer is an NSIS installer
     #
-    # Original downloaded here came from
-    # http://releases.llvm.org/9.0.0/LLVM-9.0.0-win64.exe
+    # Original downloaded here came from:
+    #
+    #   https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe
+    #
     # That installer was run through `wine ./installer.exe /S /NCRC` on Linux
     # and then the resulting installation directory (found in
     # `$HOME/.wine/drive_c/Program Files/LLVM`) was packaged up into a tarball.
@@ -45,7 +50,7 @@ elif isWindows && [[ ${CUSTOM_MINGW-0} -ne 1 ]]; then
 
     mkdir -p citools
     cd citools
-    curl -f "${MIRRORS_BASE}/LLVM-9.0.0-win64.tar.gz" | tar xzf -
+    curl -f "${MIRRORS_BASE}/LLVM-${LLVM_VERSION}-win64.tar.gz" | tar xzf -
     ciCommandSetEnv RUST_CONFIGURE_ARGS \
         "${RUST_CONFIGURE_ARGS} --set llvm.clang-cl=$(pwd)/clang-rust/bin/clang-cl.exe"
 fi
index 21874853839bd3b88002a7fac60594c1abd89fb1..546eb74b2cb5d0c90333e3d1c812f8ff72b7c852 100644 (file)
@@ -165,7 +165,7 @@ target | std | host | notes
 `armv7-wrs-vxworks-eabihf` | ? |  |
 `armv7a-none-eabihf` | * | | ARM Cortex-A, hardfloat
 `armv7s-apple-ios` | âœ“[^apple] |  |
-`avr-unknown-unknown` | ? |  | AVR
+`avr-unknown-gnu-atmega328` | âœ— |  | AVR. Requires `-Z build-std=core`
 `hexagon-unknown-linux-musl` | ? |  |
 `i386-apple-ios` | âœ“[^apple] |  | 32-bit x86 iOS
 `i686-apple-darwin` | âœ“ | âœ“ | 32-bit OSX (10.7+, Lion+)
index 31eedeaed0a684ac33e46bade44b4217314cdb4b..586355fe6136e9a62fb1000e5de12d2c70549941 100644 (file)
@@ -2058,7 +2058,7 @@ fn lower_async_fn_output_type_to_future_bound(
 
         hir::GenericBound::LangItemTrait(
             // ::std::future::Future<future_params>
-            hir::LangItem::FutureTraitLangItem,
+            hir::LangItem::Future,
             span,
             self.next_id(),
             future_args,
index 2e4b9f22f818c335552cd0e61907c8658665330c..d8ccaf16e28bc9aef60107c1267919fc33b91fee 100644 (file)
@@ -12,9 +12,9 @@ doctest = false
 
 [dependencies]
 bitflags = "1.0"
-flate2 = "1.0"
 libc = "0.2"
 measureme = "0.7.1"
+snap = "1"
 tracing = "0.1"
 rustc_middle = { path = "../librustc_middle" }
 rustc-demangle = "0.1"
index 4fef94dde5f7a3f41dc60a31cceff88262bd3172..a468d09c2d93d57e2e1a7ba9434833db66286c68 100644 (file)
@@ -485,7 +485,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
                 format!("{{{}{}}}", class, idx)
             } else if reg == InlineAsmReg::AArch64(AArch64InlineAsmReg::x30) {
                 // LLVM doesn't recognize x30
-                "lr".to_string()
+                "{lr}".to_string()
             } else {
                 format!("{{{}}}", reg.name())
             }
index b19199b9cfabd5bc80beb7dbc6fc167f3af7e0df..6a1b373ef071145332252ee5f64fd71737bd6719 100644 (file)
@@ -45,15 +45,12 @@ pub fn write_compressed_metadata<'tcx>(
     metadata: &EncodedMetadata,
     llvm_module: &mut ModuleLlvm,
 ) {
-    use flate2::write::DeflateEncoder;
-    use flate2::Compression;
+    use snap::write::FrameEncoder;
     use std::io::Write;
 
     let (metadata_llcx, metadata_llmod) = (&*llvm_module.llcx, llvm_module.llmod());
     let mut compressed = tcx.metadata_encoding_version();
-    DeflateEncoder::new(&mut compressed, Compression::fast())
-        .write_all(&metadata.raw_data)
-        .unwrap();
+    FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap();
 
     let llmeta = common::bytes_in_context(metadata_llcx, &compressed);
     let llconst = common::struct_in_context(metadata_llcx, &[llmeta], false);
index 26707fdf8395af9f59015b7a078a8a1389e2869e..1c51a9df5d884eeb4256a45dccf5282f922eb071 100644 (file)
@@ -82,6 +82,7 @@ pub struct CodegenCx<'ll, 'tcx> {
     pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
 
     eh_personality: Cell<Option<&'ll Value>>,
+    eh_catch_typeinfo: Cell<Option<&'ll Value>>,
     pub rust_try_fn: Cell<Option<&'ll Value>>,
 
     intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>,
@@ -311,6 +312,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
             coverage_cx,
             dbg_cx,
             eh_personality: Cell::new(None),
+            eh_catch_typeinfo: Cell::new(None),
             rust_try_fn: Cell::new(None),
             intrinsics: Default::default(),
             local_gen_sym_counter: Cell::new(0),
@@ -819,6 +821,25 @@ macro_rules! vector_types {
         }
         None
     }
+
+    crate fn eh_catch_typeinfo(&self) -> &'b Value {
+        if let Some(eh_catch_typeinfo) = self.eh_catch_typeinfo.get() {
+            return eh_catch_typeinfo;
+        }
+        let tcx = self.tcx;
+        assert!(self.sess().target.target.options.is_like_emscripten);
+        let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() {
+            Some(def_id) => self.get_static(def_id),
+            _ => {
+                let ty = self
+                    .type_struct(&[self.type_ptr_to(self.type_isize()), self.type_i8p()], false);
+                self.declare_global("rust_eh_catch_typeinfo", ty)
+            }
+        };
+        let eh_catch_typeinfo = self.const_bitcast(eh_catch_typeinfo, self.type_i8p());
+        self.eh_catch_typeinfo.set(Some(eh_catch_typeinfo));
+        eh_catch_typeinfo
+    }
 }
 
 impl<'b, 'tcx> CodegenCx<'b, 'tcx> {
index bb79a52dcf95cb58d3db0cab740db796e671720c..c1dfb83b1355f2154e3e7825144ba6e2a66e85ed 100644 (file)
@@ -855,6 +855,8 @@ fn try_intrinsic(
         bx.store(bx.const_i32(0), dest, ret_align);
     } else if wants_msvc_seh(bx.sess()) {
         codegen_msvc_try(bx, try_func, data, catch_func, dest);
+    } else if bx.sess().target.target.options.is_like_emscripten {
+        codegen_emcc_try(bx, try_func, data, catch_func, dest);
     } else {
         codegen_gnu_try(bx, try_func, data, catch_func, dest);
     }
@@ -880,7 +882,8 @@ fn codegen_msvc_try(
 
         let mut normal = bx.build_sibling_block("normal");
         let mut catchswitch = bx.build_sibling_block("catchswitch");
-        let mut catchpad = bx.build_sibling_block("catchpad");
+        let mut catchpad_rust = bx.build_sibling_block("catchpad_rust");
+        let mut catchpad_foreign = bx.build_sibling_block("catchpad_foreign");
         let mut caught = bx.build_sibling_block("caught");
 
         let try_func = llvm::get_param(bx.llfn(), 0);
@@ -890,21 +893,26 @@ fn codegen_msvc_try(
         // We're generating an IR snippet that looks like:
         //
         //   declare i32 @rust_try(%try_func, %data, %catch_func) {
-        //      %slot = alloca u8*
+        //      %slot = alloca i8*
         //      invoke %try_func(%data) to label %normal unwind label %catchswitch
         //
         //   normal:
         //      ret i32 0
         //
         //   catchswitch:
-        //      %cs = catchswitch within none [%catchpad] unwind to caller
+        //      %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller
         //
-        //   catchpad:
-        //      %tok = catchpad within %cs [%type_descriptor, 0, %slot]
+        //   catchpad_rust:
+        //      %tok = catchpad within %cs [%type_descriptor, 8, %slot]
         //      %ptr = load %slot
         //      call %catch_func(%data, %ptr)
         //      catchret from %tok to label %caught
         //
+        //   catchpad_foreign:
+        //      %tok = catchpad within %cs [null, 64, null]
+        //      call %catch_func(%data, null)
+        //      catchret from %tok to label %caught
+        //
         //   caught:
         //      ret i32 1
         //   }
@@ -912,13 +920,11 @@ fn codegen_msvc_try(
         // This structure follows the basic usage of throw/try/catch in LLVM.
         // For example, compile this C++ snippet to see what LLVM generates:
         //
-        //      #include <stdint.h>
-        //
         //      struct rust_panic {
         //          rust_panic(const rust_panic&);
         //          ~rust_panic();
         //
-        //          uint64_t x[2];
+        //          void* x[2];
         //      };
         //
         //      int __rust_try(
@@ -932,6 +938,9 @@ fn codegen_msvc_try(
         //          } catch(rust_panic& a) {
         //              catch_func(data, &a);
         //              return 1;
+        //          } catch(...) {
+        //              catch_func(data, NULL);
+        //              return 1;
         //          }
         //      }
         //
@@ -942,8 +951,9 @@ fn codegen_msvc_try(
 
         normal.ret(bx.const_i32(0));
 
-        let cs = catchswitch.catch_switch(None, None, 1);
-        catchswitch.add_handler(cs, catchpad.llbb());
+        let cs = catchswitch.catch_switch(None, None, 2);
+        catchswitch.add_handler(cs, catchpad_rust.llbb());
+        catchswitch.add_handler(cs, catchpad_foreign.llbb());
 
         // We can't use the TypeDescriptor defined in libpanic_unwind because it
         // might be in another DLL and the SEH encoding only supports specifying
@@ -977,11 +987,17 @@ fn codegen_msvc_try(
         //
         // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
         let flags = bx.const_i32(8);
-        let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]);
-        let ptr = catchpad.load(slot, ptr_align);
-        catchpad.call(catch_func, &[data, ptr], Some(&funclet));
-
-        catchpad.catch_ret(&funclet, caught.llbb());
+        let funclet = catchpad_rust.catch_pad(cs, &[tydesc, flags, slot]);
+        let ptr = catchpad_rust.load(slot, ptr_align);
+        catchpad_rust.call(catch_func, &[data, ptr], Some(&funclet));
+        catchpad_rust.catch_ret(&funclet, caught.llbb());
+
+        // The flag value of 64 indicates a "catch-all".
+        let flags = bx.const_i32(64);
+        let null = bx.const_null(bx.type_i8p());
+        let funclet = catchpad_foreign.catch_pad(cs, &[null, flags, null]);
+        catchpad_foreign.call(catch_func, &[data, null], Some(&funclet));
+        catchpad_foreign.catch_ret(&funclet, caught.llbb());
 
         caught.ret(bx.const_i32(1));
     });
@@ -1044,13 +1060,7 @@ fn codegen_gnu_try(
         // rust_try ignores the selector.
         let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
         let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1);
-        let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() {
-            Some(tydesc) => {
-                let tydesc = bx.get_static(tydesc);
-                bx.bitcast(tydesc, bx.type_i8p())
-            }
-            None => bx.const_null(bx.type_i8p()),
-        };
+        let tydesc = bx.const_null(bx.type_i8p());
         catch.add_clause(vals, tydesc);
         let ptr = catch.extract_value(vals, 0);
         catch.call(catch_func, &[data, ptr], None);
@@ -1064,6 +1074,88 @@ fn codegen_gnu_try(
     bx.store(ret, dest, i32_align);
 }
 
+// Variant of codegen_gnu_try used for emscripten where Rust panics are
+// implemented using C++ exceptions. Here we use exceptions of a specific type
+// (`struct rust_panic`) to represent Rust panics.
+fn codegen_emcc_try(
+    bx: &mut Builder<'a, 'll, 'tcx>,
+    try_func: &'ll Value,
+    data: &'ll Value,
+    catch_func: &'ll Value,
+    dest: &'ll Value,
+) {
+    let llfn = get_rust_try_fn(bx, &mut |mut bx| {
+        // Codegens the shims described above:
+        //
+        //   bx:
+        //      invoke %try_func(%data) normal %normal unwind %catch
+        //
+        //   normal:
+        //      ret 0
+        //
+        //   catch:
+        //      (%ptr, %selector) = landingpad
+        //      %rust_typeid = @llvm.eh.typeid.for(@_ZTI10rust_panic)
+        //      %is_rust_panic = %selector == %rust_typeid
+        //      %catch_data = alloca { i8*, i8 }
+        //      %catch_data[0] = %ptr
+        //      %catch_data[1] = %is_rust_panic
+        //      call %catch_func(%data, %catch_data)
+        //      ret 1
+
+        bx.sideeffect();
+
+        let mut then = bx.build_sibling_block("then");
+        let mut catch = bx.build_sibling_block("catch");
+
+        let try_func = llvm::get_param(bx.llfn(), 0);
+        let data = llvm::get_param(bx.llfn(), 1);
+        let catch_func = llvm::get_param(bx.llfn(), 2);
+        bx.invoke(try_func, &[data], then.llbb(), catch.llbb(), None);
+        then.ret(bx.const_i32(0));
+
+        // Type indicator for the exception being thrown.
+        //
+        // The first value in this tuple is a pointer to the exception object
+        // being thrown.  The second value is a "selector" indicating which of
+        // the landing pad clauses the exception's type had been matched to.
+        let tydesc = bx.eh_catch_typeinfo();
+        let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
+        let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 2);
+        catch.add_clause(vals, tydesc);
+        catch.add_clause(vals, bx.const_null(bx.type_i8p()));
+        let ptr = catch.extract_value(vals, 0);
+        let selector = catch.extract_value(vals, 1);
+
+        // Check if the typeid we got is the one for a Rust panic.
+        let llvm_eh_typeid_for = bx.get_intrinsic("llvm.eh.typeid.for");
+        let rust_typeid = catch.call(llvm_eh_typeid_for, &[tydesc], None);
+        let is_rust_panic = catch.icmp(IntPredicate::IntEQ, selector, rust_typeid);
+        let is_rust_panic = catch.zext(is_rust_panic, bx.type_bool());
+
+        // We need to pass two values to catch_func (ptr and is_rust_panic), so
+        // create an alloca and pass a pointer to that.
+        let ptr_align = bx.tcx().data_layout.pointer_align.abi;
+        let i8_align = bx.tcx().data_layout.i8_align.abi;
+        let catch_data =
+            catch.alloca(bx.type_struct(&[bx.type_i8p(), bx.type_bool()], false), ptr_align);
+        let catch_data_0 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(0)]);
+        catch.store(ptr, catch_data_0, ptr_align);
+        let catch_data_1 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(1)]);
+        catch.store(is_rust_panic, catch_data_1, i8_align);
+        let catch_data = catch.bitcast(catch_data, bx.type_i8p());
+
+        catch.call(catch_func, &[data, catch_data], None);
+        catch.ret(bx.const_i32(1));
+    });
+
+    // Note that no invoke is used here because by definition this function
+    // can't panic (that's what it's catching).
+    let ret = bx.call(llfn, &[try_func, data, catch_func], None);
+    let i32_align = bx.tcx().data_layout.i32_align.abi;
+    bx.store(ret, dest, i32_align);
+}
+
 // Helper function to give a Block to a closure to codegen a shim function.
 // This is currently primarily used for the `try` intrinsic functions above.
 fn gen_fn<'ll, 'tcx>(
index b28cb071de6592d18de7b4708da301bfcc5509cb..77c12c410d5f97cb48dfd06dee64bf971e9cf2fc 100644 (file)
@@ -31,7 +31,7 @@
 use rustc_data_structures::sync::{par_iter, Lock, ParallelIterator};
 use rustc_hir as hir;
 use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE};
-use rustc_hir::lang_items::StartFnLangItem;
+use rustc_hir::lang_items::LangItem;
 use rustc_index::vec::Idx;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::middle::cstore::EncodedMetadata;
@@ -458,7 +458,7 @@ fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx);
 
         let (start_fn, args) = if use_start_lang_item {
-            let start_def_id = cx.tcx().require_lang_item(StartFnLangItem, None);
+            let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None);
             let start_fn = cx.get_fn_addr(
                 ty::Instance::resolve(
                     cx.tcx(),
index a4e039de4df587dcc101a7571afbfc4f876cb15b..8048a569f79ea7b369ccc119783210b5bde04298 100644 (file)
@@ -10,7 +10,7 @@
 use crate::MemFlags;
 
 use rustc_ast as ast;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_index::vec::Idx;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{AllocId, ConstValue, Pointer, Scalar};
@@ -420,14 +420,14 @@ fn codegen_assert_terminator(
                 let index = self.codegen_operand(&mut bx, index).immediate();
                 // It's `fn panic_bounds_check(index: usize, len: usize)`,
                 // and `#[track_caller]` adds an implicit third argument.
-                (lang_items::PanicBoundsCheckFnLangItem, vec![index, len, location])
+                (LangItem::PanicBoundsCheck, vec![index, len, location])
             }
             _ => {
                 let msg_str = Symbol::intern(msg.description());
                 let msg = bx.const_str(msg_str);
                 // It's `pub fn panic(expr: &str)`, with the wide reference being passed
                 // as two arguments, and `#[track_caller]` adds an implicit third argument.
-                (lang_items::PanicFnLangItem, vec![msg.0, msg.1, location])
+                (LangItem::Panic, vec![msg.0, msg.1, location])
             }
         };
 
@@ -492,8 +492,7 @@ enum AssertIntrinsic {
 
                 // Obtain the panic entry point.
                 // FIXME: dedup this with `codegen_assert_terminator` above.
-                let def_id =
-                    common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
+                let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::Panic);
                 let instance = ty::Instance::mono(bx.tcx(), def_id);
                 let fn_abi = FnAbi::of_instance(bx, instance, &[]);
                 let llfn = bx.get_fn_addr(instance);
index 77e94fe3d0adc150d311e798a8da022a6d2268b1..71f924df119a3ba6ebb47040dd051938e8f3c688 100644 (file)
@@ -8,7 +8,7 @@
 use crate::MemFlags;
 
 use rustc_apfloat::{ieee, Float, Round, Status};
-use rustc_hir::lang_items::ExchangeMallocFnLangItem;
+use rustc_hir::lang_items::LangItem;
 use rustc_middle::mir;
 use rustc_middle::ty::cast::{CastTy, IntTy};
 use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
@@ -507,7 +507,7 @@ pub fn codegen_rvalue_operand(
                 let llty_ptr = bx.cx().backend_type(box_layout);
 
                 // Allocate space:
-                let def_id = match bx.tcx().lang_items().require(ExchangeMallocFnLangItem) {
+                let def_id = match bx.tcx().lang_items().require(LangItem::ExchangeMalloc) {
                     Ok(id) => id,
                     Err(s) => {
                         bx.cx().sess().fatal(&format!("allocation of `{}` {}", box_layout.ty, s));
index c01574e413cfaa62ec8077e3b6ce3075b373299c..e112674fbcc49b7d95d33a903ffc6b2bd2ee022d 100644 (file)
@@ -2,24 +2,20 @@ Multiple candidate files were found for an out-of-line module.
 
 Erroneous code example:
 
-```rust
+```ignore (multiple source files required for compile_fail)
 // file: ambiguous_module/mod.rs
 
 fn foo() {}
-```
 
-```rust
 // file: ambiguous_module.rs
 
 fn foo() {}
-```
 
-```ignore (multiple source files required for compile_fail)
+// file: lib.rs
+
 mod ambiguous_module; // error: file for module `ambiguous_module`
                       // found at both ambiguous_module.rs and
                       // ambiguous_module.rs/mod.rs
-
-fn main() {}
 ```
 
 Please remove this ambiguity by deleting/renaming one of the candidate files.
index fb7fced27c28056b225947f9aeba2416543ce8c5..0d61dc037c65a56f6eaaa64542df6896b88b629c 100644 (file)
@@ -451,13 +451,19 @@ pub fn macro_kind(self) -> Option<MacroKind> {
         }
     }
 
-    pub fn matches_ns(&self, ns: Namespace) -> bool {
+    /// Returns `None` if this is `Res::Err`
+    pub fn ns(&self) -> Option<Namespace> {
         match self {
-            Res::Def(kind, ..) => kind.ns() == Some(ns),
-            Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => ns == Namespace::TypeNS,
-            Res::SelfCtor(..) | Res::Local(..) => ns == Namespace::ValueNS,
-            Res::NonMacroAttr(..) => ns == Namespace::MacroNS,
-            Res::Err => true,
+            Res::Def(kind, ..) => kind.ns(),
+            Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => Some(Namespace::TypeNS),
+            Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS),
+            Res::NonMacroAttr(..) => Some(Namespace::MacroNS),
+            Res::Err => None,
         }
     }
+
+    /// Always returns `true` if `self` is `Res::Err`
+    pub fn matches_ns(&self, ns: Namespace) -> bool {
+        self.ns().map_or(true, |actual_ns| actual_ns == ns)
+    }
 }
index 978f73760ecb5e6eb1cc74e7789acaa527f4947b..acf6847c014f14e6a2461f7be547aa26d5f2d1ed 100644 (file)
@@ -7,8 +7,6 @@
 //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`.
 //! * Functions called by the compiler itself.
 
-pub use self::LangItem::*;
-
 use crate::def_id::DefId;
 use crate::{MethodKind, Target};
 
@@ -57,14 +55,14 @@ impl LangItem {
             /// that is `#[lang = "eq"]` would result in `sym::eq`.
             pub fn name(self) -> Symbol {
                 match self {
-                    $( $variant => $name, )*
+                    $( LangItem::$variant => $name, )*
                 }
             }
 
             pub fn group(self) -> Option<LangItemGroup> {
                 use LangItemGroup::*;
                 match self {
-                    $( $variant => expand_group!($($group)*), )*
+                    $( LangItem::$variant => expand_group!($($group)*), )*
                 }
             }
         }
@@ -87,7 +85,7 @@ pub fn new() -> Self {
                 fn init_none(_: LangItem) -> Option<DefId> { None }
 
                 Self {
-                    items: vec![$(init_none($variant)),*],
+                    items: vec![$(init_none(LangItem::$variant)),*],
                     missing: Vec::new(),
                     groups: [vec![]; NUM_GROUPS],
                 }
@@ -114,7 +112,7 @@ pub fn group(&self, group: LangItemGroup) -> &[DefId] {
                 /// exists.
                 #[allow(dead_code)]
                 pub fn $method(&self) -> Option<DefId> {
-                    self.items[$variant as usize]
+                    self.items[LangItem::$variant as usize]
                 }
             )*
         }
@@ -123,7 +121,7 @@ pub fn $method(&self) -> Option<DefId> {
             /// A mapping from the name of the lang item to its order and the form it must be of.
             pub static ref ITEM_REFS: FxHashMap<Symbol, (usize, Target)> = {
                 let mut item_refs = FxHashMap::default();
-                $( item_refs.insert($name, ($variant as usize, $target)); )*
+                $( item_refs.insert($name, (LangItem::$variant as usize, $target)); )*
                 item_refs
             };
         }
@@ -161,179 +159,176 @@ pub fn extract<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<(Sym
 }
 
 language_item_table! {
-//  Variant name,                  Name,                    Method name,             Target;
-    BoolImplItem,                  sym::bool,               bool_impl,               Target::Impl;
-    CharImplItem,                  sym::char,               char_impl,               Target::Impl;
-    StrImplItem,                   sym::str,                str_impl,                Target::Impl;
-    ArrayImplItem,                 sym::array,              array_impl,              Target::Impl;
-    SliceImplItem,                 sym::slice,              slice_impl,              Target::Impl;
-    SliceU8ImplItem,               sym::slice_u8,           slice_u8_impl,           Target::Impl;
-    StrAllocImplItem,              sym::str_alloc,          str_alloc_impl,          Target::Impl;
-    SliceAllocImplItem,            sym::slice_alloc,        slice_alloc_impl,        Target::Impl;
-    SliceU8AllocImplItem,          sym::slice_u8_alloc,     slice_u8_alloc_impl,     Target::Impl;
-    ConstPtrImplItem,              sym::const_ptr,          const_ptr_impl,          Target::Impl;
-    MutPtrImplItem,                sym::mut_ptr,            mut_ptr_impl,            Target::Impl;
-    ConstSlicePtrImplItem,         sym::const_slice_ptr,    const_slice_ptr_impl,    Target::Impl;
-    MutSlicePtrImplItem,           sym::mut_slice_ptr,      mut_slice_ptr_impl,      Target::Impl;
-    I8ImplItem,                    sym::i8,                 i8_impl,                 Target::Impl;
-    I16ImplItem,                   sym::i16,                i16_impl,                Target::Impl;
-    I32ImplItem,                   sym::i32,                i32_impl,                Target::Impl;
-    I64ImplItem,                   sym::i64,                i64_impl,                Target::Impl;
-    I128ImplItem,                  sym::i128,               i128_impl,               Target::Impl;
-    IsizeImplItem,                 sym::isize,              isize_impl,              Target::Impl;
-    U8ImplItem,                    sym::u8,                 u8_impl,                 Target::Impl;
-    U16ImplItem,                   sym::u16,                u16_impl,                Target::Impl;
-    U32ImplItem,                   sym::u32,                u32_impl,                Target::Impl;
-    U64ImplItem,                   sym::u64,                u64_impl,                Target::Impl;
-    U128ImplItem,                  sym::u128,               u128_impl,               Target::Impl;
-    UsizeImplItem,                 sym::usize,              usize_impl,              Target::Impl;
-    F32ImplItem,                   sym::f32,                f32_impl,                Target::Impl;
-    F64ImplItem,                   sym::f64,                f64_impl,                Target::Impl;
-    F32RuntimeImplItem,            sym::f32_runtime,        f32_runtime_impl,        Target::Impl;
-    F64RuntimeImplItem,            sym::f64_runtime,        f64_runtime_impl,        Target::Impl;
-
-    SizedTraitLangItem,            sym::sized,              sized_trait,             Target::Trait;
-    UnsizeTraitLangItem,           sym::unsize,             unsize_trait,            Target::Trait;
-    // trait injected by #[derive(PartialEq)], (i.e. "Partial EQ").
-    StructuralPeqTraitLangItem,    sym::structural_peq,     structural_peq_trait,    Target::Trait;
-    // trait injected by #[derive(Eq)], (i.e. "Total EQ"; no, I will not apologize).
-    StructuralTeqTraitLangItem,    sym::structural_teq,     structural_teq_trait,    Target::Trait;
-    CopyTraitLangItem,             sym::copy,               copy_trait,              Target::Trait;
-    CloneTraitLangItem,            sym::clone,              clone_trait,             Target::Trait;
-    SyncTraitLangItem,             sym::sync,               sync_trait,              Target::Trait;
-    DiscriminantKindTraitLangItem, sym::discriminant_kind,  discriminant_kind_trait, Target::Trait;
+//  Variant name,            Name,                    Method name,             Target;
+    Bool,                    sym::bool,                bool_impl,                  Target::Impl;
+    Char,                    sym::char,                char_impl,                  Target::Impl;
+    Str,                     sym::str,                 str_impl,                   Target::Impl;
+    Array,                   sym::array,               array_impl,                 Target::Impl;
+    Slice,                   sym::slice,               slice_impl,                 Target::Impl;
+    SliceU8,                 sym::slice_u8,            slice_u8_impl,              Target::Impl;
+    StrAlloc,                sym::str_alloc,           str_alloc_impl,             Target::Impl;
+    SliceAlloc,              sym::slice_alloc,         slice_alloc_impl,           Target::Impl;
+    SliceU8Alloc,            sym::slice_u8_alloc,      slice_u8_alloc_impl,        Target::Impl;
+    ConstPtr,                sym::const_ptr,           const_ptr_impl,             Target::Impl;
+    MutPtr,                  sym::mut_ptr,             mut_ptr_impl,               Target::Impl;
+    ConstSlicePtr,           sym::const_slice_ptr,     const_slice_ptr_impl,       Target::Impl;
+    MutSlicePtr,             sym::mut_slice_ptr,       mut_slice_ptr_impl,         Target::Impl;
+    I8,                      sym::i8,                  i8_impl,                    Target::Impl;
+    I16,                     sym::i16,                 i16_impl,                   Target::Impl;
+    I32,                     sym::i32,                 i32_impl,                   Target::Impl;
+    I64,                     sym::i64,                 i64_impl,                   Target::Impl;
+    I128,                    sym::i128,                i128_impl,                  Target::Impl;
+    Isize,                   sym::isize,               isize_impl,                 Target::Impl;
+    U8,                      sym::u8,                  u8_impl,                    Target::Impl;
+    U16,                     sym::u16,                 u16_impl,                   Target::Impl;
+    U32,                     sym::u32,                 u32_impl,                   Target::Impl;
+    U64,                     sym::u64,                 u64_impl,                   Target::Impl;
+    U128,                    sym::u128,                u128_impl,                  Target::Impl;
+    Usize,                   sym::usize,               usize_impl,                 Target::Impl;
+    F32,                     sym::f32,                 f32_impl,                   Target::Impl;
+    F64,                     sym::f64,                 f64_impl,                   Target::Impl;
+    F32Runtime,              sym::f32_runtime,         f32_runtime_impl,           Target::Impl;
+    F64Runtime,              sym::f64_runtime,         f64_runtime_impl,           Target::Impl;
+
+    Sized,                   sym::sized,               sized_trait,                Target::Trait;
+    Unsize,                  sym::unsize,              unsize_trait,               Target::Trait;
+    // Trait injected by #[derive(PartialEq)], (i.e. "Partial EQ").
+    StructuralPeq,           sym::structural_peq,      structural_peq_trait,       Target::Trait;
+    // Trait injected by #[derive(Eq)], (i.e. "Total EQ"; no, I will not apologize).
+    StructuralTeq,           sym::structural_teq,      structural_teq_trait,       Target::Trait;
+    Copy,                    sym::copy,                copy_trait,                 Target::Trait;
+    Clone,                   sym::clone,               clone_trait,                Target::Trait;
+    Sync,                    sym::sync,                sync_trait,                 Target::Trait;
+    DiscriminantKind,        sym::discriminant_kind,   discriminant_kind_trait,    Target::Trait;
     // The associated item of `trait DiscriminantKind`.
-    DiscriminantTypeLangItem,      sym::discriminant_type,  discriminant_type,       Target::AssocTy;
-
-    FreezeTraitLangItem,           sym::freeze,             freeze_trait,            Target::Trait;
-
-    DropTraitLangItem,             sym::drop,               drop_trait,              Target::Trait;
-
-    CoerceUnsizedTraitLangItem,    sym::coerce_unsized,     coerce_unsized_trait,    Target::Trait;
-    DispatchFromDynTraitLangItem,  sym::dispatch_from_dyn,  dispatch_from_dyn_trait, Target::Trait;
-
-    AddTraitLangItem(Op),          sym::add,                add_trait,               Target::Trait;
-    SubTraitLangItem(Op),          sym::sub,                sub_trait,               Target::Trait;
-    MulTraitLangItem(Op),          sym::mul,                mul_trait,               Target::Trait;
-    DivTraitLangItem(Op),          sym::div,                div_trait,               Target::Trait;
-    RemTraitLangItem(Op),          sym::rem,                rem_trait,               Target::Trait;
-    NegTraitLangItem(Op),          sym::neg,                neg_trait,               Target::Trait;
-    NotTraitLangItem(Op),          sym::not,                not_trait,               Target::Trait;
-    BitXorTraitLangItem(Op),       sym::bitxor,             bitxor_trait,            Target::Trait;
-    BitAndTraitLangItem(Op),       sym::bitand,             bitand_trait,            Target::Trait;
-    BitOrTraitLangItem(Op),        sym::bitor,              bitor_trait,             Target::Trait;
-    ShlTraitLangItem(Op),          sym::shl,                shl_trait,               Target::Trait;
-    ShrTraitLangItem(Op),          sym::shr,                shr_trait,               Target::Trait;
-    AddAssignTraitLangItem(Op),    sym::add_assign,         add_assign_trait,        Target::Trait;
-    SubAssignTraitLangItem(Op),    sym::sub_assign,         sub_assign_trait,        Target::Trait;
-    MulAssignTraitLangItem(Op),    sym::mul_assign,         mul_assign_trait,        Target::Trait;
-    DivAssignTraitLangItem(Op),    sym::div_assign,         div_assign_trait,        Target::Trait;
-    RemAssignTraitLangItem(Op),    sym::rem_assign,         rem_assign_trait,        Target::Trait;
-    BitXorAssignTraitLangItem(Op), sym::bitxor_assign,      bitxor_assign_trait,     Target::Trait;
-    BitAndAssignTraitLangItem(Op), sym::bitand_assign,      bitand_assign_trait,     Target::Trait;
-    BitOrAssignTraitLangItem(Op),  sym::bitor_assign,       bitor_assign_trait,      Target::Trait;
-    ShlAssignTraitLangItem(Op),    sym::shl_assign,         shl_assign_trait,        Target::Trait;
-    ShrAssignTraitLangItem(Op),    sym::shr_assign,         shr_assign_trait,        Target::Trait;
-    IndexTraitLangItem(Op),        sym::index,              index_trait,             Target::Trait;
-    IndexMutTraitLangItem(Op),     sym::index_mut,          index_mut_trait,         Target::Trait;
-
-    UnsafeCellTypeLangItem,        sym::unsafe_cell,        unsafe_cell_type,        Target::Struct;
-    VaListTypeLangItem,            sym::va_list,            va_list,                 Target::Struct;
-
-    DerefTraitLangItem,            sym::deref,              deref_trait,             Target::Trait;
-    DerefMutTraitLangItem,         sym::deref_mut,          deref_mut_trait,         Target::Trait;
-    ReceiverTraitLangItem,         sym::receiver,           receiver_trait,          Target::Trait;
-
-    FnTraitLangItem,               kw::Fn,                  fn_trait,                Target::Trait;
-    FnMutTraitLangItem,            sym::fn_mut,             fn_mut_trait,            Target::Trait;
-    FnOnceTraitLangItem,           sym::fn_once,            fn_once_trait,           Target::Trait;
-
-    FnOnceOutputLangItem,          sym::fn_once_output,     fn_once_output,          Target::AssocTy;
-
-    FutureTraitLangItem,           sym::future_trait,       future_trait,            Target::Trait;
-    GeneratorStateLangItem,        sym::generator_state,    gen_state,               Target::Enum;
-    GeneratorTraitLangItem,        sym::generator,          gen_trait,               Target::Trait;
-    UnpinTraitLangItem,            sym::unpin,              unpin_trait,             Target::Trait;
-    PinTypeLangItem,               sym::pin,                pin_type,                Target::Struct;
-
-    // Don't be fooled by the naming here: this lang item denotes `PartialEq`, not `Eq`.
-    EqTraitLangItem,               sym::eq,                 eq_trait,                Target::Trait;
-    PartialOrdTraitLangItem,       sym::partial_ord,        partial_ord_trait,       Target::Trait;
-
-    // A number of panic-related lang items. The `panic` item corresponds to
-    // divide-by-zero and various panic cases with `match`. The
-    // `panic_bounds_check` item is for indexing arrays.
+    Discriminant,            sym::discriminant_type,   discriminant_type,          Target::AssocTy;
+
+    Freeze,                  sym::freeze,              freeze_trait,               Target::Trait;
+
+    Drop,                    sym::drop,                drop_trait,                 Target::Trait;
+
+    CoerceUnsized,           sym::coerce_unsized,      coerce_unsized_trait,       Target::Trait;
+    DispatchFromDyn,         sym::dispatch_from_dyn,   dispatch_from_dyn_trait,    Target::Trait;
+
+    Add(Op),                 sym::add,                 add_trait,                  Target::Trait;
+    Sub(Op),                 sym::sub,                 sub_trait,                  Target::Trait;
+    Mul(Op),                 sym::mul,                 mul_trait,                  Target::Trait;
+    Div(Op),                 sym::div,                 div_trait,                  Target::Trait;
+    Rem(Op),                 sym::rem,                 rem_trait,                  Target::Trait;
+    Neg(Op),                 sym::neg,                 neg_trait,                  Target::Trait;
+    Not(Op),                 sym::not,                 not_trait,                  Target::Trait;
+    BitXor(Op),              sym::bitxor,              bitxor_trait,               Target::Trait;
+    BitAnd(Op),              sym::bitand,              bitand_trait,               Target::Trait;
+    BitOr(Op),               sym::bitor,               bitor_trait,                Target::Trait;
+    Shl(Op),                 sym::shl,                 shl_trait,                  Target::Trait;
+    Shr(Op),                 sym::shr,                 shr_trait,                  Target::Trait;
+    AddAssign(Op),           sym::add_assign,          add_assign_trait,           Target::Trait;
+    SubAssign(Op),           sym::sub_assign,          sub_assign_trait,           Target::Trait;
+    MulAssign(Op),           sym::mul_assign,          mul_assign_trait,           Target::Trait;
+    DivAssign(Op),           sym::div_assign,          div_assign_trait,           Target::Trait;
+    RemAssign(Op),           sym::rem_assign,          rem_assign_trait,           Target::Trait;
+    BitXorAssign(Op),        sym::bitxor_assign,       bitxor_assign_trait,        Target::Trait;
+    BitAndAssign(Op),        sym::bitand_assign,       bitand_assign_trait,        Target::Trait;
+    BitOrAssign(Op),         sym::bitor_assign,        bitor_assign_trait,         Target::Trait;
+    ShlAssign(Op),           sym::shl_assign,          shl_assign_trait,           Target::Trait;
+    ShrAssign(Op),           sym::shr_assign,          shr_assign_trait,           Target::Trait;
+    Index(Op),               sym::index,               index_trait,                Target::Trait;
+    IndexMut(Op),            sym::index_mut,           index_mut_trait,            Target::Trait;
+
+    UnsafeCell,              sym::unsafe_cell,         unsafe_cell_type,           Target::Struct;
+    VaList,                  sym::va_list,             va_list,                    Target::Struct;
+
+    Deref,                   sym::deref,               deref_trait,                Target::Trait;
+    DerefMut,                sym::deref_mut,           deref_mut_trait,            Target::Trait;
+    Receiver,                sym::receiver,            receiver_trait,             Target::Trait;
+
+    Fn,                      kw::Fn,                   fn_trait,                   Target::Trait;
+    FnMut,                   sym::fn_mut,              fn_mut_trait,               Target::Trait;
+    FnOnce,                  sym::fn_once,             fn_once_trait,              Target::Trait;
+
+    FnOnceOutput,            sym::fn_once_output,      fn_once_output,             Target::AssocTy;
+
+    Future,                  sym::future_trait,        future_trait,               Target::Trait;
+    GeneratorState,          sym::generator_state,     gen_state,                  Target::Enum;
+    Generator,               sym::generator,           gen_trait,                  Target::Trait;
+    Unpin,                   sym::unpin,               unpin_trait,                Target::Trait;
+    Pin,                     sym::pin,                 pin_type,                   Target::Struct;
+
+    PartialEq,               sym::eq,                  eq_trait,                   Target::Trait;
+    PartialOrd,              sym::partial_ord,         partial_ord_trait,          Target::Trait;
+
+    // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
+    // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays.
     //
-    // The `begin_unwind` lang item has a predefined symbol name and is sort of
-    // a "weak lang item" in the sense that a crate is not required to have it
-    // defined to use it, but a final product is required to define it
-    // somewhere. Additionally, there are restrictions on crates that use a weak
-    // lang item, but do not have it defined.
-    PanicFnLangItem,               sym::panic,              panic_fn,                Target::Fn;
-    PanicBoundsCheckFnLangItem,    sym::panic_bounds_check, panic_bounds_check_fn,   Target::Fn;
-    PanicInfoLangItem,             sym::panic_info,         panic_info,              Target::Struct;
-    PanicLocationLangItem,         sym::panic_location,     panic_location,          Target::Struct;
-    PanicImplLangItem,             sym::panic_impl,         panic_impl,              Target::Fn;
-    // Libstd panic entry point. Necessary for const eval to be able to catch it
-    BeginPanicFnLangItem,          sym::begin_panic,        begin_panic_fn,          Target::Fn;
+    // The `begin_unwind` lang item has a predefined symbol name and is sort of a "weak lang item"
+    // in the sense that a crate is not required to have it defined to use it, but a final product
+    // is required to define it somewhere. Additionally, there are restrictions on crates that use
+    // a weak lang item, but do not have it defined.
+    Panic,                   sym::panic,               panic_fn,                   Target::Fn;
+    PanicBoundsCheck,        sym::panic_bounds_check,  panic_bounds_check_fn,      Target::Fn;
+    PanicInfo,               sym::panic_info,          panic_info,                 Target::Struct;
+    PanicLocation,           sym::panic_location,      panic_location,             Target::Struct;
+    PanicImpl,               sym::panic_impl,          panic_impl,                 Target::Fn;
+    // libstd panic entry point. Necessary for const eval to be able to catch it
+    BeginPanic,              sym::begin_panic,         begin_panic_fn,             Target::Fn;
 
-    ExchangeMallocFnLangItem,      sym::exchange_malloc,    exchange_malloc_fn,      Target::Fn;
-    BoxFreeFnLangItem,             sym::box_free,           box_free_fn,             Target::Fn;
-    DropInPlaceFnLangItem,         sym::drop_in_place,      drop_in_place_fn,        Target::Fn;
-    OomLangItem,                   sym::oom,                oom,                     Target::Fn;
-    AllocLayoutLangItem,           sym::alloc_layout,       alloc_layout,            Target::Struct;
+    ExchangeMalloc,          sym::exchange_malloc,     exchange_malloc_fn,         Target::Fn;
+    BoxFree,                 sym::box_free,            box_free_fn,                Target::Fn;
+    DropInPlace,             sym::drop_in_place,       drop_in_place_fn,           Target::Fn;
+    Oom,                     sym::oom,                 oom,                        Target::Fn;
+    AllocLayout,             sym::alloc_layout,        alloc_layout,               Target::Struct;
 
-    StartFnLangItem,               sym::start,              start_fn,                Target::Fn;
+    Start,                   sym::start,               start_fn,                   Target::Fn;
 
-    EhPersonalityLangItem,         sym::eh_personality,     eh_personality,          Target::Fn;
-    EhCatchTypeinfoLangItem,       sym::eh_catch_typeinfo,  eh_catch_typeinfo,       Target::Static;
+    EhPersonality,           sym::eh_personality,      eh_personality,             Target::Fn;
+    EhCatchTypeinfo,         sym::eh_catch_typeinfo,   eh_catch_typeinfo,          Target::Static;
 
-    OwnedBoxLangItem,              sym::owned_box,          owned_box,               Target::Struct;
+    OwnedBox,                sym::owned_box,           owned_box,                  Target::Struct;
 
-    PhantomDataItem,               sym::phantom_data,       phantom_data,            Target::Struct;
+    PhantomData,             sym::phantom_data,        phantom_data,               Target::Struct;
 
-    ManuallyDropItem,              sym::manually_drop,      manually_drop,           Target::Struct;
+    ManuallyDrop,            sym::manually_drop,       manually_drop,              Target::Struct;
 
-    MaybeUninitLangItem,           sym::maybe_uninit,       maybe_uninit,            Target::Union;
+    MaybeUninit,             sym::maybe_uninit,        maybe_uninit,               Target::Union;
 
     // Align offset for stride != 1; must not panic.
-    AlignOffsetLangItem,           sym::align_offset,       align_offset_fn,         Target::Fn;
+    AlignOffset,             sym::align_offset,        align_offset_fn,            Target::Fn;
 
-    TerminationTraitLangItem,      sym::termination,        termination,             Target::Trait;
+    Termination,             sym::termination,         termination,                Target::Trait;
 
-    TryTraitLangItem,              kw::Try,                 try_trait,               Target::Trait;
+    Try,                     kw::Try,                  try_trait,                  Target::Trait;
 
     // Language items from AST lowering
-    TryFromError,                  sym::from_error,         from_error_fn,           Target::Method(MethodKind::Trait { body: false });
-    TryFromOk,                     sym::from_ok,            from_ok_fn,              Target::Method(MethodKind::Trait { body: false });
-    TryIntoResult,                 sym::into_result,        into_result_fn,          Target::Method(MethodKind::Trait { body: false });
+    TryFromError,            sym::from_error,          from_error_fn,              Target::Method(MethodKind::Trait { body: false });
+    TryFromOk,               sym::from_ok,             from_ok_fn,                 Target::Method(MethodKind::Trait { body: false });
+    TryIntoResult,           sym::into_result,         into_result_fn,             Target::Method(MethodKind::Trait { body: false });
 
-    PollReady,                     sym::Ready,              poll_ready_variant,      Target::Variant;
-    PollPending,                   sym::Pending,            poll_pending_variant,    Target::Variant;
+    PollReady,               sym::Ready,               poll_ready_variant,         Target::Variant;
+    PollPending,             sym::Pending,             poll_pending_variant,       Target::Variant;
 
-    FromGenerator,                 sym::from_generator,     from_generator_fn,       Target::Fn;
-    GetContext,                    sym::get_context,        get_context_fn,          Target::Fn;
+    FromGenerator,           sym::from_generator,      from_generator_fn,          Target::Fn;
+    GetContext,              sym::get_context,         get_context_fn,             Target::Fn;
 
-    FuturePoll,                    sym::poll,               future_poll_fn,          Target::Method(MethodKind::Trait { body: false });
+    FuturePoll,              sym::poll,                future_poll_fn,             Target::Method(MethodKind::Trait { body: false });
 
-    FromFrom,                      sym::from,               from_fn,                 Target::Method(MethodKind::Trait { body: false });
+    FromFrom,                sym::from,                from_fn,                    Target::Method(MethodKind::Trait { body: false });
 
-    OptionSome,                    sym::Some,               option_some_variant,     Target::Variant;
-    OptionNone,                    sym::None,               option_none_variant,     Target::Variant;
+    OptionSome,              sym::Some,                option_some_variant,        Target::Variant;
+    OptionNone,              sym::None,                option_none_variant,        Target::Variant;
 
-    ResultOk,                      sym::Ok,                 result_ok_variant,       Target::Variant;
-    ResultErr,                     sym::Err,                result_err_variant,      Target::Variant;
+    ResultOk,                sym::Ok,                  result_ok_variant,          Target::Variant;
+    ResultErr,               sym::Err,                 result_err_variant,         Target::Variant;
 
-    IntoIterIntoIter,              sym::into_iter,          into_iter_fn,            Target::Method(MethodKind::Trait { body: false });
-    IteratorNext,                  sym::next,               next_fn,                 Target::Method(MethodKind::Trait { body: false});
+    IntoIterIntoIter,        sym::into_iter,           into_iter_fn,               Target::Method(MethodKind::Trait { body: false });
+    IteratorNext,            sym::next,                next_fn,                    Target::Method(MethodKind::Trait { body: false});
 
-    PinNewUnchecked,               sym::new_unchecked,      new_unchecked_fn,        Target::Method(MethodKind::Inherent);
+    PinNewUnchecked,         sym::new_unchecked,       new_unchecked_fn,           Target::Method(MethodKind::Inherent);
 
-    RangeFrom,                     sym::RangeFrom,           range_from_struct,          Target::Struct;
-    RangeFull,                     sym::RangeFull,           range_full_struct,          Target::Struct;
-    RangeInclusiveStruct,          sym::RangeInclusive,      range_inclusive_struct,     Target::Struct;
-    RangeInclusiveNew,             sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent);
-    Range,                         sym::Range,               range_struct,               Target::Struct;
-    RangeToInclusive,              sym::RangeToInclusive,    range_to_inclusive_struct,  Target::Struct;
-    RangeTo,                       sym::RangeTo,             range_to_struct,            Target::Struct;
+    RangeFrom,               sym::RangeFrom,           range_from_struct,          Target::Struct;
+    RangeFull,               sym::RangeFull,           range_full_struct,          Target::Struct;
+    RangeInclusiveStruct,    sym::RangeInclusive,      range_inclusive_struct,     Target::Struct;
+    RangeInclusiveNew,       sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent);
+    Range,                   sym::Range,               range_struct,               Target::Struct;
+    RangeToInclusive,        sym::RangeToInclusive,    range_to_inclusive_struct,  Target::Struct;
+    RangeTo,                 sym::RangeTo,             range_to_struct,            Target::Struct;
 }
index 76b95c696f7ca0dcb1e2af38233b467cdfba9cf8..129eec7d29efe0d4794e01f444f68c553da23737 100644 (file)
@@ -15,7 +15,7 @@ macro_rules! weak_lang_items {
 lazy_static! {
     pub static ref WEAK_ITEMS_REFS: FxHashMap<Symbol, LangItem> = {
         let mut map = FxHashMap::default();
-        $(map.insert(sym::$name, lang_items::$item);)*
+        $(map.insert(sym::$name, LangItem::$item);)*
         map
     };
 }
@@ -46,7 +46,8 @@ pub fn is_weak_lang_item(&self, item_def_id: DefId) -> bool {
 ) }
 
 weak_lang_items! {
-    panic_impl,         PanicImplLangItem,          rust_begin_unwind;
-    eh_personality,     EhPersonalityLangItem,      rust_eh_personality;
-    oom,                OomLangItem,                rust_oom;
+    panic_impl,         PanicImpl,          rust_begin_unwind;
+    eh_personality,     EhPersonality,      rust_eh_personality;
+    eh_catch_typeinfo,  EhCatchTypeinfo,    rust_eh_catch_typeinfo;
+    oom,                Oom,                rust_oom;
 }
index 8212958510a6f88bb5a672b35c5e1cccdb583a69..d72ed72e3a8aceaa19e070b0e4d08b52678cb6c1 100644 (file)
@@ -50,6 +50,7 @@
 use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
 
 use crate::infer;
+use crate::infer::OriginalQueryValues;
 use crate::traits::error_reporting::report_object_safety_error;
 use crate::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{Item, ItemKind, Node};
 use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::ParamEnvAnd;
 use rustc_middle::ty::{
     self,
     subst::{Subst, SubstsRef},
@@ -1529,6 +1532,7 @@ enum Mismatch<'a> {
         };
         if let Some(exp_found) = exp_found {
             self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
+            self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
         }
 
         // In some (most?) cases cause.body_id points to actual body, but in some cases
@@ -1547,6 +1551,62 @@ enum Mismatch<'a> {
         self.note_error_origin(diag, cause, exp_found);
     }
 
+    fn suggest_await_on_expect_found(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut DiagnosticBuilder<'tcx>,
+    ) {
+        debug!(
+            "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
+            exp_span, exp_found.expected, exp_found.found
+        );
+
+        if let ty::Opaque(def_id, _) = exp_found.expected.kind {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+            // Future::Output
+            let item_def_id = self
+                .tcx
+                .associated_items(future_trait)
+                .in_definition_order()
+                .next()
+                .unwrap()
+                .def_id;
+
+            let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
+            if let Some(projection_ty) = projection_ty {
+                let projection_query = self.canonicalize_query(
+                    &ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty },
+                    &mut OriginalQueryValues::default(),
+                );
+                if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) {
+                    let normalized_ty = resp.value.value.normalized_ty;
+                    debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty);
+                    if ty::TyS::same_type(normalized_ty, exp_found.found) {
+                        let span = if let ObligationCauseCode::Pattern {
+                            span,
+                            origin_expr: _,
+                            root_ty: _,
+                        } = cause.code
+                        {
+                            // scrutinee's span
+                            span.unwrap_or(exp_span)
+                        } else {
+                            exp_span
+                        };
+                        diag.span_suggestion_verbose(
+                            span.shrink_to_hi(),
+                            "consider awaiting on the future",
+                            ".await".to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
     /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
     /// suggests it.
     fn suggest_as_ref_where_appropriate(
index f135bde7f8337a0b12f488f5fd52ec2cfd583233..e05041d88460e3cf40fbd010f42f295fd8a12ec6 100644 (file)
@@ -22,7 +22,6 @@
 #![feature(extend_one)]
 #![feature(never_type)]
 #![feature(or_patterns)]
-#![feature(range_is_empty)]
 #![feature(in_band_lifetimes)]
 #![feature(crate_visibility_modifier)]
 #![recursion_limit = "512"] // For rustdoc
index 950771f0a692710021dc7e29c18736fdbbe3ea0b..28b56f6fef4b80861b86ccc76619f3bd82be76f4 100644 (file)
@@ -19,3 +19,6 @@ name = "rustc_lexer"
 # Note that this crate purposefully does not depend on other rustc crates
 [dependencies]
 unicode-xid = "0.2.0"
+
+[dev-dependencies]
+expect-test = "0.1"
index 2d80ca5a4de10449f99fbe4772dd42dc0c6ef62b..b7d6194cd77cf956d16ede3087c5b8a0a1ae4517 100644 (file)
@@ -35,6 +35,7 @@
 /// Parsed token.
 /// It doesn't contain information about data that has been parsed,
 /// only the type of the token and its size.
+#[derive(Debug)]
 pub struct Token {
     pub kind: TokenKind,
     pub len: usize,
index b0209ac2899d616fd7e0fab082ea4b2915e5ed1c..a1ea5ceb1f61283a2c5a5722f27bd1d9593552be 100644 (file)
@@ -1,5 +1,7 @@
 use super::*;
 
+use expect_test::{expect, Expect};
+
 fn check_raw_str(s: &str, expected_hashes: u16, expected_err: Option<RawStrError>) {
     let s = &format!("r{}", s);
     let mut cursor = Cursor::new(s);
@@ -120,3 +122,46 @@ fn test_shebang_followed_by_attrib() {
     let input = "#!/bin/rust-scripts\n#![allow_unused(true)]";
     assert_eq!(strip_shebang(input), Some(19));
 }
+
+fn check_lexing(src: &str, expect: Expect) {
+    let actual: String = tokenize(src).map(|token| format!("{:?}\n", token)).collect();
+    expect.assert_eq(&actual)
+}
+
+#[test]
+fn comment_flavors() {
+    check_lexing(
+        r"
+// line
+//// line as well
+/// outer doc line
+//! inner doc line
+/* block */
+/**/
+/*** also block */
+/** outer doc block */
+/*! inner doc block */
+",
+        expect![[r#"
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: LineComment { doc_style: None }, len: 7 }
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: LineComment { doc_style: None }, len: 17 }
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: LineComment { doc_style: Some(Outer) }, len: 18 }
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: LineComment { doc_style: Some(Inner) }, len: 18 }
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 }
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: BlockComment { doc_style: None, terminated: true }, len: 4 }
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: BlockComment { doc_style: None, terminated: true }, len: 18 }
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: BlockComment { doc_style: Some(Outer), terminated: true }, len: 22 }
+                Token { kind: Whitespace, len: 1 }
+                Token { kind: BlockComment { doc_style: Some(Inner), terminated: true }, len: 22 }
+                Token { kind: Whitespace, len: 1 }
+            "#]],
+    )
+}
index b337bf0a3f9226f7fecb3ccdfc4375a4791fd1b3..ea624b9ed300320005f09a304256ecfde485f372 100644 (file)
 use rustc_hir::def_id::DefId;
 use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind};
 use rustc_hir::{HirId, HirIdSet, Node};
+use rustc_index::vec::Idx;
 use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::ty::subst::{GenericArgKind, Subst};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt};
 use rustc_session::lint::FutureIncompatibleInfo;
 use rustc_session::Session;
 use rustc_span::edition::Edition;
@@ -2162,6 +2163,40 @@ fn structurally_same_type_impl<'tcx>(
             ckind: CItemKind,
         ) -> bool {
             debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b);
+            let tcx = cx.tcx;
+
+            // Given a transparent newtype, reach through and grab the inner
+            // type unless the newtype makes the type non-null.
+            let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> {
+                let mut ty = ty;
+                loop {
+                    if let ty::Adt(def, substs) = ty.kind {
+                        let is_transparent = def.subst(tcx, substs).repr.transparent();
+                        let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, &def);
+                        debug!(
+                            "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}",
+                            ty, is_transparent, is_non_null
+                        );
+                        if is_transparent && !is_non_null {
+                            debug_assert!(def.variants.len() == 1);
+                            let v = &def.variants[VariantIdx::new(0)];
+                            ty = v
+                                .transparent_newtype_field(tcx)
+                                .expect(
+                                    "single-variant transparent structure with zero-sized field",
+                                )
+                                .ty(tcx, substs);
+                            continue;
+                        }
+                    }
+                    debug!("non_transparent_ty -> {:?}", ty);
+                    return ty;
+                }
+            };
+
+            let a = non_transparent_ty(a);
+            let b = non_transparent_ty(b);
+
             if !seen_types.insert((a, b)) {
                 // We've encountered a cycle. There's no point going any further -- the types are
                 // structurally the same.
@@ -2177,11 +2212,17 @@ fn structurally_same_type_impl<'tcx>(
                 let a_kind = &a.kind;
                 let b_kind = &b.kind;
 
-                let compare_layouts = |a, b| -> bool {
-                    let a_layout = &cx.layout_of(a).unwrap().layout.abi;
-                    let b_layout = &cx.layout_of(b).unwrap().layout.abi;
-                    debug!("{:?} == {:?} = {}", a_layout, b_layout, a_layout == b_layout);
-                    a_layout == b_layout
+                let compare_layouts = |a, b| -> Result<bool, LayoutError<'tcx>> {
+                    debug!("compare_layouts({:?}, {:?})", a, b);
+                    let a_layout = &cx.layout_of(a)?.layout.abi;
+                    let b_layout = &cx.layout_of(b)?.layout.abi;
+                    debug!(
+                        "comparing layouts: {:?} == {:?} = {}",
+                        a_layout,
+                        b_layout,
+                        a_layout == b_layout
+                    );
+                    Ok(a_layout == b_layout)
                 };
 
                 #[allow(rustc::usage_of_ty_tykind)]
@@ -2196,11 +2237,19 @@ fn structurally_same_type_impl<'tcx>(
                             let b = b.subst(cx.tcx, b_substs);
                             debug!("Comparing {:?} and {:?}", a, b);
 
+                            // We can immediately rule out these types as structurally same if
+                            // their layouts differ.
+                            match compare_layouts(a, b) {
+                                Ok(false) => return false,
+                                _ => (), // otherwise, continue onto the full, fields comparison
+                            }
+
                             // Grab a flattened representation of all fields.
                             let a_fields = a_def.variants.iter().flat_map(|v| v.fields.iter());
                             let b_fields = b_def.variants.iter().flat_map(|v| v.fields.iter());
-                            compare_layouts(a, b)
-                            && a_fields.eq_by(
+
+                            // Perform a structural comparison for each field.
+                            a_fields.eq_by(
                                 b_fields,
                                 |&ty::FieldDef { did: a_did, .. },
                                  &ty::FieldDef { did: b_did, .. }| {
@@ -2287,13 +2336,13 @@ fn structurally_same_type_impl<'tcx>(
                             if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) {
                                 ty == primitive
                             } else {
-                                compare_layouts(a, b)
+                                compare_layouts(a, b).unwrap_or(false)
                             }
                         }
                         // Otherwise, just compare the layouts. This may fail to lint for some
                         // incompatible types, but at the very least, will stop reads into
                         // uninitialised memory.
-                        _ => compare_layouts(a, b),
+                        _ => compare_layouts(a, b).unwrap_or(false),
                     }
                 })
             }
index 4ca5f23ebfe6c22bfd2ca99b26862e26a91fab9e..35c462c24c8e88a0b229e182e4c20a65e313dfac 100644 (file)
@@ -11,7 +11,7 @@
 use rustc_middle::mir::interpret::{sign_extend, truncate};
 use rustc_middle::ty::layout::{IntegerExt, SizeSkeleton};
 use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, AdtKind, Ty, TypeFoldable};
+use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
 use rustc_span::source_map;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, DUMMY_SP};
@@ -527,22 +527,26 @@ enum FfiResult<'tcx> {
     FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option<String> },
 }
 
+crate fn nonnull_optimization_guaranteed<'tcx>(tcx: TyCtxt<'tcx>, def: &ty::AdtDef) -> bool {
+    tcx.get_attrs(def.did)
+        .iter()
+        .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed))
+}
+
 /// Is type known to be non-null?
-fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
+crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
     let tcx = cx.tcx;
     match ty.kind {
         ty::FnPtr(_) => true,
         ty::Ref(..) => true,
         ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
         ty::Adt(def, substs) if def.repr.transparent() && !def.is_union() => {
-            let guaranteed_nonnull_optimization = tcx
-                .get_attrs(def.did)
-                .iter()
-                .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed));
+            let marked_non_null = nonnull_optimization_guaranteed(tcx, &def);
 
-            if guaranteed_nonnull_optimization {
+            if marked_non_null {
                 return true;
             }
+
             for variant in &def.variants {
                 if let Some(field) = variant.transparent_newtype_field(tcx) {
                     if ty_is_known_nonnull(cx, field.ty(tcx, substs), mode) {
index 21b8080714c1744ca54130adbb6325ece0082bff..a45028eafe1c4cd8f806d0b7a299be21b324ba49 100644 (file)
@@ -151,6 +151,12 @@ fn main() {
             continue;
         }
 
+        // Include path contains host directory, replace it with target
+        if is_crossed && flag.starts_with("-I") {
+            cfg.flag(&flag.replace(&host, &target));
+            continue;
+        }
+
         cfg.flag(flag);
     }
 
@@ -189,6 +195,11 @@ fn main() {
 
     if !is_crossed {
         cmd.arg("--system-libs");
+    } else if target.contains("windows-gnu") {
+        println!("cargo:rustc-link-lib=shell32");
+        println!("cargo:rustc-link-lib=uuid");
+    } else if target.contains("netbsd") {
+        println!("cargo:rustc-link-lib=z");
     }
     cmd.args(&components);
 
index af6aacfd64b44e4b65e8a4c5b3f9698a47d0eb16..76e11bd689c5c4d46f9cf25af383fb23f200558d 100644 (file)
@@ -10,8 +10,8 @@ path = "lib.rs"
 doctest = false
 
 [dependencies]
-flate2 = "1.0"
 libc = "0.2"
+snap = "1"
 tracing = "0.1"
 memmap = "0.7"
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
index ce19240a009d091f52de29382790ef80f90500b3..bdb53e3f75a40866d347e3f3c279935eb90f7ae5 100644 (file)
@@ -51,51 +51,90 @@ pub unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String> {
 
 #[cfg(unix)]
 mod dl {
-    use std::ffi::{CStr, CString, OsStr};
+    use std::ffi::{CString, OsStr};
     use std::os::unix::prelude::*;
-    use std::ptr;
-    use std::str;
 
-    pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> {
-        check_for_errors_in(|| unsafe {
-            let s = CString::new(filename.as_bytes()).unwrap();
-            libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) as *mut u8
-        })
-    }
+    // As of the 2017 revision of the POSIX standard (IEEE 1003.1-2017), it is
+    // implementation-defined whether `dlerror` is thread-safe (in which case it returns the most
+    // recent error in the calling thread) or not thread-safe (in which case it returns the most
+    // recent error in *any* thread).
+    //
+    // There's no easy way to tell what strategy is used by a given POSIX implementation, so we
+    // lock around all calls that can modify `dlerror` in this module lest we accidentally read an
+    // error from a different thread. This is bulletproof when we are the *only* code using the
+    // dynamic library APIs at a given point in time. However, it's still possible for us to race
+    // with other code (see #74469) on platforms where `dlerror` is not thread-safe.
+    mod error {
+        use std::ffi::CStr;
+        use std::lazy::SyncLazy;
+        use std::sync::{Mutex, MutexGuard};
+
+        pub fn lock() -> MutexGuard<'static, Guard> {
+            static LOCK: SyncLazy<Mutex<Guard>> = SyncLazy::new(|| Mutex::new(Guard { _priv: () }));
+            LOCK.lock().unwrap()
+        }
 
-    fn check_for_errors_in<T, F>(f: F) -> Result<T, String>
-    where
-        F: FnOnce() -> T,
-    {
-        use std::sync::{Mutex, Once};
-        static INIT: Once = Once::new();
-        static mut LOCK: *mut Mutex<()> = ptr::null_mut();
-        unsafe {
-            INIT.call_once(|| {
-                LOCK = Box::into_raw(Box::new(Mutex::new(())));
-            });
-            // dlerror isn't thread safe, so we need to lock around this entire
-            // sequence
-            let _guard = (*LOCK).lock();
-            let _old_error = libc::dlerror();
-
-            let result = f();
-
-            let last_error = libc::dlerror() as *const _;
-            if ptr::null() == last_error {
-                Ok(result)
-            } else {
-                let s = CStr::from_ptr(last_error).to_bytes();
-                Err(str::from_utf8(s).unwrap().to_owned())
+        pub struct Guard {
+            _priv: (),
+        }
+
+        impl Guard {
+            pub fn get(&mut self) -> Result<(), String> {
+                let msg = unsafe { libc::dlerror() };
+                if msg.is_null() {
+                    Ok(())
+                } else {
+                    let msg = unsafe { CStr::from_ptr(msg as *const _) };
+                    Err(msg.to_string_lossy().into_owned())
+                }
+            }
+
+            pub fn clear(&mut self) {
+                let _ = unsafe { libc::dlerror() };
             }
         }
     }
 
+    pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> {
+        let s = CString::new(filename.as_bytes()).unwrap();
+
+        let mut dlerror = error::lock();
+        let ret = unsafe { libc::dlopen(s.as_ptr(), libc::RTLD_LAZY | libc::RTLD_LOCAL) };
+
+        if !ret.is_null() {
+            return Ok(ret.cast());
+        }
+
+        // A NULL return from `dlopen` indicates that an error has definitely occurred, so if
+        // nothing is in `dlerror`, we are racing with another thread that has stolen our error
+        // message. See the explanation on the `dl::error` module for more information.
+        dlerror.get().and_then(|()| Err("Unknown error".to_string()))
+    }
+
     pub(super) unsafe fn symbol(
         handle: *mut u8,
         symbol: *const libc::c_char,
     ) -> Result<*mut u8, String> {
-        check_for_errors_in(|| libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8)
+        let mut dlerror = error::lock();
+
+        // Unlike `dlopen`, it's possible for `dlsym` to return NULL without overwriting `dlerror`.
+        // Because of this, we clear `dlerror` before calling `dlsym` to avoid picking up a stale
+        // error message by accident.
+        dlerror.clear();
+
+        let ret = libc::dlsym(handle as *mut libc::c_void, symbol);
+
+        if !ret.is_null() {
+            return Ok(ret.cast());
+        }
+
+        // If `dlsym` returns NULL but there is nothing in `dlerror` it means one of two things:
+        // - We tried to load a symbol mapped to address 0. This is not technically an error but is
+        //   unlikely to occur in practice and equally unlikely to be handled correctly by calling
+        //   code. Therefore we treat it as an error anyway.
+        // - An error has occurred, but we are racing with another thread that has stolen our error
+        //   message. See the explanation on the `dl::error` module for more information.
+        dlerror.get().and_then(|()| Err("Tried to load symbol mapped to address 0".to_string()))
     }
 
     pub(super) unsafe fn close(handle: *mut u8) {
index e50fa34554d51acbed0fc7e135651bc6d912f045..85490f5f6e91ab1e1b956e4753440c7cb343a740 100644 (file)
@@ -5,6 +5,7 @@
 #![feature(drain_filter)]
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
+#![feature(once_cell)]
 #![feature(or_patterns)]
 #![feature(proc_macro_internals)]
 #![feature(min_specialization)]
index 8fa14a44f527e48fa6fef3071314e03cfad4a840..0869ec2836753881b1ad7b8180fc2c12a1fc09c5 100644 (file)
 use rustc_span::Span;
 use rustc_target::spec::{Target, TargetTriple};
 
-use flate2::read::DeflateDecoder;
+use snap::read::FrameDecoder;
 use std::io::{Read, Result as IoResult, Write};
 use std::ops::Deref;
 use std::path::{Path, PathBuf};
@@ -766,7 +766,7 @@ fn get_metadata_section(
             let compressed_bytes = &buf[header_len..];
             debug!("inflating {} bytes of compressed metadata", compressed_bytes.len());
             let mut inflated = Vec::new();
-            match DeflateDecoder::new(compressed_bytes).read_to_end(&mut inflated) {
+            match FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated) {
                 Ok(_) => rustc_erase_owner!(OwningRef::new(inflated).map_owner_box()),
                 Err(_) => {
                     return Err(format!("failed to decompress metadata: {}", filename.display()));
index ec1dcd29ef2d8180328b85073d5288af75c0253c..1b2dea8a378c33df1bfb1017ce063ba54ee441e3 100644 (file)
@@ -40,7 +40,6 @@
 #![feature(nll)]
 #![feature(option_expect_none)]
 #![feature(or_patterns)]
-#![feature(range_is_empty)]
 #![feature(min_specialization)]
 #![feature(trusted_len)]
 #![feature(stmt_expr_attributes)]
index 70c90198276c1700a91850037f0b4a851dab4b57..7194a035e89f6b15278b878ae1314d7029e91d6e 100644 (file)
@@ -53,7 +53,9 @@ pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool {
     // symbols. Other panic runtimes ensure that the relevant symbols are
     // available to link things together, but they're never exercised.
     match tcx.sess.panic_strategy() {
-        PanicStrategy::Abort => lang_item != LangItem::EhPersonalityLangItem,
+        PanicStrategy::Abort => {
+            lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo
+        }
         PanicStrategy::Unwind => true,
     }
 }
index 4c47f25105d0f4eb5d049b5b5e32ab6692e5806e..7d6ff3eb5c1ccb25c7732403b2e7b7bfffe6025a 100644 (file)
@@ -503,6 +503,11 @@ pub fn to_u64(self) -> InterpResult<'static, u64> {
         self.to_unsigned_with_bit_width(64).map(|v| u64::try_from(v).unwrap())
     }
 
+    /// Converts the scalar to produce an `u128`. Fails if the scalar is a pointer.
+    pub fn to_u128(self) -> InterpResult<'static, u128> {
+        self.to_unsigned_with_bit_width(128)
+    }
+
     pub fn to_machine_usize(self, cx: &impl HasDataLayout) -> InterpResult<'static, u64> {
         let b = self.to_bits(cx.data_layout().pointer_size)?;
         Ok(u64::try_from(b).unwrap())
@@ -535,6 +540,11 @@ pub fn to_i64(self) -> InterpResult<'static, i64> {
         self.to_signed_with_bit_width(64).map(|v| i64::try_from(v).unwrap())
     }
 
+    /// Converts the scalar to produce an `i128`. Fails if the scalar is a pointer.
+    pub fn to_i128(self) -> InterpResult<'static, i128> {
+        self.to_signed_with_bit_width(128)
+    }
+
     pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'static, i64> {
         let sz = cx.data_layout().pointer_size;
         let b = self.to_bits(sz)?;
index ad40cf221bc3269b13e36d29bd2facaf0a41f497..785a7f0c51a0bb538452f94376a3be218283cf5e 100644 (file)
@@ -1430,6 +1430,15 @@ pub enum StatementKind<'tcx> {
     Nop,
 }
 
+impl<'tcx> StatementKind<'tcx> {
+    pub fn as_assign_mut(&mut self) -> Option<&mut Box<(Place<'tcx>, Rvalue<'tcx>)>> {
+        match self {
+            StatementKind::Assign(x) => Some(x),
+            _ => None,
+        }
+    }
+}
+
 /// Describes what kind of retag is to be performed.
 #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, HashStable)]
 pub enum RetagKind {
@@ -1564,10 +1573,10 @@ pub enum ProjectionElem<V, T> {
     /// ```
     ConstantIndex {
         /// index or -index (in Python terms), depending on from_end
-        offset: u32,
+        offset: u64,
         /// The thing being indexed must be at least this long. For arrays this
         /// is always the exact length.
-        min_length: u32,
+        min_length: u64,
         /// Counting backwards from end? This is always false when indexing an
         /// array.
         from_end: bool,
@@ -1578,8 +1587,8 @@ pub enum ProjectionElem<V, T> {
     /// If `from_end` is true `slice[from..slice.len() - to]`.
     /// Otherwise `array[from..to]`.
     Subslice {
-        from: u32,
-        to: u32,
+        from: u64,
+        to: u64,
         /// Whether `to` counts from the start or end of the array/slice.
         /// For `PlaceElem`s this is `true` if and only if the base is a slice.
         /// For `ProjectionKind`, this can also be `true` for arrays.
@@ -1616,7 +1625,7 @@ fn is_indirect(&self) -> bool {
 
 // At least on 64 bit systems, `PlaceElem` should not be larger than two pointers.
 #[cfg(target_arch = "x86_64")]
-static_assert_size!(PlaceElem<'_>, 16);
+static_assert_size!(PlaceElem<'_>, 24);
 
 /// Alias for projections as they appear in `UserTypeProjection`, where we
 /// need neither the `V` parameter for `Index` nor the `T` for `Field`.
@@ -1843,6 +1852,10 @@ pub fn function_handle(
         })
     }
 
+    pub fn is_move(&self) -> bool {
+        matches!(self, Operand::Move(..))
+    }
+
     /// Convenience helper to make a literal-like constant from a given scalar value.
     /// Since this is used to synthesize MIR, assumes `user_ty` is None.
     pub fn const_from_scalar(
@@ -2330,7 +2343,7 @@ pub fn index(self) -> Self {
         self.map_projections(|pat_ty_proj| pat_ty_proj.index())
     }
 
-    pub fn subslice(self, from: u32, to: u32) -> Self {
+    pub fn subslice(self, from: u64, to: u64) -> Self {
         self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to))
     }
 
@@ -2376,7 +2389,7 @@ pub(crate) fn index(mut self) -> Self {
         self
     }
 
-    pub(crate) fn subslice(mut self, from: u32, to: u32) -> Self {
+    pub(crate) fn subslice(mut self, from: u64, to: u64) -> Self {
         self.projs.push(ProjectionElem::Subslice { from, to, from_end: true });
         self
     }
index d6836d2ee36658015043f53f56674ffdee0a73fa..e05752f08f631d299c8c150c36fe196c1e5da8f0 100644 (file)
@@ -173,6 +173,10 @@ fn describe_as_module(def_id: LocalDefId, tcx: TyCtxt<'_>) -> String {
             desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) }
         }
 
+        query projection_ty_from_predicates(key: (DefId, DefId)) -> Option<ty::ProjectionTy<'tcx>> {
+            desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) }
+        }
+
         query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLib>> {
             desc { "looking up the native libraries of a linked crate" }
         }
index 0ab07aea426c3c95d217133c567d291e0c0f8cc0..6a9bb8d6c284fc31d09a89c1e12ac8cd283cbfae 100644 (file)
@@ -2,7 +2,7 @@
 use crate::ty::{self, Ty, TyCtxt};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::{DerefMutTraitLangItem, DerefTraitLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_macros::HashStable;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
@@ -118,8 +118,8 @@ pub struct OverloadedDeref<'tcx> {
 impl<'tcx> OverloadedDeref<'tcx> {
     pub fn method_call(&self, tcx: TyCtxt<'tcx>, source: Ty<'tcx>) -> (DefId, SubstsRef<'tcx>) {
         let trait_def_id = match self.mutbl {
-            hir::Mutability::Not => tcx.require_lang_item(DerefTraitLangItem, None),
-            hir::Mutability::Mut => tcx.require_lang_item(DerefMutTraitLangItem, None),
+            hir::Mutability::Not => tcx.require_lang_item(LangItem::Deref, None),
+            hir::Mutability::Mut => tcx.require_lang_item(LangItem::DerefMut, None),
         };
         let method_def_id = tcx
             .associated_items(trait_def_id)
index d1fd65fc244b7dfe8290a6009e65bce7cde72080..18ae744cb1ee328792bdf203c039e29155d73a73 100644 (file)
@@ -40,7 +40,7 @@
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
 use rustc_hir::definitions::{DefPathHash, Definitions};
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::lang_items::{self, PanicLocationLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_macros::HashStable;
@@ -1538,7 +1538,7 @@ pub fn has_strict_asm_symbol_naming(&self) -> bool {
     pub fn caller_location_ty(&self) -> Ty<'tcx> {
         self.mk_imm_ref(
             self.lifetimes.re_static,
-            self.type_of(self.require_lang_item(PanicLocationLangItem, None))
+            self.type_of(self.require_lang_item(LangItem::PanicLocation, None))
                 .subst(*self, self.mk_substs([self.lifetimes.re_static.into()].iter())),
         )
     }
@@ -2185,12 +2185,12 @@ fn mk_generic_adt(self, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> {
 
     #[inline]
     pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let def_id = self.require_lang_item(lang_items::OwnedBoxLangItem, None);
+        let def_id = self.require_lang_item(LangItem::OwnedBox, None);
         self.mk_generic_adt(def_id, ty)
     }
 
     #[inline]
-    pub fn mk_lang_item(self, ty: Ty<'tcx>, item: lang_items::LangItem) -> Option<Ty<'tcx>> {
+    pub fn mk_lang_item(self, ty: Ty<'tcx>, item: LangItem) -> Option<Ty<'tcx>> {
         let def_id = self.lang_items().require(item).ok()?;
         Some(self.mk_generic_adt(def_id, ty))
     }
@@ -2203,7 +2203,7 @@ pub fn mk_diagnostic_item(self, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>>
 
     #[inline]
     pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let def_id = self.require_lang_item(lang_items::MaybeUninitLangItem, None);
+        let def_id = self.require_lang_item(LangItem::MaybeUninit, None);
         self.mk_generic_adt(def_id, ty)
     }
 
index e6dafd4965bc9fb321568a1af6d9be3e738a7927..8e08fe4b87b8256e2f6677030a00f8403b1b4e77 100644 (file)
@@ -5,7 +5,7 @@
 use rustc_errors::ErrorReported;
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::{CrateNum, DefId};
-use rustc_hir::lang_items::{DropInPlaceFnLangItem, FnOnceTraitLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_macros::HashStable;
 
 use std::fmt;
@@ -408,7 +408,7 @@ pub fn resolve_closure(
     }
 
     pub fn resolve_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> {
-        let def_id = tcx.require_lang_item(DropInPlaceFnLangItem, None);
+        let def_id = tcx.require_lang_item(LangItem::DropInPlace, None);
         let substs = tcx.intern_substs(&[ty.into()]);
         Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap()
     }
@@ -419,7 +419,7 @@ pub fn fn_once_adapter_instance(
         substs: ty::SubstsRef<'tcx>,
     ) -> Instance<'tcx> {
         debug!("fn_once_adapter_shim({:?}, {:?})", closure_did, substs);
-        let fn_once = tcx.require_lang_item(FnOnceTraitLangItem, None);
+        let fn_once = tcx.require_lang_item(LangItem::FnOnce, None);
         let call_once = tcx
             .associated_items(fn_once)
             .in_definition_order()
index 928cba324d5849025aceb7c9303af7ec2c0728cc..08bd131565bfa7b1d0f868331156ac95dd8d7687 100644 (file)
@@ -8,7 +8,7 @@
 use rustc_attr as attr;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_hir as hir;
-use rustc_hir::lang_items::{GeneratorStateLangItem, PinTypeLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
@@ -2371,13 +2371,13 @@ fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> {
                 let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
                 let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
 
-                let pin_did = tcx.require_lang_item(PinTypeLangItem, None);
+                let pin_did = tcx.require_lang_item(LangItem::Pin, None);
                 let pin_adt_ref = tcx.adt_def(pin_did);
                 let pin_substs = tcx.intern_substs(&[env_ty.into()]);
                 let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
 
                 sig.map_bound(|sig| {
-                    let state_did = tcx.require_lang_item(GeneratorStateLangItem, None);
+                    let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
                     let state_adt_ref = tcx.adt_def(state_did);
                     let state_substs =
                         tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
index 4fa86a91254ce698cffbe64a1a4ee8d000015d57..b6300a40b0d8d1e3e92dc0baa40442232b9dace8 100644 (file)
@@ -32,7 +32,7 @@
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX};
-use rustc_hir::lang_items::{FnMutTraitLangItem, FnOnceTraitLangItem, FnTraitLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{Constness, Node};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_macros::HashStable;
@@ -721,7 +721,13 @@ pub enum UpvarCapture<'tcx> {
     /// Upvar is captured by value. This is always true when the
     /// closure is labeled `move`, but can also be true in other cases
     /// depending on inference.
-    ByValue,
+    ///
+    /// If the upvar was inferred to be captured by value (e.g. `move`
+    /// was not used), then the `Span` points to a usage that
+    /// required it. There may be more than one such usage
+    /// (e.g. `|| { a; a; }`), in which case we pick an
+    /// arbitrary one.
+    ByValue(Option<Span>),
 
     /// Upvar is captured by reference.
     ByRef(UpvarBorrow<'tcx>),
@@ -1971,6 +1977,9 @@ pub struct VariantFlags: u32 {
         const NO_VARIANT_FLAGS        = 0;
         /// Indicates whether the field list of this variant is `#[non_exhaustive]`.
         const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0;
+        /// Indicates whether this variant was obtained as part of recovering from
+        /// a syntactic error. May be incomplete or bogus.
+        const IS_RECOVERED = 1 << 1;
     }
 }
 
@@ -1994,9 +2003,6 @@ pub struct VariantDef {
     pub ctor_kind: CtorKind,
     /// Flags of the variant (e.g. is field list non-exhaustive)?
     flags: VariantFlags,
-    /// Variant is obtained as part of recovering from a syntactic error.
-    /// May be incomplete or bogus.
-    pub recovered: bool,
 }
 
 impl<'tcx> VariantDef {
@@ -2039,6 +2045,10 @@ pub fn new(
             flags |= VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
         }
 
+        if recovered {
+            flags |= VariantFlags::IS_RECOVERED;
+        }
+
         VariantDef {
             def_id: variant_did.unwrap_or(parent_did),
             ctor_def_id,
@@ -2047,7 +2057,6 @@ pub fn new(
             fields,
             ctor_kind,
             flags,
-            recovered,
         }
     }
 
@@ -2057,6 +2066,12 @@ pub fn is_field_list_non_exhaustive(&self) -> bool {
         self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE)
     }
 
+    /// Was this variant obtained as part of recovering from a syntactic error?
+    #[inline]
+    pub fn is_recovered(&self) -> bool {
+        self.flags.intersects(VariantFlags::IS_RECOVERED)
+    }
+
     /// `repr(transparent)` structs can have a single non-ZST field, this function returns that
     /// field.
     pub fn transparent_newtype_field(&self, tcx: TyCtxt<'tcx>) -> Option<&FieldDef> {
@@ -2670,9 +2685,9 @@ impl<'tcx> ClosureKind {
 
     pub fn trait_did(&self, tcx: TyCtxt<'tcx>) -> DefId {
         match *self {
-            ClosureKind::Fn => tcx.require_lang_item(FnTraitLangItem, None),
-            ClosureKind::FnMut => tcx.require_lang_item(FnMutTraitLangItem, None),
-            ClosureKind::FnOnce => tcx.require_lang_item(FnOnceTraitLangItem, None),
+            ClosureKind::Fn => tcx.require_lang_item(LangItem::Fn, None),
+            ClosureKind::FnMut => tcx.require_lang_item(LangItem::FnMut, None),
+            ClosureKind::FnOnce => tcx.require_lang_item(LangItem::FnOnce, None),
         }
     }
 
index 7a50bdfeef625e0a25a906aa3b917241004d0471..9076dbccb525352f708a325c1d5d67bf8e1de9e9 100644 (file)
@@ -113,23 +113,32 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                 }
             }
 
-            let msg = ""; //FIXME: add "partially " or "collaterally "
+            let is_partial_move = move_site_vec.iter().any(|move_site| {
+                let move_out = self.move_data.moves[(*move_site).moi];
+                let moved_place = &self.move_data.move_paths[move_out.path].place;
+                // `*(_1)` where `_1` is a `Box` is actually a move out.
+                let is_box_move = moved_place.as_ref().projection == &[ProjectionElem::Deref]
+                    && self.body.local_decls[moved_place.local].ty.is_box();
+
+                !is_box_move
+                    && used_place != moved_place.as_ref()
+                    && used_place.is_prefix_of(moved_place.as_ref())
+            });
+
+            let partial_str = if is_partial_move { "partial " } else { "" };
+            let partially_str = if is_partial_move { "partially " } else { "" };
 
             let mut err = self.cannot_act_on_moved_value(
                 span,
                 desired_action.as_noun(),
-                msg,
+                partially_str,
                 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
             );
 
             self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
 
             let mut is_loop_move = false;
-            let is_partial_move = move_site_vec.iter().any(|move_site| {
-                let move_out = self.move_data.moves[(*move_site).moi];
-                let moved_place = &self.move_data.move_paths[move_out.path].place;
-                used_place != moved_place.as_ref() && used_place.is_prefix_of(moved_place.as_ref())
-            });
+
             for move_site in &move_site_vec {
                 let move_out = self.move_data.moves[(*move_site).moi];
                 let moved_place = &self.move_data.move_paths[move_out.path].place;
@@ -142,13 +151,19 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                 if location == move_out.source {
                     err.span_label(
                         span,
-                        format!("value moved{} here, in previous iteration of loop", move_msg),
+                        format!(
+                            "value {}moved{} here, in previous iteration of loop",
+                            partially_str, move_msg
+                        ),
                     );
                     is_loop_move = true;
                 } else if move_site.traversed_back_edge {
                     err.span_label(
                         move_span,
-                        format!("value moved{} here, in previous iteration of loop", move_msg),
+                        format!(
+                            "value {}moved{} here, in previous iteration of loop",
+                            partially_str, move_msg
+                        ),
                     );
                 } else {
                     if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } =
@@ -162,7 +177,10 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                             FnSelfUseKind::FnOnceCall => {
                                 err.span_label(
                                     fn_call_span,
-                                    &format!("{} moved due to this call", place_name),
+                                    &format!(
+                                        "{} {}moved due to this call",
+                                        place_name, partially_str
+                                    ),
                                 );
                                 err.span_note(
                                     var_span,
@@ -172,7 +190,10 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                             FnSelfUseKind::Operator { self_arg } => {
                                 err.span_label(
                                     fn_call_span,
-                                    &format!("{} moved due to usage in operator", place_name),
+                                    &format!(
+                                        "{} {}moved due to usage in operator",
+                                        place_name, partially_str
+                                    ),
                                 );
                                 if self.fn_self_span_reported.insert(fn_span) {
                                     err.span_note(
@@ -186,14 +207,17 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                                     err.span_label(
                                         fn_call_span,
                                         &format!(
-                                            "{} moved due to this implicit call to `.into_iter()`",
-                                            place_name
+                                            "{} {}moved due to this implicit call to `.into_iter()`",
+                                            place_name, partially_str
                                         ),
                                     );
                                 } else {
                                     err.span_label(
                                         fn_call_span,
-                                        &format!("{} moved due to this method call", place_name),
+                                        &format!(
+                                            "{} {}moved due to this method call",
+                                            place_name, partially_str
+                                        ),
                                     );
                                 }
                                 // Avoid pointing to the same function in multiple different
@@ -207,10 +231,17 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                             }
                         }
                     } else {
-                        err.span_label(move_span, format!("value moved{} here", move_msg));
+                        err.span_label(
+                            move_span,
+                            format!("value {}moved{} here", partially_str, move_msg),
+                        );
                         move_spans.var_span_label(
                             &mut err,
-                            format!("variable moved due to use{}", move_spans.describe()),
+                            format!(
+                                "variable {}moved due to use{}",
+                                partially_str,
+                                move_spans.describe()
+                            ),
                         );
                     }
                 }
@@ -250,9 +281,9 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                 err.span_label(
                     span,
                     format!(
-                        "value {} here {}",
+                        "value {} here after {}move",
                         desired_action.as_verb_in_past_tense(),
-                        if is_partial_move { "after partial move" } else { "after move" },
+                        partial_str
                     ),
                 );
             }
@@ -321,7 +352,7 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                 } else {
                     None
                 };
-                self.note_type_does_not_implement_copy(&mut err, &note_msg, ty, span);
+                self.note_type_does_not_implement_copy(&mut err, &note_msg, ty, span, partial_str);
             }
 
             if let Some((_, mut old_err)) =
@@ -1398,8 +1429,12 @@ fn predecessor_locations(
 
                 for moi in &self.move_data.loc_map[location] {
                     debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
-                    if mpis.contains(&self.move_data.moves[*moi].path) {
-                        debug!("report_use_of_moved_or_uninitialized: found");
+                    let path = self.move_data.moves[*moi].path;
+                    if mpis.contains(&path) {
+                        debug!(
+                            "report_use_of_moved_or_uninitialized: found {:?}",
+                            move_paths[path].place
+                        );
                         result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
 
                         // Strictly speaking, we could continue our DFS here. There may be
index daffdec2a83bed72239b89fcfc78aa523b553688..dfaa75d9f23f80f14c2c56700b7b0b3289c084cd 100644 (file)
@@ -412,10 +412,11 @@ pub(super) fn note_type_does_not_implement_copy(
         place_desc: &str,
         ty: Ty<'tcx>,
         span: Option<Span>,
+        move_prefix: &str,
     ) {
         let message = format!(
-            "move occurs because {} has type `{}`, which does not implement the `Copy` trait",
-            place_desc, ty,
+            "{}move occurs because {} has type `{}`, which does not implement the `Copy` trait",
+            move_prefix, place_desc, ty,
         );
         if let Some(span) = span {
             err.span_label(span, message);
@@ -937,11 +938,14 @@ fn closure_span(
             "closure_span: def_id={:?} target_place={:?} places={:?}",
             def_id, target_place, places
         );
-        let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(def_id.as_local()?);
+        let local_did = def_id.as_local()?;
+        let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did);
         let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
         if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
-            for (upvar, place) in self.infcx.tcx.upvars_mentioned(def_id)?.values().zip(places) {
+            for ((upvar_hir_id, upvar), place) in
+                self.infcx.tcx.upvars_mentioned(def_id)?.iter().zip(places)
+            {
                 match place {
                     Operand::Copy(place) | Operand::Move(place)
                         if target_place == place.as_ref() =>
@@ -949,7 +953,23 @@ fn closure_span(
                         debug!("closure_span: found captured local {:?}", place);
                         let body = self.infcx.tcx.hir().body(*body_id);
                         let generator_kind = body.generator_kind();
-                        return Some((*args_span, generator_kind, upvar.span));
+                        let upvar_id = ty::UpvarId {
+                            var_path: ty::UpvarPath { hir_id: *upvar_hir_id },
+                            closure_expr_id: local_did,
+                        };
+
+                        // If we have a more specific span available, point to that.
+                        // We do this even though this span might be part of a borrow error
+                        // message rather than a move error message. Our goal is to point
+                        // to a span that shows why the upvar is used in the closure,
+                        // so a move-related span is as good as any (and potentially better,
+                        // if the overall error is due to a move of the upvar).
+                        let usage_span =
+                            match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) {
+                                ty::UpvarCapture::ByValue(Some(span)) => span,
+                                _ => upvar.span,
+                            };
+                        return Some((*args_span, generator_kind, usage_span));
                     }
                     _ => {}
                 }
index bd3e20458b078bb72f8237a908656f02d9f5bf11..1c8da212f108853740a30545914f865f6d07afbb 100644 (file)
@@ -445,7 +445,13 @@ fn add_move_hints(
                         None => "value".to_string(),
                     };
 
-                    self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span));
+                    self.note_type_does_not_implement_copy(
+                        err,
+                        &place_desc,
+                        place_ty,
+                        Some(span),
+                        "",
+                    );
                 } else {
                     binds_to.sort();
                     binds_to.dedup();
@@ -467,7 +473,7 @@ fn add_move_hints(
                     Some(desc) => format!("`{}`", desc),
                     None => "value".to_string(),
                 };
-                self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span));
+                self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span), "");
 
                 use_spans.args_span_label(err, format!("move out of {} occurs here", place_desc));
                 use_spans
@@ -529,6 +535,7 @@ fn add_move_error_details(&self, err: &mut DiagnosticBuilder<'a>, binds_to: &[Lo
                     &format!("`{}`", self.local_names[*local].unwrap()),
                     bind_to.ty,
                     Some(binding_span),
+                    "",
                 );
             }
         }
index a61af5c3f0523605f2a27e37abe7091aeeb822c5..86908eaabd1f4e1963d69f14111df62644bf5d2e 100644 (file)
@@ -163,7 +163,7 @@ fn do_mir_borrowck<'a, 'tcx>(
             let var_hir_id = upvar_id.var_path.hir_id;
             let capture = tables.upvar_capture(*upvar_id);
             let by_ref = match capture {
-                ty::UpvarCapture::ByValue => false,
+                ty::UpvarCapture::ByValue(_) => false,
                 ty::UpvarCapture::ByRef(..) => true,
             };
             let mut upvar = Upvar {
@@ -1694,8 +1694,8 @@ fn check_if_subslice_element_is_moved(
         desired_action: InitializationRequiringAction,
         place_span: (PlaceRef<'tcx>, Span),
         maybe_uninits: &BitSet<MovePathIndex>,
-        from: u32,
-        to: u32,
+        from: u64,
+        to: u64,
     ) {
         if let Some(mpi) = self.move_path_for_place(place_span.0) {
             let move_paths = &self.move_data.move_paths;
index 168a352591d0e12b400ff93bede87ef594513303..69c4f633770f2bc8869e786076fbb4aa72f54d0a 100644 (file)
@@ -10,7 +10,7 @@
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::lang_items::{CoerceUnsizedTraitLangItem, CopyTraitLangItem, SizedTraitLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_infer::infer::canonical::QueryRegionConstraints;
 use rustc_infer::infer::outlives::env::RegionBoundPairs;
@@ -507,7 +507,7 @@ fn sanitize_place(
         if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
             let tcx = self.tcx();
             let trait_ref = ty::TraitRef {
-                def_id: tcx.require_lang_item(CopyTraitLangItem, Some(self.last_span)),
+                def_id: tcx.require_lang_item(LangItem::Copy, Some(self.last_span)),
                 substs: tcx.mk_substs_trait(place_ty.ty, &[]),
             };
 
@@ -649,7 +649,7 @@ fn sanitize_projection(
                 PlaceTy::from_ty(match base_ty.kind {
                     ty::Array(inner, _) => {
                         assert!(!from_end, "array subslices should not use from_end");
-                        tcx.mk_array(inner, (to - from) as u64)
+                        tcx.mk_array(inner, to - from)
                     }
                     ty::Slice(..) => {
                         assert!(from_end, "slice subslices should use from_end");
@@ -1474,7 +1474,7 @@ fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Lo
                 self.check_rvalue(body, rv, location);
                 if !self.tcx().features().unsized_locals {
                     let trait_ref = ty::TraitRef {
-                        def_id: tcx.require_lang_item(SizedTraitLangItem, Some(self.last_span)),
+                        def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
                         substs: tcx.mk_substs_trait(place_ty, &[]),
                     };
                     self.prove_trait_ref(
@@ -2025,7 +2025,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
                                     self.param_env,
                                     ty::Binder::bind(ty::TraitRef::new(
                                         self.tcx().require_lang_item(
-                                            CopyTraitLangItem,
+                                            LangItem::Copy,
                                             Some(self.last_span),
                                         ),
                                         tcx.mk_substs_trait(ty, &[]),
@@ -2050,7 +2050,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
                 }
 
                 let trait_ref = ty::TraitRef {
-                    def_id: tcx.require_lang_item(SizedTraitLangItem, Some(self.last_span)),
+                    def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
                     substs: tcx.mk_substs_trait(ty, &[]),
                 };
 
@@ -2148,10 +2148,8 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
                     CastKind::Pointer(PointerCast::Unsize) => {
                         let &ty = ty;
                         let trait_ref = ty::TraitRef {
-                            def_id: tcx.require_lang_item(
-                                CoerceUnsizedTraitLangItem,
-                                Some(self.last_span),
-                            ),
+                            def_id: tcx
+                                .require_lang_item(LangItem::CoerceUnsized, Some(self.last_span)),
                             substs: tcx.mk_substs_trait(op.ty(body, tcx), &[ty.into()]),
                         };
 
index cd6b75cf55602cb7a3150720486190c5e269dd47..9dfc67bcf67aad353376b31fa0baadbd37be3279 100644 (file)
@@ -17,7 +17,7 @@
 use rustc_errors::DiagnosticBuilder;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{BodyOwnerKind, HirId};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin};
@@ -456,7 +456,7 @@ fn build(self) -> UniversalRegions<'tcx> {
         if let DefiningTy::FnDef(def_id, _) = defining_ty {
             if self.infcx.tcx.fn_sig(def_id).c_variadic() {
                 let va_list_did = self.infcx.tcx.require_lang_item(
-                    lang_items::VaListTypeLangItem,
+                    LangItem::VaList,
                     Some(self.infcx.tcx.def_span(self.mir_def.did)),
                 );
                 let region = self
index e567063e0d5997db17e1a194c97972e73fae3799..e088dc6a954a3b451975e2a9aa9028bb885632ab 100644 (file)
@@ -480,7 +480,7 @@ fn gather_move(&mut self, place: Place<'tcx>) {
                 }
             };
             let base_ty = base_place.ty(self.builder.body, self.builder.tcx).ty;
-            let len: u32 = match base_ty.kind {
+            let len: u64 = match base_ty.kind {
                 ty::Array(_, size) => {
                     let length = size.eval_usize(self.builder.tcx, self.builder.param_env);
                     length
index fb3a670714b58ad0298612dc5261383d64f27b79..d9be28cf9dbb60c44c5b13234601bcbd050d0198 100644 (file)
@@ -1,6 +1,6 @@
 use std::convert::TryFrom;
 
-use rustc_hir::lang_items::PanicLocationLangItem;
+use rustc_hir::lang_items::LangItem;
 use rustc_middle::mir::TerminatorKind;
 use rustc_middle::ty::subst::Subst;
 use rustc_span::{Span, Symbol};
@@ -63,7 +63,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // Allocate memory for `CallerLocation` struct.
         let loc_ty = self
             .tcx
-            .type_of(self.tcx.require_lang_item(PanicLocationLangItem, None))
+            .type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None))
             .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_erased.into()].iter()));
         let loc_layout = self.layout_of(loc_ty).unwrap();
         let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
index 20fd8e43361d6561d9c3cb7c74e52c0d5ee8016b..6ba6103b311a3e3b90d7cf8e694846df382cb634 100644 (file)
@@ -549,17 +549,17 @@ pub(super) fn mplace_projection(
 
             ConstantIndex { offset, min_length, from_end } => {
                 let n = base.len(self)?;
-                if n < u64::from(min_length) {
+                if n < min_length {
                     // This can only be reached in ConstProp and non-rustc-MIR.
                     throw_ub!(BoundsCheckFailed { len: min_length.into(), index: n });
                 }
 
                 let index = if from_end {
                     assert!(0 < offset && offset <= min_length);
-                    n.checked_sub(u64::from(offset)).unwrap()
+                    n.checked_sub(offset).unwrap()
                 } else {
                     assert!(offset < min_length);
-                    u64::from(offset)
+                    offset
                 };
 
                 self.mplace_index(base, index)?
index 3118e7ac3ab17f57ec3e2f7076bd1b03adba385d..2e3b508463540ca5472f407c14abe41394705572 100644 (file)
@@ -22,7 +22,6 @@
 #![feature(try_blocks)]
 #![feature(associated_type_bounds)]
 #![feature(associated_type_defaults)]
-#![feature(range_is_empty)]
 #![feature(stmt_expr_attributes)]
 #![feature(trait_alias)]
 #![feature(option_expect_none)]
index e724180f4d833d588c199cc4bdf46bab1c681410..d379f4ef428a6ce44564c4e6c0596a3e4c17a3e0 100644 (file)
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_hir::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::interpret::{AllocId, ConstValue};
@@ -594,7 +594,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
             mir::Rvalue::NullaryOp(mir::NullOp::Box, _) => {
                 let tcx = self.tcx;
                 let exchange_malloc_fn_def_id =
-                    tcx.require_lang_item(ExchangeMallocFnLangItem, None);
+                    tcx.require_lang_item(LangItem::ExchangeMalloc, None);
                 let instance = Instance::mono(tcx, exchange_malloc_fn_def_id);
                 if should_codegen_locally(tcx, &instance) {
                     self.output.push(create_fn_mono_item(self.tcx, instance, span));
@@ -1083,7 +1083,7 @@ fn push_extra_entry_roots(&mut self) {
             _ => return,
         };
 
-        let start_def_id = match self.tcx.lang_items().require(StartFnLangItem) {
+        let start_def_id = match self.tcx.lang_items().require(LangItem::Start) {
             Ok(s) => s,
             Err(err) => self.tcx.sess.fatal(&err),
         };
index 15d7b11124071a59b9e0f01eb2abf1791f8ba50f..edafa00a03ad0fd597ba0673d59a6481be745b8d 100644 (file)
@@ -2,7 +2,7 @@
 use rustc_middle::ty::adjustment::CustomCoerceUnsized;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 
-use rustc_hir::lang_items::CoerceUnsizedTraitLangItem;
+use rustc_hir::lang_items::LangItem;
 
 pub mod collector;
 pub mod partitioning;
@@ -13,7 +13,7 @@ pub fn custom_coerce_unsize_info<'tcx>(
     source_ty: Ty<'tcx>,
     target_ty: Ty<'tcx>,
 ) -> CustomCoerceUnsized {
-    let def_id = tcx.require_lang_item(CoerceUnsizedTraitLangItem, None);
+    let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None);
 
     let trait_ref = ty::Binder::bind(ty::TraitRef {
         def_id,
diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs
deleted file mode 100644 (file)
index 6162651..0000000
+++ /dev/null
@@ -1,1012 +0,0 @@
-//! Partitioning Codegen Units for Incremental Compilation
-//! ======================================================
-//!
-//! The task of this module is to take the complete set of monomorphizations of
-//! a crate and produce a set of codegen units from it, where a codegen unit
-//! is a named set of (mono-item, linkage) pairs. That is, this module
-//! decides which monomorphization appears in which codegen units with which
-//! linkage. The following paragraphs describe some of the background on the
-//! partitioning scheme.
-//!
-//! The most important opportunity for saving on compilation time with
-//! incremental compilation is to avoid re-codegenning and re-optimizing code.
-//! Since the unit of codegen and optimization for LLVM is "modules" or, how
-//! we call them "codegen units", the particulars of how much time can be saved
-//! by incremental compilation are tightly linked to how the output program is
-//! partitioned into these codegen units prior to passing it to LLVM --
-//! especially because we have to treat codegen units as opaque entities once
-//! they are created: There is no way for us to incrementally update an existing
-//! LLVM module and so we have to build any such module from scratch if it was
-//! affected by some change in the source code.
-//!
-//! From that point of view it would make sense to maximize the number of
-//! codegen units by, for example, putting each function into its own module.
-//! That way only those modules would have to be re-compiled that were actually
-//! affected by some change, minimizing the number of functions that could have
-//! been re-used but just happened to be located in a module that is
-//! re-compiled.
-//!
-//! However, since LLVM optimization does not work across module boundaries,
-//! using such a highly granular partitioning would lead to very slow runtime
-//! code since it would effectively prohibit inlining and other inter-procedure
-//! optimizations. We want to avoid that as much as possible.
-//!
-//! Thus we end up with a trade-off: The bigger the codegen units, the better
-//! LLVM's optimizer can do its work, but also the smaller the compilation time
-//! reduction we get from incremental compilation.
-//!
-//! Ideally, we would create a partitioning such that there are few big codegen
-//! units with few interdependencies between them. For now though, we use the
-//! following heuristic to determine the partitioning:
-//!
-//! - There are two codegen units for every source-level module:
-//! - One for "stable", that is non-generic, code
-//! - One for more "volatile" code, i.e., monomorphized instances of functions
-//!   defined in that module
-//!
-//! In order to see why this heuristic makes sense, let's take a look at when a
-//! codegen unit can get invalidated:
-//!
-//! 1. The most straightforward case is when the BODY of a function or global
-//! changes. Then any codegen unit containing the code for that item has to be
-//! re-compiled. Note that this includes all codegen units where the function
-//! has been inlined.
-//!
-//! 2. The next case is when the SIGNATURE of a function or global changes. In
-//! this case, all codegen units containing a REFERENCE to that item have to be
-//! re-compiled. This is a superset of case 1.
-//!
-//! 3. The final and most subtle case is when a REFERENCE to a generic function
-//! is added or removed somewhere. Even though the definition of the function
-//! might be unchanged, a new REFERENCE might introduce a new monomorphized
-//! instance of this function which has to be placed and compiled somewhere.
-//! Conversely, when removing a REFERENCE, it might have been the last one with
-//! that particular set of generic arguments and thus we have to remove it.
-//!
-//! From the above we see that just using one codegen unit per source-level
-//! module is not such a good idea, since just adding a REFERENCE to some
-//! generic item somewhere else would invalidate everything within the module
-//! containing the generic item. The heuristic above reduces this detrimental
-//! side-effect of references a little by at least not touching the non-generic
-//! code of the module.
-//!
-//! A Note on Inlining
-//! ------------------
-//! As briefly mentioned above, in order for LLVM to be able to inline a
-//! function call, the body of the function has to be available in the LLVM
-//! module where the call is made. This has a few consequences for partitioning:
-//!
-//! - The partitioning algorithm has to take care of placing functions into all
-//!   codegen units where they should be available for inlining. It also has to
-//!   decide on the correct linkage for these functions.
-//!
-//! - The partitioning algorithm has to know which functions are likely to get
-//!   inlined, so it can distribute function instantiations accordingly. Since
-//!   there is no way of knowing for sure which functions LLVM will decide to
-//!   inline in the end, we apply a heuristic here: Only functions marked with
-//!   `#[inline]` are considered for inlining by the partitioner. The current
-//!   implementation will not try to determine if a function is likely to be
-//!   inlined by looking at the functions definition.
-//!
-//! Note though that as a side-effect of creating a codegen units per
-//! source-level module, functions from the same module will be available for
-//! inlining, even when they are not marked `#[inline]`.
-
-use std::cmp;
-use std::collections::hash_map::Entry;
-
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::sync;
-use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE};
-use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_middle::middle::exported_symbols::SymbolExportLevel;
-use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility};
-use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
-use rustc_middle::ty::print::characteristic_def_id_of_type;
-use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, DefIdTree, InstanceDef, TyCtxt};
-use rustc_span::symbol::{Symbol, SymbolStr};
-
-use crate::monomorphize::collector::InliningMap;
-use crate::monomorphize::collector::{self, MonoItemCollectionMode};
-
-// Anything we can't find a proper codegen unit for goes into this.
-fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol {
-    name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu"))
-}
-
-pub fn partition<'tcx, I>(
-    tcx: TyCtxt<'tcx>,
-    mono_items: I,
-    max_cgu_count: usize,
-    inlining_map: &InliningMap<'tcx>,
-) -> Vec<CodegenUnit<'tcx>>
-where
-    I: Iterator<Item = MonoItem<'tcx>>,
-{
-    let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
-
-    // In the first step, we place all regular monomorphizations into their
-    // respective 'home' codegen unit. Regular monomorphizations are all
-    // functions and statics defined in the local crate.
-    let mut initial_partitioning = {
-        let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots");
-        place_root_mono_items(tcx, mono_items)
-    };
-
-    initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
-
-    debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter());
-
-    // Merge until we have at most `max_cgu_count` codegen units.
-    {
-        let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
-        merge_codegen_units(tcx, &mut initial_partitioning, max_cgu_count);
-        debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter());
-    }
-
-    // In the next step, we use the inlining map to determine which additional
-    // monomorphizations have to go into each codegen unit. These additional
-    // monomorphizations can be drop-glue, functions from external crates, and
-    // local functions the definition of which is marked with `#[inline]`.
-    let mut post_inlining = {
-        let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items");
-        place_inlined_mono_items(initial_partitioning, inlining_map)
-    };
-
-    post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
-
-    debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter());
-
-    // Next we try to make as many symbols "internal" as possible, so LLVM has
-    // more freedom to optimize.
-    if tcx.sess.opts.cg.link_dead_code != Some(true) {
-        let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
-        internalize_symbols(tcx, &mut post_inlining, inlining_map);
-    }
-
-    // Finally, sort by codegen unit name, so that we get deterministic results.
-    let PostInliningPartitioning {
-        codegen_units: mut result,
-        mono_item_placements: _,
-        internalization_candidates: _,
-    } = post_inlining;
-
-    result.sort_by_cached_key(|cgu| cgu.name().as_str());
-
-    result
-}
-
-struct PreInliningPartitioning<'tcx> {
-    codegen_units: Vec<CodegenUnit<'tcx>>,
-    roots: FxHashSet<MonoItem<'tcx>>,
-    internalization_candidates: FxHashSet<MonoItem<'tcx>>,
-}
-
-/// For symbol internalization, we need to know whether a symbol/mono-item is
-/// accessed from outside the codegen unit it is defined in. This type is used
-/// to keep track of that.
-#[derive(Clone, PartialEq, Eq, Debug)]
-enum MonoItemPlacement {
-    SingleCgu { cgu_name: Symbol },
-    MultipleCgus,
-}
-
-struct PostInliningPartitioning<'tcx> {
-    codegen_units: Vec<CodegenUnit<'tcx>>,
-    mono_item_placements: FxHashMap<MonoItem<'tcx>, MonoItemPlacement>,
-    internalization_candidates: FxHashSet<MonoItem<'tcx>>,
-}
-
-fn place_root_mono_items<'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I) -> PreInliningPartitioning<'tcx>
-where
-    I: Iterator<Item = MonoItem<'tcx>>,
-{
-    let mut roots = FxHashSet::default();
-    let mut codegen_units = FxHashMap::default();
-    let is_incremental_build = tcx.sess.opts.incremental.is_some();
-    let mut internalization_candidates = FxHashSet::default();
-
-    // Determine if monomorphizations instantiated in this crate will be made
-    // available to downstream crates. This depends on whether we are in
-    // share-generics mode and whether the current crate can even have
-    // downstream crates.
-    let export_generics = tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics();
-
-    let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
-    let cgu_name_cache = &mut FxHashMap::default();
-
-    for mono_item in mono_items {
-        match mono_item.instantiation_mode(tcx) {
-            InstantiationMode::GloballyShared { .. } => {}
-            InstantiationMode::LocalCopy => continue,
-        }
-
-        let characteristic_def_id = characteristic_def_id_of_mono_item(tcx, mono_item);
-        let is_volatile = is_incremental_build && mono_item.is_generic_fn();
-
-        let codegen_unit_name = match characteristic_def_id {
-            Some(def_id) => compute_codegen_unit_name(
-                tcx,
-                cgu_name_builder,
-                def_id,
-                is_volatile,
-                cgu_name_cache,
-            ),
-            None => fallback_cgu_name(cgu_name_builder),
-        };
-
-        let codegen_unit = codegen_units
-            .entry(codegen_unit_name)
-            .or_insert_with(|| CodegenUnit::new(codegen_unit_name));
-
-        let mut can_be_internalized = true;
-        let (linkage, visibility) = mono_item_linkage_and_visibility(
-            tcx,
-            &mono_item,
-            &mut can_be_internalized,
-            export_generics,
-        );
-        if visibility == Visibility::Hidden && can_be_internalized {
-            internalization_candidates.insert(mono_item);
-        }
-
-        codegen_unit.items_mut().insert(mono_item, (linkage, visibility));
-        roots.insert(mono_item);
-    }
-
-    // Always ensure we have at least one CGU; otherwise, if we have a
-    // crate with just types (for example), we could wind up with no CGU.
-    if codegen_units.is_empty() {
-        let codegen_unit_name = fallback_cgu_name(cgu_name_builder);
-        codegen_units.insert(codegen_unit_name, CodegenUnit::new(codegen_unit_name));
-    }
-
-    PreInliningPartitioning {
-        codegen_units: codegen_units.into_iter().map(|(_, codegen_unit)| codegen_unit).collect(),
-        roots,
-        internalization_candidates,
-    }
-}
-
-fn mono_item_linkage_and_visibility(
-    tcx: TyCtxt<'tcx>,
-    mono_item: &MonoItem<'tcx>,
-    can_be_internalized: &mut bool,
-    export_generics: bool,
-) -> (Linkage, Visibility) {
-    if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
-        return (explicit_linkage, Visibility::Default);
-    }
-    let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics);
-    (Linkage::External, vis)
-}
-
-fn mono_item_visibility(
-    tcx: TyCtxt<'tcx>,
-    mono_item: &MonoItem<'tcx>,
-    can_be_internalized: &mut bool,
-    export_generics: bool,
-) -> Visibility {
-    let instance = match mono_item {
-        // This is pretty complicated; see below.
-        MonoItem::Fn(instance) => instance,
-
-        // Misc handling for generics and such, but otherwise:
-        MonoItem::Static(def_id) => {
-            return if tcx.is_reachable_non_generic(*def_id) {
-                *can_be_internalized = false;
-                default_visibility(tcx, *def_id, false)
-            } else {
-                Visibility::Hidden
-            };
-        }
-        MonoItem::GlobalAsm(hir_id) => {
-            let def_id = tcx.hir().local_def_id(*hir_id);
-            return if tcx.is_reachable_non_generic(def_id) {
-                *can_be_internalized = false;
-                default_visibility(tcx, def_id.to_def_id(), false)
-            } else {
-                Visibility::Hidden
-            };
-        }
-    };
-
-    let def_id = match instance.def {
-        InstanceDef::Item(def) => def.did,
-        InstanceDef::DropGlue(def_id, Some(_)) => def_id,
-
-        // These are all compiler glue and such, never exported, always hidden.
-        InstanceDef::VtableShim(..)
-        | InstanceDef::ReifyShim(..)
-        | InstanceDef::FnPtrShim(..)
-        | InstanceDef::Virtual(..)
-        | InstanceDef::Intrinsic(..)
-        | InstanceDef::ClosureOnceShim { .. }
-        | InstanceDef::DropGlue(..)
-        | InstanceDef::CloneShim(..) => return Visibility::Hidden,
-    };
-
-    // The `start_fn` lang item is actually a monomorphized instance of a
-    // function in the standard library, used for the `main` function. We don't
-    // want to export it so we tag it with `Hidden` visibility but this symbol
-    // is only referenced from the actual `main` symbol which we unfortunately
-    // don't know anything about during partitioning/collection. As a result we
-    // forcibly keep this symbol out of the `internalization_candidates` set.
-    //
-    // FIXME: eventually we don't want to always force this symbol to have
-    //        hidden visibility, it should indeed be a candidate for
-    //        internalization, but we have to understand that it's referenced
-    //        from the `main` symbol we'll generate later.
-    //
-    //        This may be fixable with a new `InstanceDef` perhaps? Unsure!
-    if tcx.lang_items().start_fn() == Some(def_id) {
-        *can_be_internalized = false;
-        return Visibility::Hidden;
-    }
-
-    let is_generic = instance.substs.non_erasable_generics().next().is_some();
-
-    // Upstream `DefId` instances get different handling than local ones.
-    if !def_id.is_local() {
-        return if export_generics && is_generic {
-            // If it is a upstream monomorphization and we export generics, we must make
-            // it available to downstream crates.
-            *can_be_internalized = false;
-            default_visibility(tcx, def_id, true)
-        } else {
-            Visibility::Hidden
-        };
-    }
-
-    if is_generic {
-        if export_generics {
-            if tcx.is_unreachable_local_definition(def_id) {
-                // This instance cannot be used from another crate.
-                Visibility::Hidden
-            } else {
-                // This instance might be useful in a downstream crate.
-                *can_be_internalized = false;
-                default_visibility(tcx, def_id, true)
-            }
-        } else {
-            // We are not exporting generics or the definition is not reachable
-            // for downstream crates, we can internalize its instantiations.
-            Visibility::Hidden
-        }
-    } else {
-        // If this isn't a generic function then we mark this a `Default` if
-        // this is a reachable item, meaning that it's a symbol other crates may
-        // access when they link to us.
-        if tcx.is_reachable_non_generic(def_id) {
-            *can_be_internalized = false;
-            debug_assert!(!is_generic);
-            return default_visibility(tcx, def_id, false);
-        }
-
-        // If this isn't reachable then we're gonna tag this with `Hidden`
-        // visibility. In some situations though we'll want to prevent this
-        // symbol from being internalized.
-        //
-        // There's two categories of items here:
-        //
-        // * First is weak lang items. These are basically mechanisms for
-        //   libcore to forward-reference symbols defined later in crates like
-        //   the standard library or `#[panic_handler]` definitions. The
-        //   definition of these weak lang items needs to be referenceable by
-        //   libcore, so we're no longer a candidate for internalization.
-        //   Removal of these functions can't be done by LLVM but rather must be
-        //   done by the linker as it's a non-local decision.
-        //
-        // * Second is "std internal symbols". Currently this is primarily used
-        //   for allocator symbols. Allocators are a little weird in their
-        //   implementation, but the idea is that the compiler, at the last
-        //   minute, defines an allocator with an injected object file. The
-        //   `alloc` crate references these symbols (`__rust_alloc`) and the
-        //   definition doesn't get hooked up until a linked crate artifact is
-        //   generated.
-        //
-        //   The symbols synthesized by the compiler (`__rust_alloc`) are thin
-        //   veneers around the actual implementation, some other symbol which
-        //   implements the same ABI. These symbols (things like `__rg_alloc`,
-        //   `__rdl_alloc`, `__rde_alloc`, etc), are all tagged with "std
-        //   internal symbols".
-        //
-        //   The std-internal symbols here **should not show up in a dll as an
-        //   exported interface**, so they return `false` from
-        //   `is_reachable_non_generic` above and we'll give them `Hidden`
-        //   visibility below. Like the weak lang items, though, we can't let
-        //   LLVM internalize them as this decision is left up to the linker to
-        //   omit them, so prevent them from being internalized.
-        let attrs = tcx.codegen_fn_attrs(def_id);
-        if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
-            *can_be_internalized = false;
-        }
-
-        Visibility::Hidden
-    }
-}
-
-fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility {
-    if !tcx.sess.target.target.options.default_hidden_visibility {
-        return Visibility::Default;
-    }
-
-    // Generic functions never have export-level C.
-    if is_generic {
-        return Visibility::Hidden;
-    }
-
-    // Things with export level C don't get instantiated in
-    // downstream crates.
-    if !id.is_local() {
-        return Visibility::Hidden;
-    }
-
-    // C-export level items remain at `Default`, all other internal
-    // items become `Hidden`.
-    match tcx.reachable_non_generics(id.krate).get(&id) {
-        Some(SymbolExportLevel::C) => Visibility::Default,
-        _ => Visibility::Hidden,
-    }
-}
-
-fn merge_codegen_units<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    initial_partitioning: &mut PreInliningPartitioning<'tcx>,
-    target_cgu_count: usize,
-) {
-    assert!(target_cgu_count >= 1);
-    let codegen_units = &mut initial_partitioning.codegen_units;
-
-    // Note that at this point in time the `codegen_units` here may not be in a
-    // deterministic order (but we know they're deterministically the same set).
-    // We want this merging to produce a deterministic ordering of codegen units
-    // from the input.
-    //
-    // Due to basically how we've implemented the merging below (merge the two
-    // smallest into each other) we're sure to start off with a deterministic
-    // order (sorted by name). This'll mean that if two cgus have the same size
-    // the stable sort below will keep everything nice and deterministic.
-    codegen_units.sort_by_cached_key(|cgu| cgu.name().as_str());
-
-    // This map keeps track of what got merged into what.
-    let mut cgu_contents: FxHashMap<Symbol, Vec<SymbolStr>> =
-        codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name().as_str()])).collect();
-
-    // Merge the two smallest codegen units until the target size is reached.
-    while codegen_units.len() > target_cgu_count {
-        // Sort small cgus to the back
-        codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate()));
-        let mut smallest = codegen_units.pop().unwrap();
-        let second_smallest = codegen_units.last_mut().unwrap();
-
-        // Move the mono-items from `smallest` to `second_smallest`
-        second_smallest.modify_size_estimate(smallest.size_estimate());
-        for (k, v) in smallest.items_mut().drain() {
-            second_smallest.items_mut().insert(k, v);
-        }
-
-        // Record that `second_smallest` now contains all the stuff that was in
-        // `smallest` before.
-        let mut consumed_cgu_names = cgu_contents.remove(&smallest.name()).unwrap();
-        cgu_contents.get_mut(&second_smallest.name()).unwrap().extend(consumed_cgu_names.drain(..));
-
-        debug!(
-            "CodegenUnit {} merged into CodegenUnit {}",
-            smallest.name(),
-            second_smallest.name()
-        );
-    }
-
-    let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
-
-    if tcx.sess.opts.incremental.is_some() {
-        // If we are doing incremental compilation, we want CGU names to
-        // reflect the path of the source level module they correspond to.
-        // For CGUs that contain the code of multiple modules because of the
-        // merging done above, we use a concatenation of the names of
-        // all contained CGUs.
-        let new_cgu_names: FxHashMap<Symbol, String> = cgu_contents
-            .into_iter()
-            // This `filter` makes sure we only update the name of CGUs that
-            // were actually modified by merging.
-            .filter(|(_, cgu_contents)| cgu_contents.len() > 1)
-            .map(|(current_cgu_name, cgu_contents)| {
-                let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| &s[..]).collect();
-
-                // Sort the names, so things are deterministic and easy to
-                // predict.
-                cgu_contents.sort();
-
-                (current_cgu_name, cgu_contents.join("--"))
-            })
-            .collect();
-
-        for cgu in codegen_units.iter_mut() {
-            if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
-                if tcx.sess.opts.debugging_opts.human_readable_cgu_names {
-                    cgu.set_name(Symbol::intern(&new_cgu_name));
-                } else {
-                    // If we don't require CGU names to be human-readable, we
-                    // use a fixed length hash of the composite CGU name
-                    // instead.
-                    let new_cgu_name = CodegenUnit::mangle_name(&new_cgu_name);
-                    cgu.set_name(Symbol::intern(&new_cgu_name));
-                }
-            }
-        }
-    } else {
-        // If we are compiling non-incrementally we just generate simple CGU
-        // names containing an index.
-        for (index, cgu) in codegen_units.iter_mut().enumerate() {
-            cgu.set_name(numbered_codegen_unit_name(cgu_name_builder, index));
-        }
-    }
-}
-
-fn place_inlined_mono_items<'tcx>(
-    initial_partitioning: PreInliningPartitioning<'tcx>,
-    inlining_map: &InliningMap<'tcx>,
-) -> PostInliningPartitioning<'tcx> {
-    let mut new_partitioning = Vec::new();
-    let mut mono_item_placements = FxHashMap::default();
-
-    let PreInliningPartitioning { codegen_units: initial_cgus, roots, internalization_candidates } =
-        initial_partitioning;
-
-    let single_codegen_unit = initial_cgus.len() == 1;
-
-    for old_codegen_unit in initial_cgus {
-        // Collect all items that need to be available in this codegen unit.
-        let mut reachable = FxHashSet::default();
-        for root in old_codegen_unit.items().keys() {
-            follow_inlining(*root, inlining_map, &mut reachable);
-        }
-
-        let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name());
-
-        // Add all monomorphizations that are not already there.
-        for mono_item in reachable {
-            if let Some(linkage) = old_codegen_unit.items().get(&mono_item) {
-                // This is a root, just copy it over.
-                new_codegen_unit.items_mut().insert(mono_item, *linkage);
-            } else {
-                if roots.contains(&mono_item) {
-                    bug!(
-                        "GloballyShared mono-item inlined into other CGU: \
-                          {:?}",
-                        mono_item
-                    );
-                }
-
-                // This is a CGU-private copy.
-                new_codegen_unit
-                    .items_mut()
-                    .insert(mono_item, (Linkage::Internal, Visibility::Default));
-            }
-
-            if !single_codegen_unit {
-                // If there is more than one codegen unit, we need to keep track
-                // in which codegen units each monomorphization is placed.
-                match mono_item_placements.entry(mono_item) {
-                    Entry::Occupied(e) => {
-                        let placement = e.into_mut();
-                        debug_assert!(match *placement {
-                            MonoItemPlacement::SingleCgu { cgu_name } => {
-                                cgu_name != new_codegen_unit.name()
-                            }
-                            MonoItemPlacement::MultipleCgus => true,
-                        });
-                        *placement = MonoItemPlacement::MultipleCgus;
-                    }
-                    Entry::Vacant(e) => {
-                        e.insert(MonoItemPlacement::SingleCgu {
-                            cgu_name: new_codegen_unit.name(),
-                        });
-                    }
-                }
-            }
-        }
-
-        new_partitioning.push(new_codegen_unit);
-    }
-
-    return PostInliningPartitioning {
-        codegen_units: new_partitioning,
-        mono_item_placements,
-        internalization_candidates,
-    };
-
-    fn follow_inlining<'tcx>(
-        mono_item: MonoItem<'tcx>,
-        inlining_map: &InliningMap<'tcx>,
-        visited: &mut FxHashSet<MonoItem<'tcx>>,
-    ) {
-        if !visited.insert(mono_item) {
-            return;
-        }
-
-        inlining_map.with_inlining_candidates(mono_item, |target| {
-            follow_inlining(target, inlining_map, visited);
-        });
-    }
-}
-
-fn internalize_symbols<'tcx>(
-    _tcx: TyCtxt<'tcx>,
-    partitioning: &mut PostInliningPartitioning<'tcx>,
-    inlining_map: &InliningMap<'tcx>,
-) {
-    if partitioning.codegen_units.len() == 1 {
-        // Fast path for when there is only one codegen unit. In this case we
-        // can internalize all candidates, since there is nowhere else they
-        // could be accessed from.
-        for cgu in &mut partitioning.codegen_units {
-            for candidate in &partitioning.internalization_candidates {
-                cgu.items_mut().insert(*candidate, (Linkage::Internal, Visibility::Default));
-            }
-        }
-
-        return;
-    }
-
-    // Build a map from every monomorphization to all the monomorphizations that
-    // reference it.
-    let mut accessor_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>> = Default::default();
-    inlining_map.iter_accesses(|accessor, accessees| {
-        for accessee in accessees {
-            accessor_map.entry(*accessee).or_default().push(accessor);
-        }
-    });
-
-    let mono_item_placements = &partitioning.mono_item_placements;
-
-    // For each internalization candidates in each codegen unit, check if it is
-    // accessed from outside its defining codegen unit.
-    for cgu in &mut partitioning.codegen_units {
-        let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name() };
-
-        for (accessee, linkage_and_visibility) in cgu.items_mut() {
-            if !partitioning.internalization_candidates.contains(accessee) {
-                // This item is no candidate for internalizing, so skip it.
-                continue;
-            }
-            debug_assert_eq!(mono_item_placements[accessee], home_cgu);
-
-            if let Some(accessors) = accessor_map.get(accessee) {
-                if accessors
-                    .iter()
-                    .filter_map(|accessor| {
-                        // Some accessors might not have been
-                        // instantiated. We can safely ignore those.
-                        mono_item_placements.get(accessor)
-                    })
-                    .any(|placement| *placement != home_cgu)
-                {
-                    // Found an accessor from another CGU, so skip to the next
-                    // item without marking this one as internal.
-                    continue;
-                }
-            }
-
-            // If we got here, we did not find any accesses from other CGUs,
-            // so it's fine to make this monomorphization internal.
-            *linkage_and_visibility = (Linkage::Internal, Visibility::Default);
-        }
-    }
-}
-
-fn characteristic_def_id_of_mono_item<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    mono_item: MonoItem<'tcx>,
-) -> Option<DefId> {
-    match mono_item {
-        MonoItem::Fn(instance) => {
-            let def_id = match instance.def {
-                ty::InstanceDef::Item(def) => def.did,
-                ty::InstanceDef::VtableShim(..)
-                | ty::InstanceDef::ReifyShim(..)
-                | ty::InstanceDef::FnPtrShim(..)
-                | ty::InstanceDef::ClosureOnceShim { .. }
-                | ty::InstanceDef::Intrinsic(..)
-                | ty::InstanceDef::DropGlue(..)
-                | ty::InstanceDef::Virtual(..)
-                | ty::InstanceDef::CloneShim(..) => return None,
-            };
-
-            // If this is a method, we want to put it into the same module as
-            // its self-type. If the self-type does not provide a characteristic
-            // DefId, we use the location of the impl after all.
-
-            if tcx.trait_of_item(def_id).is_some() {
-                let self_ty = instance.substs.type_at(0);
-                // This is a default implementation of a trait method.
-                return characteristic_def_id_of_type(self_ty).or(Some(def_id));
-            }
-
-            if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
-                if tcx.sess.opts.incremental.is_some()
-                    && tcx.trait_id_of_impl(impl_def_id) == tcx.lang_items().drop_trait()
-                {
-                    // Put `Drop::drop` into the same cgu as `drop_in_place`
-                    // since `drop_in_place` is the only thing that can
-                    // call it.
-                    return None;
-                }
-                // This is a method within an impl, find out what the self-type is:
-                let impl_self_ty = tcx.subst_and_normalize_erasing_regions(
-                    instance.substs,
-                    ty::ParamEnv::reveal_all(),
-                    &tcx.type_of(impl_def_id),
-                );
-                if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) {
-                    return Some(def_id);
-                }
-            }
-
-            Some(def_id)
-        }
-        MonoItem::Static(def_id) => Some(def_id),
-        MonoItem::GlobalAsm(hir_id) => Some(tcx.hir().local_def_id(hir_id).to_def_id()),
-    }
-}
-
-type CguNameCache = FxHashMap<(DefId, bool), Symbol>;
-
-fn compute_codegen_unit_name(
-    tcx: TyCtxt<'_>,
-    name_builder: &mut CodegenUnitNameBuilder<'_>,
-    def_id: DefId,
-    volatile: bool,
-    cache: &mut CguNameCache,
-) -> Symbol {
-    // Find the innermost module that is not nested within a function.
-    let mut current_def_id = def_id;
-    let mut cgu_def_id = None;
-    // Walk backwards from the item we want to find the module for.
-    loop {
-        if current_def_id.index == CRATE_DEF_INDEX {
-            if cgu_def_id.is_none() {
-                // If we have not found a module yet, take the crate root.
-                cgu_def_id = Some(DefId { krate: def_id.krate, index: CRATE_DEF_INDEX });
-            }
-            break;
-        } else if tcx.def_kind(current_def_id) == DefKind::Mod {
-            if cgu_def_id.is_none() {
-                cgu_def_id = Some(current_def_id);
-            }
-        } else {
-            // If we encounter something that is not a module, throw away
-            // any module that we've found so far because we now know that
-            // it is nested within something else.
-            cgu_def_id = None;
-        }
-
-        current_def_id = tcx.parent(current_def_id).unwrap();
-    }
-
-    let cgu_def_id = cgu_def_id.unwrap();
-
-    *cache.entry((cgu_def_id, volatile)).or_insert_with(|| {
-        let def_path = tcx.def_path(cgu_def_id);
-
-        let components = def_path.data.iter().map(|part| part.data.as_symbol());
-
-        let volatile_suffix = volatile.then_some("volatile");
-
-        name_builder.build_cgu_name(def_path.krate, components, volatile_suffix)
-    })
-}
-
-fn numbered_codegen_unit_name(
-    name_builder: &mut CodegenUnitNameBuilder<'_>,
-    index: usize,
-) -> Symbol {
-    name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(index))
-}
-
-fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I)
-where
-    I: Iterator<Item = &'a CodegenUnit<'tcx>>,
-    'tcx: 'a,
-{
-    if cfg!(debug_assertions) {
-        debug!("{}", label);
-        for cgu in cgus {
-            debug!("CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate());
-
-            for (mono_item, linkage) in cgu.items() {
-                let symbol_name = mono_item.symbol_name(tcx).name;
-                let symbol_hash_start = symbol_name.rfind('h');
-                let symbol_hash =
-                    symbol_hash_start.map(|i| &symbol_name[i..]).unwrap_or("<no hash>");
-
-                debug!(
-                    " - {} [{:?}] [{}] estimated size {}",
-                    mono_item.to_string(tcx, true),
-                    linkage,
-                    symbol_hash,
-                    mono_item.size_estimate(tcx)
-                );
-            }
-
-            debug!("");
-        }
-    }
-}
-
-#[inline(never)] // give this a place in the profiler
-fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
-where
-    I: Iterator<Item = &'a MonoItem<'tcx>>,
-    'tcx: 'a,
-{
-    let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
-
-    let mut symbols: Vec<_> =
-        mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
-
-    symbols.sort_by_key(|sym| sym.1);
-
-    for pair in symbols.windows(2) {
-        let sym1 = &pair[0].1;
-        let sym2 = &pair[1].1;
-
-        if sym1 == sym2 {
-            let mono_item1 = pair[0].0;
-            let mono_item2 = pair[1].0;
-
-            let span1 = mono_item1.local_span(tcx);
-            let span2 = mono_item2.local_span(tcx);
-
-            // Deterministically select one of the spans for error reporting
-            let span = match (span1, span2) {
-                (Some(span1), Some(span2)) => {
-                    Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
-                }
-                (span1, span2) => span1.or(span2),
-            };
-
-            let error_message = format!("symbol `{}` is already defined", sym1);
-
-            if let Some(span) = span {
-                tcx.sess.span_fatal(span, &error_message)
-            } else {
-                tcx.sess.fatal(&error_message)
-            }
-        }
-    }
-}
-
-fn collect_and_partition_mono_items(
-    tcx: TyCtxt<'tcx>,
-    cnum: CrateNum,
-) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) {
-    assert_eq!(cnum, LOCAL_CRATE);
-
-    let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items {
-        Some(ref s) => {
-            let mode_string = s.to_lowercase();
-            let mode_string = mode_string.trim();
-            if mode_string == "eager" {
-                MonoItemCollectionMode::Eager
-            } else {
-                if mode_string != "lazy" {
-                    let message = format!(
-                        "Unknown codegen-item collection mode '{}'. \
-                                           Falling back to 'lazy' mode.",
-                        mode_string
-                    );
-                    tcx.sess.warn(&message);
-                }
-
-                MonoItemCollectionMode::Lazy
-            }
-        }
-        None => {
-            if tcx.sess.opts.cg.link_dead_code == Some(true) {
-                MonoItemCollectionMode::Eager
-            } else {
-                MonoItemCollectionMode::Lazy
-            }
-        }
-    };
-
-    let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode);
-
-    tcx.sess.abort_if_errors();
-
-    let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
-        sync::join(
-            || {
-                &*tcx.arena.alloc_from_iter(partition(
-                    tcx,
-                    items.iter().cloned(),
-                    tcx.sess.codegen_units(),
-                    &inlining_map,
-                ))
-            },
-            || assert_symbols_are_distinct(tcx, items.iter()),
-        )
-    });
-
-    let mono_items: DefIdSet = items
-        .iter()
-        .filter_map(|mono_item| match *mono_item {
-            MonoItem::Fn(ref instance) => Some(instance.def_id()),
-            MonoItem::Static(def_id) => Some(def_id),
-            _ => None,
-        })
-        .collect();
-
-    if tcx.sess.opts.debugging_opts.print_mono_items.is_some() {
-        let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
-
-        for cgu in codegen_units {
-            for (&mono_item, &linkage) in cgu.items() {
-                item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage));
-            }
-        }
-
-        let mut item_keys: Vec<_> = items
-            .iter()
-            .map(|i| {
-                let mut output = i.to_string(tcx, false);
-                output.push_str(" @@");
-                let mut empty = Vec::new();
-                let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
-                cgus.sort_by_key(|(name, _)| *name);
-                cgus.dedup();
-                for &(ref cgu_name, (linkage, _)) in cgus.iter() {
-                    output.push_str(" ");
-                    output.push_str(&cgu_name.as_str());
-
-                    let linkage_abbrev = match linkage {
-                        Linkage::External => "External",
-                        Linkage::AvailableExternally => "Available",
-                        Linkage::LinkOnceAny => "OnceAny",
-                        Linkage::LinkOnceODR => "OnceODR",
-                        Linkage::WeakAny => "WeakAny",
-                        Linkage::WeakODR => "WeakODR",
-                        Linkage::Appending => "Appending",
-                        Linkage::Internal => "Internal",
-                        Linkage::Private => "Private",
-                        Linkage::ExternalWeak => "ExternalWeak",
-                        Linkage::Common => "Common",
-                    };
-
-                    output.push_str("[");
-                    output.push_str(linkage_abbrev);
-                    output.push_str("]");
-                }
-                output
-            })
-            .collect();
-
-        item_keys.sort();
-
-        for item in item_keys {
-            println!("MONO_ITEM {}", item);
-        }
-    }
-
-    (tcx.arena.alloc(mono_items), codegen_units)
-}
-
-pub fn provide(providers: &mut Providers) {
-    providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
-
-    providers.is_codegened_item = |tcx, def_id| {
-        let (all_mono_items, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
-        all_mono_items.contains(&def_id)
-    };
-
-    providers.codegen_unit = |tcx, name| {
-        let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
-        all.iter()
-            .find(|cgu| cgu.name() == name)
-            .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name))
-    };
-}
diff --git a/src/librustc_mir/monomorphize/partitioning/default.rs b/src/librustc_mir/monomorphize/partitioning/default.rs
new file mode 100644 (file)
index 0000000..b48bae8
--- /dev/null
@@ -0,0 +1,552 @@
+use std::collections::hash_map::Entry;
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::middle::exported_symbols::SymbolExportLevel;
+use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility};
+use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
+use rustc_middle::ty::print::characteristic_def_id_of_type;
+use rustc_middle::ty::{self, DefIdTree, InstanceDef, TyCtxt};
+use rustc_span::symbol::Symbol;
+
+use crate::monomorphize::collector::InliningMap;
+use crate::monomorphize::partitioning::merging;
+use crate::monomorphize::partitioning::{
+    MonoItemPlacement, Partitioner, PostInliningPartitioning, PreInliningPartitioning,
+};
+
+pub struct DefaultPartitioning;
+
+impl<'tcx> Partitioner<'tcx> for DefaultPartitioning {
+    fn place_root_mono_items(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
+    ) -> PreInliningPartitioning<'tcx> {
+        let mut roots = FxHashSet::default();
+        let mut codegen_units = FxHashMap::default();
+        let is_incremental_build = tcx.sess.opts.incremental.is_some();
+        let mut internalization_candidates = FxHashSet::default();
+
+        // Determine if monomorphizations instantiated in this crate will be made
+        // available to downstream crates. This depends on whether we are in
+        // share-generics mode and whether the current crate can even have
+        // downstream crates.
+        let export_generics = tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics();
+
+        let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
+        let cgu_name_cache = &mut FxHashMap::default();
+
+        for mono_item in mono_items {
+            match mono_item.instantiation_mode(tcx) {
+                InstantiationMode::GloballyShared { .. } => {}
+                InstantiationMode::LocalCopy => continue,
+            }
+
+            let characteristic_def_id = characteristic_def_id_of_mono_item(tcx, mono_item);
+            let is_volatile = is_incremental_build && mono_item.is_generic_fn();
+
+            let codegen_unit_name = match characteristic_def_id {
+                Some(def_id) => compute_codegen_unit_name(
+                    tcx,
+                    cgu_name_builder,
+                    def_id,
+                    is_volatile,
+                    cgu_name_cache,
+                ),
+                None => fallback_cgu_name(cgu_name_builder),
+            };
+
+            let codegen_unit = codegen_units
+                .entry(codegen_unit_name)
+                .or_insert_with(|| CodegenUnit::new(codegen_unit_name));
+
+            let mut can_be_internalized = true;
+            let (linkage, visibility) = mono_item_linkage_and_visibility(
+                tcx,
+                &mono_item,
+                &mut can_be_internalized,
+                export_generics,
+            );
+            if visibility == Visibility::Hidden && can_be_internalized {
+                internalization_candidates.insert(mono_item);
+            }
+
+            codegen_unit.items_mut().insert(mono_item, (linkage, visibility));
+            roots.insert(mono_item);
+        }
+
+        // Always ensure we have at least one CGU; otherwise, if we have a
+        // crate with just types (for example), we could wind up with no CGU.
+        if codegen_units.is_empty() {
+            let codegen_unit_name = fallback_cgu_name(cgu_name_builder);
+            codegen_units.insert(codegen_unit_name, CodegenUnit::new(codegen_unit_name));
+        }
+
+        PreInliningPartitioning {
+            codegen_units: codegen_units
+                .into_iter()
+                .map(|(_, codegen_unit)| codegen_unit)
+                .collect(),
+            roots,
+            internalization_candidates,
+        }
+    }
+
+    fn merge_codegen_units(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        initial_partitioning: &mut PreInliningPartitioning<'tcx>,
+        target_cgu_count: usize,
+    ) {
+        merging::merge_codegen_units(tcx, initial_partitioning, target_cgu_count);
+    }
+
+    fn place_inlined_mono_items(
+        &mut self,
+        initial_partitioning: PreInliningPartitioning<'tcx>,
+        inlining_map: &InliningMap<'tcx>,
+    ) -> PostInliningPartitioning<'tcx> {
+        let mut new_partitioning = Vec::new();
+        let mut mono_item_placements = FxHashMap::default();
+
+        let PreInliningPartitioning {
+            codegen_units: initial_cgus,
+            roots,
+            internalization_candidates,
+        } = initial_partitioning;
+
+        let single_codegen_unit = initial_cgus.len() == 1;
+
+        for old_codegen_unit in initial_cgus {
+            // Collect all items that need to be available in this codegen unit.
+            let mut reachable = FxHashSet::default();
+            for root in old_codegen_unit.items().keys() {
+                follow_inlining(*root, inlining_map, &mut reachable);
+            }
+
+            let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name());
+
+            // Add all monomorphizations that are not already there.
+            for mono_item in reachable {
+                if let Some(linkage) = old_codegen_unit.items().get(&mono_item) {
+                    // This is a root, just copy it over.
+                    new_codegen_unit.items_mut().insert(mono_item, *linkage);
+                } else {
+                    if roots.contains(&mono_item) {
+                        bug!(
+                            "GloballyShared mono-item inlined into other CGU: \
+                              {:?}",
+                            mono_item
+                        );
+                    }
+
+                    // This is a CGU-private copy.
+                    new_codegen_unit
+                        .items_mut()
+                        .insert(mono_item, (Linkage::Internal, Visibility::Default));
+                }
+
+                if !single_codegen_unit {
+                    // If there is more than one codegen unit, we need to keep track
+                    // in which codegen units each monomorphization is placed.
+                    match mono_item_placements.entry(mono_item) {
+                        Entry::Occupied(e) => {
+                            let placement = e.into_mut();
+                            debug_assert!(match *placement {
+                                MonoItemPlacement::SingleCgu { cgu_name } => {
+                                    cgu_name != new_codegen_unit.name()
+                                }
+                                MonoItemPlacement::MultipleCgus => true,
+                            });
+                            *placement = MonoItemPlacement::MultipleCgus;
+                        }
+                        Entry::Vacant(e) => {
+                            e.insert(MonoItemPlacement::SingleCgu {
+                                cgu_name: new_codegen_unit.name(),
+                            });
+                        }
+                    }
+                }
+            }
+
+            new_partitioning.push(new_codegen_unit);
+        }
+
+        return PostInliningPartitioning {
+            codegen_units: new_partitioning,
+            mono_item_placements,
+            internalization_candidates,
+        };
+
+        fn follow_inlining<'tcx>(
+            mono_item: MonoItem<'tcx>,
+            inlining_map: &InliningMap<'tcx>,
+            visited: &mut FxHashSet<MonoItem<'tcx>>,
+        ) {
+            if !visited.insert(mono_item) {
+                return;
+            }
+
+            inlining_map.with_inlining_candidates(mono_item, |target| {
+                follow_inlining(target, inlining_map, visited);
+            });
+        }
+    }
+
+    fn internalize_symbols(
+        &mut self,
+        _tcx: TyCtxt<'tcx>,
+        partitioning: &mut PostInliningPartitioning<'tcx>,
+        inlining_map: &InliningMap<'tcx>,
+    ) {
+        if partitioning.codegen_units.len() == 1 {
+            // Fast path for when there is only one codegen unit. In this case we
+            // can internalize all candidates, since there is nowhere else they
+            // could be accessed from.
+            for cgu in &mut partitioning.codegen_units {
+                for candidate in &partitioning.internalization_candidates {
+                    cgu.items_mut().insert(*candidate, (Linkage::Internal, Visibility::Default));
+                }
+            }
+
+            return;
+        }
+
+        // Build a map from every monomorphization to all the monomorphizations that
+        // reference it.
+        let mut accessor_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>> = Default::default();
+        inlining_map.iter_accesses(|accessor, accessees| {
+            for accessee in accessees {
+                accessor_map.entry(*accessee).or_default().push(accessor);
+            }
+        });
+
+        let mono_item_placements = &partitioning.mono_item_placements;
+
+        // For each internalization candidates in each codegen unit, check if it is
+        // accessed from outside its defining codegen unit.
+        for cgu in &mut partitioning.codegen_units {
+            let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name() };
+
+            for (accessee, linkage_and_visibility) in cgu.items_mut() {
+                if !partitioning.internalization_candidates.contains(accessee) {
+                    // This item is no candidate for internalizing, so skip it.
+                    continue;
+                }
+                debug_assert_eq!(mono_item_placements[accessee], home_cgu);
+
+                if let Some(accessors) = accessor_map.get(accessee) {
+                    if accessors
+                        .iter()
+                        .filter_map(|accessor| {
+                            // Some accessors might not have been
+                            // instantiated. We can safely ignore those.
+                            mono_item_placements.get(accessor)
+                        })
+                        .any(|placement| *placement != home_cgu)
+                    {
+                        // Found an accessor from another CGU, so skip to the next
+                        // item without marking this one as internal.
+                        continue;
+                    }
+                }
+
+                // If we got here, we did not find any accesses from other CGUs,
+                // so it's fine to make this monomorphization internal.
+                *linkage_and_visibility = (Linkage::Internal, Visibility::Default);
+            }
+        }
+    }
+}
+
+fn characteristic_def_id_of_mono_item<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mono_item: MonoItem<'tcx>,
+) -> Option<DefId> {
+    match mono_item {
+        MonoItem::Fn(instance) => {
+            let def_id = match instance.def {
+                ty::InstanceDef::Item(def) => def.did,
+                ty::InstanceDef::VtableShim(..)
+                | ty::InstanceDef::ReifyShim(..)
+                | ty::InstanceDef::FnPtrShim(..)
+                | ty::InstanceDef::ClosureOnceShim { .. }
+                | ty::InstanceDef::Intrinsic(..)
+                | ty::InstanceDef::DropGlue(..)
+                | ty::InstanceDef::Virtual(..)
+                | ty::InstanceDef::CloneShim(..) => return None,
+            };
+
+            // If this is a method, we want to put it into the same module as
+            // its self-type. If the self-type does not provide a characteristic
+            // DefId, we use the location of the impl after all.
+
+            if tcx.trait_of_item(def_id).is_some() {
+                let self_ty = instance.substs.type_at(0);
+                // This is a default implementation of a trait method.
+                return characteristic_def_id_of_type(self_ty).or(Some(def_id));
+            }
+
+            if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
+                if tcx.sess.opts.incremental.is_some()
+                    && tcx.trait_id_of_impl(impl_def_id) == tcx.lang_items().drop_trait()
+                {
+                    // Put `Drop::drop` into the same cgu as `drop_in_place`
+                    // since `drop_in_place` is the only thing that can
+                    // call it.
+                    return None;
+                }
+                // This is a method within an impl, find out what the self-type is:
+                let impl_self_ty = tcx.subst_and_normalize_erasing_regions(
+                    instance.substs,
+                    ty::ParamEnv::reveal_all(),
+                    &tcx.type_of(impl_def_id),
+                );
+                if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) {
+                    return Some(def_id);
+                }
+            }
+
+            Some(def_id)
+        }
+        MonoItem::Static(def_id) => Some(def_id),
+        MonoItem::GlobalAsm(hir_id) => Some(tcx.hir().local_def_id(hir_id).to_def_id()),
+    }
+}
+
+fn compute_codegen_unit_name(
+    tcx: TyCtxt<'_>,
+    name_builder: &mut CodegenUnitNameBuilder<'_>,
+    def_id: DefId,
+    volatile: bool,
+    cache: &mut CguNameCache,
+) -> Symbol {
+    // Find the innermost module that is not nested within a function.
+    let mut current_def_id = def_id;
+    let mut cgu_def_id = None;
+    // Walk backwards from the item we want to find the module for.
+    loop {
+        if current_def_id.index == CRATE_DEF_INDEX {
+            if cgu_def_id.is_none() {
+                // If we have not found a module yet, take the crate root.
+                cgu_def_id = Some(DefId { krate: def_id.krate, index: CRATE_DEF_INDEX });
+            }
+            break;
+        } else if tcx.def_kind(current_def_id) == DefKind::Mod {
+            if cgu_def_id.is_none() {
+                cgu_def_id = Some(current_def_id);
+            }
+        } else {
+            // If we encounter something that is not a module, throw away
+            // any module that we've found so far because we now know that
+            // it is nested within something else.
+            cgu_def_id = None;
+        }
+
+        current_def_id = tcx.parent(current_def_id).unwrap();
+    }
+
+    let cgu_def_id = cgu_def_id.unwrap();
+
+    *cache.entry((cgu_def_id, volatile)).or_insert_with(|| {
+        let def_path = tcx.def_path(cgu_def_id);
+
+        let components = def_path.data.iter().map(|part| part.data.as_symbol());
+
+        let volatile_suffix = volatile.then_some("volatile");
+
+        name_builder.build_cgu_name(def_path.krate, components, volatile_suffix)
+    })
+}
+
+// Anything we can't find a proper codegen unit for goes into this.
+fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol {
+    name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu"))
+}
+
+fn mono_item_linkage_and_visibility(
+    tcx: TyCtxt<'tcx>,
+    mono_item: &MonoItem<'tcx>,
+    can_be_internalized: &mut bool,
+    export_generics: bool,
+) -> (Linkage, Visibility) {
+    if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
+        return (explicit_linkage, Visibility::Default);
+    }
+    let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics);
+    (Linkage::External, vis)
+}
+
+type CguNameCache = FxHashMap<(DefId, bool), Symbol>;
+
+fn mono_item_visibility(
+    tcx: TyCtxt<'tcx>,
+    mono_item: &MonoItem<'tcx>,
+    can_be_internalized: &mut bool,
+    export_generics: bool,
+) -> Visibility {
+    let instance = match mono_item {
+        // This is pretty complicated; see below.
+        MonoItem::Fn(instance) => instance,
+
+        // Misc handling for generics and such, but otherwise:
+        MonoItem::Static(def_id) => {
+            return if tcx.is_reachable_non_generic(*def_id) {
+                *can_be_internalized = false;
+                default_visibility(tcx, *def_id, false)
+            } else {
+                Visibility::Hidden
+            };
+        }
+        MonoItem::GlobalAsm(hir_id) => {
+            let def_id = tcx.hir().local_def_id(*hir_id);
+            return if tcx.is_reachable_non_generic(def_id) {
+                *can_be_internalized = false;
+                default_visibility(tcx, def_id.to_def_id(), false)
+            } else {
+                Visibility::Hidden
+            };
+        }
+    };
+
+    let def_id = match instance.def {
+        InstanceDef::Item(def) => def.did,
+        InstanceDef::DropGlue(def_id, Some(_)) => def_id,
+
+        // These are all compiler glue and such, never exported, always hidden.
+        InstanceDef::VtableShim(..)
+        | InstanceDef::ReifyShim(..)
+        | InstanceDef::FnPtrShim(..)
+        | InstanceDef::Virtual(..)
+        | InstanceDef::Intrinsic(..)
+        | InstanceDef::ClosureOnceShim { .. }
+        | InstanceDef::DropGlue(..)
+        | InstanceDef::CloneShim(..) => return Visibility::Hidden,
+    };
+
+    // The `start_fn` lang item is actually a monomorphized instance of a
+    // function in the standard library, used for the `main` function. We don't
+    // want to export it so we tag it with `Hidden` visibility but this symbol
+    // is only referenced from the actual `main` symbol which we unfortunately
+    // don't know anything about during partitioning/collection. As a result we
+    // forcibly keep this symbol out of the `internalization_candidates` set.
+    //
+    // FIXME: eventually we don't want to always force this symbol to have
+    //        hidden visibility, it should indeed be a candidate for
+    //        internalization, but we have to understand that it's referenced
+    //        from the `main` symbol we'll generate later.
+    //
+    //        This may be fixable with a new `InstanceDef` perhaps? Unsure!
+    if tcx.lang_items().start_fn() == Some(def_id) {
+        *can_be_internalized = false;
+        return Visibility::Hidden;
+    }
+
+    let is_generic = instance.substs.non_erasable_generics().next().is_some();
+
+    // Upstream `DefId` instances get different handling than local ones.
+    if !def_id.is_local() {
+        return if export_generics && is_generic {
+            // If it is a upstream monomorphization and we export generics, we must make
+            // it available to downstream crates.
+            *can_be_internalized = false;
+            default_visibility(tcx, def_id, true)
+        } else {
+            Visibility::Hidden
+        };
+    }
+
+    if is_generic {
+        if export_generics {
+            if tcx.is_unreachable_local_definition(def_id) {
+                // This instance cannot be used from another crate.
+                Visibility::Hidden
+            } else {
+                // This instance might be useful in a downstream crate.
+                *can_be_internalized = false;
+                default_visibility(tcx, def_id, true)
+            }
+        } else {
+            // We are not exporting generics or the definition is not reachable
+            // for downstream crates, we can internalize its instantiations.
+            Visibility::Hidden
+        }
+    } else {
+        // If this isn't a generic function then we mark this a `Default` if
+        // this is a reachable item, meaning that it's a symbol other crates may
+        // access when they link to us.
+        if tcx.is_reachable_non_generic(def_id) {
+            *can_be_internalized = false;
+            debug_assert!(!is_generic);
+            return default_visibility(tcx, def_id, false);
+        }
+
+        // If this isn't reachable then we're gonna tag this with `Hidden`
+        // visibility. In some situations though we'll want to prevent this
+        // symbol from being internalized.
+        //
+        // There's two categories of items here:
+        //
+        // * First is weak lang items. These are basically mechanisms for
+        //   libcore to forward-reference symbols defined later in crates like
+        //   the standard library or `#[panic_handler]` definitions. The
+        //   definition of these weak lang items needs to be referenceable by
+        //   libcore, so we're no longer a candidate for internalization.
+        //   Removal of these functions can't be done by LLVM but rather must be
+        //   done by the linker as it's a non-local decision.
+        //
+        // * Second is "std internal symbols". Currently this is primarily used
+        //   for allocator symbols. Allocators are a little weird in their
+        //   implementation, but the idea is that the compiler, at the last
+        //   minute, defines an allocator with an injected object file. The
+        //   `alloc` crate references these symbols (`__rust_alloc`) and the
+        //   definition doesn't get hooked up until a linked crate artifact is
+        //   generated.
+        //
+        //   The symbols synthesized by the compiler (`__rust_alloc`) are thin
+        //   veneers around the actual implementation, some other symbol which
+        //   implements the same ABI. These symbols (things like `__rg_alloc`,
+        //   `__rdl_alloc`, `__rde_alloc`, etc), are all tagged with "std
+        //   internal symbols".
+        //
+        //   The std-internal symbols here **should not show up in a dll as an
+        //   exported interface**, so they return `false` from
+        //   `is_reachable_non_generic` above and we'll give them `Hidden`
+        //   visibility below. Like the weak lang items, though, we can't let
+        //   LLVM internalize them as this decision is left up to the linker to
+        //   omit them, so prevent them from being internalized.
+        let attrs = tcx.codegen_fn_attrs(def_id);
+        if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
+            *can_be_internalized = false;
+        }
+
+        Visibility::Hidden
+    }
+}
+
+fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility {
+    if !tcx.sess.target.target.options.default_hidden_visibility {
+        return Visibility::Default;
+    }
+
+    // Generic functions never have export-level C.
+    if is_generic {
+        return Visibility::Hidden;
+    }
+
+    // Things with export level C don't get instantiated in
+    // downstream crates.
+    if !id.is_local() {
+        return Visibility::Hidden;
+    }
+
+    // C-export level items remain at `Default`, all other internal
+    // items become `Hidden`.
+    match tcx.reachable_non_generics(id.krate).get(&id) {
+        Some(SymbolExportLevel::C) => Visibility::Default,
+        _ => Visibility::Hidden,
+    }
+}
diff --git a/src/librustc_mir/monomorphize/partitioning/merging.rs b/src/librustc_mir/monomorphize/partitioning/merging.rs
new file mode 100644 (file)
index 0000000..1787e6d
--- /dev/null
@@ -0,0 +1,110 @@
+use std::cmp;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder};
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::{Symbol, SymbolStr};
+
+use crate::monomorphize::partitioning::PreInliningPartitioning;
+
+pub fn merge_codegen_units<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    initial_partitioning: &mut PreInliningPartitioning<'tcx>,
+    target_cgu_count: usize,
+) {
+    assert!(target_cgu_count >= 1);
+    let codegen_units = &mut initial_partitioning.codegen_units;
+
+    // Note that at this point in time the `codegen_units` here may not be in a
+    // deterministic order (but we know they're deterministically the same set).
+    // We want this merging to produce a deterministic ordering of codegen units
+    // from the input.
+    //
+    // Due to basically how we've implemented the merging below (merge the two
+    // smallest into each other) we're sure to start off with a deterministic
+    // order (sorted by name). This'll mean that if two cgus have the same size
+    // the stable sort below will keep everything nice and deterministic.
+    codegen_units.sort_by_cached_key(|cgu| cgu.name().as_str());
+
+    // This map keeps track of what got merged into what.
+    let mut cgu_contents: FxHashMap<Symbol, Vec<SymbolStr>> =
+        codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name().as_str()])).collect();
+
+    // Merge the two smallest codegen units until the target size is reached.
+    while codegen_units.len() > target_cgu_count {
+        // Sort small cgus to the back
+        codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate()));
+        let mut smallest = codegen_units.pop().unwrap();
+        let second_smallest = codegen_units.last_mut().unwrap();
+
+        // Move the mono-items from `smallest` to `second_smallest`
+        second_smallest.modify_size_estimate(smallest.size_estimate());
+        for (k, v) in smallest.items_mut().drain() {
+            second_smallest.items_mut().insert(k, v);
+        }
+
+        // Record that `second_smallest` now contains all the stuff that was in
+        // `smallest` before.
+        let mut consumed_cgu_names = cgu_contents.remove(&smallest.name()).unwrap();
+        cgu_contents.get_mut(&second_smallest.name()).unwrap().extend(consumed_cgu_names.drain(..));
+
+        debug!(
+            "CodegenUnit {} merged into CodegenUnit {}",
+            smallest.name(),
+            second_smallest.name()
+        );
+    }
+
+    let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
+
+    if tcx.sess.opts.incremental.is_some() {
+        // If we are doing incremental compilation, we want CGU names to
+        // reflect the path of the source level module they correspond to.
+        // For CGUs that contain the code of multiple modules because of the
+        // merging done above, we use a concatenation of the names of
+        // all contained CGUs.
+        let new_cgu_names: FxHashMap<Symbol, String> = cgu_contents
+            .into_iter()
+            // This `filter` makes sure we only update the name of CGUs that
+            // were actually modified by merging.
+            .filter(|(_, cgu_contents)| cgu_contents.len() > 1)
+            .map(|(current_cgu_name, cgu_contents)| {
+                let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| &s[..]).collect();
+
+                // Sort the names, so things are deterministic and easy to
+                // predict.
+                cgu_contents.sort();
+
+                (current_cgu_name, cgu_contents.join("--"))
+            })
+            .collect();
+
+        for cgu in codegen_units.iter_mut() {
+            if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
+                if tcx.sess.opts.debugging_opts.human_readable_cgu_names {
+                    cgu.set_name(Symbol::intern(&new_cgu_name));
+                } else {
+                    // If we don't require CGU names to be human-readable, we
+                    // use a fixed length hash of the composite CGU name
+                    // instead.
+                    let new_cgu_name = CodegenUnit::mangle_name(&new_cgu_name);
+                    cgu.set_name(Symbol::intern(&new_cgu_name));
+                }
+            }
+        }
+    } else {
+        // If we are compiling non-incrementally we just generate simple CGU
+        // names containing an index.
+        for (index, cgu) in codegen_units.iter_mut().enumerate() {
+            cgu.set_name(numbered_codegen_unit_name(cgu_name_builder, index));
+        }
+    }
+}
+
+fn numbered_codegen_unit_name(
+    name_builder: &mut CodegenUnitNameBuilder<'_>,
+    index: usize,
+) -> Symbol {
+    name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(index))
+}
diff --git a/src/librustc_mir/monomorphize/partitioning/mod.rs b/src/librustc_mir/monomorphize/partitioning/mod.rs
new file mode 100644 (file)
index 0000000..9dfbd65
--- /dev/null
@@ -0,0 +1,433 @@
+//! Partitioning Codegen Units for Incremental Compilation
+//! ======================================================
+//!
+//! The task of this module is to take the complete set of monomorphizations of
+//! a crate and produce a set of codegen units from it, where a codegen unit
+//! is a named set of (mono-item, linkage) pairs. That is, this module
+//! decides which monomorphization appears in which codegen units with which
+//! linkage. The following paragraphs describe some of the background on the
+//! partitioning scheme.
+//!
+//! The most important opportunity for saving on compilation time with
+//! incremental compilation is to avoid re-codegenning and re-optimizing code.
+//! Since the unit of codegen and optimization for LLVM is "modules" or, how
+//! we call them "codegen units", the particulars of how much time can be saved
+//! by incremental compilation are tightly linked to how the output program is
+//! partitioned into these codegen units prior to passing it to LLVM --
+//! especially because we have to treat codegen units as opaque entities once
+//! they are created: There is no way for us to incrementally update an existing
+//! LLVM module and so we have to build any such module from scratch if it was
+//! affected by some change in the source code.
+//!
+//! From that point of view it would make sense to maximize the number of
+//! codegen units by, for example, putting each function into its own module.
+//! That way only those modules would have to be re-compiled that were actually
+//! affected by some change, minimizing the number of functions that could have
+//! been re-used but just happened to be located in a module that is
+//! re-compiled.
+//!
+//! However, since LLVM optimization does not work across module boundaries,
+//! using such a highly granular partitioning would lead to very slow runtime
+//! code since it would effectively prohibit inlining and other inter-procedure
+//! optimizations. We want to avoid that as much as possible.
+//!
+//! Thus we end up with a trade-off: The bigger the codegen units, the better
+//! LLVM's optimizer can do its work, but also the smaller the compilation time
+//! reduction we get from incremental compilation.
+//!
+//! Ideally, we would create a partitioning such that there are few big codegen
+//! units with few interdependencies between them. For now though, we use the
+//! following heuristic to determine the partitioning:
+//!
+//! - There are two codegen units for every source-level module:
+//! - One for "stable", that is non-generic, code
+//! - One for more "volatile" code, i.e., monomorphized instances of functions
+//!   defined in that module
+//!
+//! In order to see why this heuristic makes sense, let's take a look at when a
+//! codegen unit can get invalidated:
+//!
+//! 1. The most straightforward case is when the BODY of a function or global
+//! changes. Then any codegen unit containing the code for that item has to be
+//! re-compiled. Note that this includes all codegen units where the function
+//! has been inlined.
+//!
+//! 2. The next case is when the SIGNATURE of a function or global changes. In
+//! this case, all codegen units containing a REFERENCE to that item have to be
+//! re-compiled. This is a superset of case 1.
+//!
+//! 3. The final and most subtle case is when a REFERENCE to a generic function
+//! is added or removed somewhere. Even though the definition of the function
+//! might be unchanged, a new REFERENCE might introduce a new monomorphized
+//! instance of this function which has to be placed and compiled somewhere.
+//! Conversely, when removing a REFERENCE, it might have been the last one with
+//! that particular set of generic arguments and thus we have to remove it.
+//!
+//! From the above we see that just using one codegen unit per source-level
+//! module is not such a good idea, since just adding a REFERENCE to some
+//! generic item somewhere else would invalidate everything within the module
+//! containing the generic item. The heuristic above reduces this detrimental
+//! side-effect of references a little by at least not touching the non-generic
+//! code of the module.
+//!
+//! A Note on Inlining
+//! ------------------
+//! As briefly mentioned above, in order for LLVM to be able to inline a
+//! function call, the body of the function has to be available in the LLVM
+//! module where the call is made. This has a few consequences for partitioning:
+//!
+//! - The partitioning algorithm has to take care of placing functions into all
+//!   codegen units where they should be available for inlining. It also has to
+//!   decide on the correct linkage for these functions.
+//!
+//! - The partitioning algorithm has to know which functions are likely to get
+//!   inlined, so it can distribute function instantiations accordingly. Since
+//!   there is no way of knowing for sure which functions LLVM will decide to
+//!   inline in the end, we apply a heuristic here: Only functions marked with
+//!   `#[inline]` are considered for inlining by the partitioner. The current
+//!   implementation will not try to determine if a function is likely to be
+//!   inlined by looking at the functions definition.
+//!
+//! Note though that as a side-effect of creating a codegen units per
+//! source-level module, functions from the same module will be available for
+//! inlining, even when they are not marked `#[inline]`.
+
+mod default;
+mod merging;
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::sync;
+use rustc_hir::def_id::{CrateNum, DefIdSet, LOCAL_CRATE};
+use rustc_middle::mir::mono::MonoItem;
+use rustc_middle::mir::mono::{CodegenUnit, Linkage};
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::Symbol;
+
+use crate::monomorphize::collector::InliningMap;
+use crate::monomorphize::collector::{self, MonoItemCollectionMode};
+
+trait Partitioner<'tcx> {
+    fn place_root_mono_items(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
+    ) -> PreInliningPartitioning<'tcx>;
+
+    fn merge_codegen_units(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        initial_partitioning: &mut PreInliningPartitioning<'tcx>,
+        target_cgu_count: usize,
+    );
+
+    fn place_inlined_mono_items(
+        &mut self,
+        initial_partitioning: PreInliningPartitioning<'tcx>,
+        inlining_map: &InliningMap<'tcx>,
+    ) -> PostInliningPartitioning<'tcx>;
+
+    fn internalize_symbols(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        partitioning: &mut PostInliningPartitioning<'tcx>,
+        inlining_map: &InliningMap<'tcx>,
+    );
+}
+
+fn get_partitioner<'tcx>(tcx: TyCtxt<'tcx>) -> Box<dyn Partitioner<'tcx>> {
+    let strategy = match &tcx.sess.opts.debugging_opts.cgu_partitioning_strategy {
+        None => "default",
+        Some(s) => &s[..],
+    };
+
+    match strategy {
+        "default" => Box::new(default::DefaultPartitioning),
+        _ => tcx.sess.fatal("unknown partitioning strategy"),
+    }
+}
+
+pub fn partition<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
+    max_cgu_count: usize,
+    inlining_map: &InliningMap<'tcx>,
+) -> Vec<CodegenUnit<'tcx>> {
+    let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
+
+    let mut partitioner = get_partitioner(tcx);
+    // In the first step, we place all regular monomorphizations into their
+    // respective 'home' codegen unit. Regular monomorphizations are all
+    // functions and statics defined in the local crate.
+    let mut initial_partitioning = {
+        let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots");
+        partitioner.place_root_mono_items(tcx, mono_items)
+    };
+
+    initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
+
+    debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter());
+
+    // Merge until we have at most `max_cgu_count` codegen units.
+    {
+        let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
+        partitioner.merge_codegen_units(tcx, &mut initial_partitioning, max_cgu_count);
+        debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter());
+    }
+
+    // In the next step, we use the inlining map to determine which additional
+    // monomorphizations have to go into each codegen unit. These additional
+    // monomorphizations can be drop-glue, functions from external crates, and
+    // local functions the definition of which is marked with `#[inline]`.
+    let mut post_inlining = {
+        let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items");
+        partitioner.place_inlined_mono_items(initial_partitioning, inlining_map)
+    };
+
+    post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
+
+    debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter());
+
+    // Next we try to make as many symbols "internal" as possible, so LLVM has
+    // more freedom to optimize.
+    if tcx.sess.opts.cg.link_dead_code != Some(true) {
+        let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
+        partitioner.internalize_symbols(tcx, &mut post_inlining, inlining_map);
+    }
+
+    // Finally, sort by codegen unit name, so that we get deterministic results.
+    let PostInliningPartitioning {
+        codegen_units: mut result,
+        mono_item_placements: _,
+        internalization_candidates: _,
+    } = post_inlining;
+
+    result.sort_by_cached_key(|cgu| cgu.name().as_str());
+
+    result
+}
+
+pub struct PreInliningPartitioning<'tcx> {
+    codegen_units: Vec<CodegenUnit<'tcx>>,
+    roots: FxHashSet<MonoItem<'tcx>>,
+    internalization_candidates: FxHashSet<MonoItem<'tcx>>,
+}
+
+/// For symbol internalization, we need to know whether a symbol/mono-item is
+/// accessed from outside the codegen unit it is defined in. This type is used
+/// to keep track of that.
+#[derive(Clone, PartialEq, Eq, Debug)]
+enum MonoItemPlacement {
+    SingleCgu { cgu_name: Symbol },
+    MultipleCgus,
+}
+
+struct PostInliningPartitioning<'tcx> {
+    codegen_units: Vec<CodegenUnit<'tcx>>,
+    mono_item_placements: FxHashMap<MonoItem<'tcx>, MonoItemPlacement>,
+    internalization_candidates: FxHashSet<MonoItem<'tcx>>,
+}
+
+fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I)
+where
+    I: Iterator<Item = &'a CodegenUnit<'tcx>>,
+    'tcx: 'a,
+{
+    if cfg!(debug_assertions) {
+        debug!("{}", label);
+        for cgu in cgus {
+            debug!("CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate());
+
+            for (mono_item, linkage) in cgu.items() {
+                let symbol_name = mono_item.symbol_name(tcx).name;
+                let symbol_hash_start = symbol_name.rfind('h');
+                let symbol_hash =
+                    symbol_hash_start.map(|i| &symbol_name[i..]).unwrap_or("<no hash>");
+
+                debug!(
+                    " - {} [{:?}] [{}] estimated size {}",
+                    mono_item.to_string(tcx, true),
+                    linkage,
+                    symbol_hash,
+                    mono_item.size_estimate(tcx)
+                );
+            }
+
+            debug!("");
+        }
+    }
+}
+
+#[inline(never)] // give this a place in the profiler
+fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
+where
+    I: Iterator<Item = &'a MonoItem<'tcx>>,
+    'tcx: 'a,
+{
+    let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
+
+    let mut symbols: Vec<_> =
+        mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
+
+    symbols.sort_by_key(|sym| sym.1);
+
+    for pair in symbols.windows(2) {
+        let sym1 = &pair[0].1;
+        let sym2 = &pair[1].1;
+
+        if sym1 == sym2 {
+            let mono_item1 = pair[0].0;
+            let mono_item2 = pair[1].0;
+
+            let span1 = mono_item1.local_span(tcx);
+            let span2 = mono_item2.local_span(tcx);
+
+            // Deterministically select one of the spans for error reporting
+            let span = match (span1, span2) {
+                (Some(span1), Some(span2)) => {
+                    Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
+                }
+                (span1, span2) => span1.or(span2),
+            };
+
+            let error_message = format!("symbol `{}` is already defined", sym1);
+
+            if let Some(span) = span {
+                tcx.sess.span_fatal(span, &error_message)
+            } else {
+                tcx.sess.fatal(&error_message)
+            }
+        }
+    }
+}
+
+fn collect_and_partition_mono_items<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    cnum: CrateNum,
+) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) {
+    assert_eq!(cnum, LOCAL_CRATE);
+
+    let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items {
+        Some(ref s) => {
+            let mode_string = s.to_lowercase();
+            let mode_string = mode_string.trim();
+            if mode_string == "eager" {
+                MonoItemCollectionMode::Eager
+            } else {
+                if mode_string != "lazy" {
+                    let message = format!(
+                        "Unknown codegen-item collection mode '{}'. \
+                                           Falling back to 'lazy' mode.",
+                        mode_string
+                    );
+                    tcx.sess.warn(&message);
+                }
+
+                MonoItemCollectionMode::Lazy
+            }
+        }
+        None => {
+            if tcx.sess.opts.cg.link_dead_code == Some(true) {
+                MonoItemCollectionMode::Eager
+            } else {
+                MonoItemCollectionMode::Lazy
+            }
+        }
+    };
+
+    let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode);
+
+    tcx.sess.abort_if_errors();
+
+    let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
+        sync::join(
+            || {
+                &*tcx.arena.alloc_from_iter(partition(
+                    tcx,
+                    &mut items.iter().cloned(),
+                    tcx.sess.codegen_units(),
+                    &inlining_map,
+                ))
+            },
+            || assert_symbols_are_distinct(tcx, items.iter()),
+        )
+    });
+
+    let mono_items: DefIdSet = items
+        .iter()
+        .filter_map(|mono_item| match *mono_item {
+            MonoItem::Fn(ref instance) => Some(instance.def_id()),
+            MonoItem::Static(def_id) => Some(def_id),
+            _ => None,
+        })
+        .collect();
+
+    if tcx.sess.opts.debugging_opts.print_mono_items.is_some() {
+        let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
+
+        for cgu in codegen_units {
+            for (&mono_item, &linkage) in cgu.items() {
+                item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage));
+            }
+        }
+
+        let mut item_keys: Vec<_> = items
+            .iter()
+            .map(|i| {
+                let mut output = i.to_string(tcx, false);
+                output.push_str(" @@");
+                let mut empty = Vec::new();
+                let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
+                cgus.sort_by_key(|(name, _)| *name);
+                cgus.dedup();
+                for &(ref cgu_name, (linkage, _)) in cgus.iter() {
+                    output.push_str(" ");
+                    output.push_str(&cgu_name.as_str());
+
+                    let linkage_abbrev = match linkage {
+                        Linkage::External => "External",
+                        Linkage::AvailableExternally => "Available",
+                        Linkage::LinkOnceAny => "OnceAny",
+                        Linkage::LinkOnceODR => "OnceODR",
+                        Linkage::WeakAny => "WeakAny",
+                        Linkage::WeakODR => "WeakODR",
+                        Linkage::Appending => "Appending",
+                        Linkage::Internal => "Internal",
+                        Linkage::Private => "Private",
+                        Linkage::ExternalWeak => "ExternalWeak",
+                        Linkage::Common => "Common",
+                    };
+
+                    output.push_str("[");
+                    output.push_str(linkage_abbrev);
+                    output.push_str("]");
+                }
+                output
+            })
+            .collect();
+
+        item_keys.sort();
+
+        for item in item_keys {
+            println!("MONO_ITEM {}", item);
+        }
+    }
+
+    (tcx.arena.alloc(mono_items), codegen_units)
+}
+
+pub fn provide(providers: &mut Providers) {
+    providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
+
+    providers.is_codegened_item = |tcx, def_id| {
+        let (all_mono_items, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
+        all_mono_items.contains(&def_id)
+    };
+
+    providers.codegen_unit = |tcx, name| {
+        let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
+        all.iter()
+            .find(|cgu| cgu.name() == name)
+            .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name))
+    };
+}
index 6a7653b60752db1164fa40bd6f78ee33c692e501..479b6c2a6ca9fb33e0b2088d42322ea5fbace51c 100644 (file)
@@ -1,6 +1,6 @@
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::FnMutTraitLangItem;
+use rustc_hir::lang_items::LangItem;
 use rustc_middle::mir::*;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -62,7 +62,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
             build_call_shim(tcx, instance, None, CallKind::Direct(def_id), None)
         }
         ty::InstanceDef::ClosureOnceShim { call_once: _ } => {
-            let fn_mut = tcx.require_lang_item(FnMutTraitLangItem, None);
+            let fn_mut = tcx.require_lang_item(LangItem::FnMut, None);
             let call_mut = tcx
                 .associated_items(fn_mut)
                 .in_definition_order()
@@ -295,7 +295,7 @@ fn deref_subpath(&self, _path: Self::Path) -> Option<Self::Path> {
     fn downcast_subpath(&self, _path: Self::Path, _variant: VariantIdx) -> Option<Self::Path> {
         Some(())
     }
-    fn array_subpath(&self, _path: Self::Path, _index: u32, _size: u32) -> Option<Self::Path> {
+    fn array_subpath(&self, _path: Self::Path, _index: u64, _size: u64) -> Option<Self::Path> {
         None
     }
 }
index 4812ef5a89455a19496c233f932daa0b1134ca7f..e21f314ca150b03b2a49ffdd6d8889e648c54568 100644 (file)
@@ -1,7 +1,7 @@
 //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
 
 use rustc_errors::struct_span_err;
-use rustc_hir::{self as hir, lang_items};
+use rustc_hir::{self as hir, LangItem};
 use rustc_hir::{def_id::DefId, HirId};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
@@ -618,7 +618,7 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId)
     tcx.infer_ctxt().enter(|infcx| {
         let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
         let mut fulfillment_cx = traits::FulfillmentContext::new();
-        let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
+        let sync_def_id = tcx.require_lang_item(LangItem::Sync, Some(body.span));
         fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
         if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
             infcx.report_fulfillment_errors(&err, None, false);
index ad49090bfc50c1dbc3b5f6e0d67294adad7d988e..5f1930693568cc83a8d8a9c8c9adb313738d3f65 100644 (file)
@@ -219,7 +219,7 @@ fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path> {
         })
     }
 
-    fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option<Self::Path> {
+    fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option<Self::Path> {
         dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e {
             ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
                 debug_assert!(size == min_length, "min_length should be exact for arrays");
index e2f22c115d0792dccf791a74fec4da14e22b5eab..a22075e760a90885dcf2a45b932e8a38e7c4728b 100644 (file)
@@ -62,7 +62,7 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::{GeneratorStateLangItem, PinTypeLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_index::bit_set::{BitMatrix, BitSet};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
@@ -395,7 +395,7 @@ fn make_generator_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Bo
 fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     let ref_gen_ty = body.local_decls.raw[1].ty;
 
-    let pin_did = tcx.require_lang_item(PinTypeLangItem, Some(body.span));
+    let pin_did = tcx.require_lang_item(LangItem::Pin, Some(body.span));
     let pin_adt_ref = tcx.adt_def(pin_did);
     let substs = tcx.intern_substs(&[ref_gen_ty.into()]);
     let pin_ref_gen_ty = tcx.mk_adt(pin_adt_ref, substs);
@@ -1270,7 +1270,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'
         };
 
         // Compute GeneratorState<yield_ty, return_ty>
-        let state_did = tcx.require_lang_item(GeneratorStateLangItem, None);
+        let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
         let state_adt_ref = tcx.adt_def(state_did);
         let state_substs = tcx.intern_substs(&[yield_ty.into(), body.return_ty().into()]);
         let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
index 0db5bd662ca39a01393ffee0892af83874549f1a..c3a34756122a176ea8ea872e207429be99b88147 100644 (file)
@@ -39,6 +39,7 @@
 pub mod rustc_peek;
 pub mod simplify;
 pub mod simplify_branches;
+pub mod simplify_comparison_integral;
 pub mod simplify_try;
 pub mod uninhabited_enum_branching;
 pub mod unreachable_prop;
@@ -456,6 +457,7 @@ fn run_optimization_passes<'tcx>(
         &match_branches::MatchBranchSimplification,
         &const_prop::ConstProp,
         &simplify_branches::SimplifyBranches::new("after-const-prop"),
+        &simplify_comparison_integral::SimplifyComparisonIntegral,
         &simplify_try::SimplifyArmIdentity,
         &simplify_try::SimplifyBranchSame,
         &copy_prop::CopyPropagation,
diff --git a/src/librustc_mir/transform/simplify_comparison_integral.rs b/src/librustc_mir/transform/simplify_comparison_integral.rs
new file mode 100644 (file)
index 0000000..a450a75
--- /dev/null
@@ -0,0 +1,226 @@
+use super::{MirPass, MirSource};
+use rustc_middle::{
+    mir::{
+        interpret::Scalar, BasicBlock, BinOp, Body, Operand, Place, Rvalue, Statement,
+        StatementKind, TerminatorKind,
+    },
+    ty::{Ty, TyCtxt},
+};
+
+/// Pass to convert `if` conditions on integrals into switches on the integral.
+/// For an example, it turns something like
+///
+/// ```
+/// _3 = Eq(move _4, const 43i32);
+/// StorageDead(_4);
+/// switchInt(_3) -> [false: bb2, otherwise: bb3];
+/// ```
+///
+/// into:
+///
+/// ```
+/// switchInt(_4) -> [43i32: bb3, otherwise: bb2];
+/// ```
+pub struct SimplifyComparisonIntegral;
+
+impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral {
+    fn run_pass(&self, _: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+        trace!("Running SimplifyComparisonIntegral on {:?}", source);
+
+        let helper = OptimizationFinder { body };
+        let opts = helper.find_optimizations();
+        let mut storage_deads_to_insert = vec![];
+        let mut storage_deads_to_remove: Vec<(usize, BasicBlock)> = vec![];
+        for opt in opts {
+            trace!("SUCCESS: Applying {:?}", opt);
+            // replace terminator with a switchInt that switches on the integer directly
+            let bbs = &mut body.basic_blocks_mut();
+            let bb = &mut bbs[opt.bb_idx];
+            // We only use the bits for the untyped, not length checked `values` field. Thus we are
+            // not using any of the convenience wrappers here and directly access the bits.
+            let new_value = match opt.branch_value_scalar {
+                Scalar::Raw { data, .. } => data,
+                Scalar::Ptr(_) => continue,
+            };
+            const FALSE: u128 = 0;
+            let mut new_targets = opt.targets.clone();
+            let first_is_false_target = opt.values[0] == FALSE;
+            match opt.op {
+                BinOp::Eq => {
+                    // if the assignment was Eq we want the true case to be first
+                    if first_is_false_target {
+                        new_targets.swap(0, 1);
+                    }
+                }
+                BinOp::Ne => {
+                    // if the assignment was Ne we want the false case to be first
+                    if !first_is_false_target {
+                        new_targets.swap(0, 1);
+                    }
+                }
+                _ => unreachable!(),
+            }
+
+            let terminator = bb.terminator_mut();
+
+            // add StorageDead for the place switched on at the top of each target
+            for bb_idx in new_targets.iter() {
+                storage_deads_to_insert.push((
+                    *bb_idx,
+                    Statement {
+                        source_info: terminator.source_info,
+                        kind: StatementKind::StorageDead(opt.to_switch_on.local),
+                    },
+                ));
+            }
+
+            terminator.kind = TerminatorKind::SwitchInt {
+                discr: Operand::Move(opt.to_switch_on),
+                switch_ty: opt.branch_value_ty,
+                values: vec![new_value].into(),
+                targets: new_targets,
+            };
+
+            // delete comparison statement if it the value being switched on was moved, which means it can not be user later on
+            if opt.can_remove_bin_op_stmt {
+                bb.statements[opt.bin_op_stmt_idx].make_nop();
+            } else {
+                // if the integer being compared to a const integral is being moved into the comparison,
+                // e.g `_2 = Eq(move _3, const 'x');`
+                // we want to avoid making a double move later on in the switchInt on _3.
+                // So to avoid `switchInt(move _3) -> ['x': bb2, otherwise: bb1];`,
+                // we convert the move in the comparison statement to a copy.
+
+                // unwrap is safe as we know this statement is an assign
+                let box (_, rhs) = bb.statements[opt.bin_op_stmt_idx].kind.as_assign_mut().unwrap();
+
+                use Operand::*;
+                match rhs {
+                    Rvalue::BinaryOp(_, ref mut left @ Move(_), Constant(_)) => {
+                        *left = Copy(opt.to_switch_on);
+                    }
+                    Rvalue::BinaryOp(_, Constant(_), ref mut right @ Move(_)) => {
+                        *right = Copy(opt.to_switch_on);
+                    }
+                    _ => (),
+                }
+            }
+
+            // remove StorageDead (if it exists) being used in the assign of the comparison
+            for (stmt_idx, stmt) in bb.statements.iter().enumerate() {
+                if !matches!(stmt.kind, StatementKind::StorageDead(local) if local == opt.to_switch_on.local)
+                {
+                    continue;
+                }
+                storage_deads_to_remove.push((stmt_idx, opt.bb_idx))
+            }
+        }
+
+        for (idx, bb_idx) in storage_deads_to_remove {
+            body.basic_blocks_mut()[bb_idx].statements[idx].make_nop();
+        }
+
+        for (idx, stmt) in storage_deads_to_insert {
+            body.basic_blocks_mut()[idx].statements.insert(0, stmt);
+        }
+    }
+}
+
+struct OptimizationFinder<'a, 'tcx> {
+    body: &'a Body<'tcx>,
+}
+
+impl<'a, 'tcx> OptimizationFinder<'a, 'tcx> {
+    fn find_optimizations(&self) -> Vec<OptimizationInfo<'tcx>> {
+        self.body
+            .basic_blocks()
+            .iter_enumerated()
+            .filter_map(|(bb_idx, bb)| {
+                // find switch
+                let (place_switched_on, values, targets, place_switched_on_moved) = match &bb
+                    .terminator()
+                    .kind
+                {
+                    rustc_middle::mir::TerminatorKind::SwitchInt {
+                        discr, values, targets, ..
+                    } => Some((discr.place()?, values, targets, discr.is_move())),
+                    _ => None,
+                }?;
+
+                // find the statement that assigns the place being switched on
+                bb.statements.iter().enumerate().rev().find_map(|(stmt_idx, stmt)| {
+                    match &stmt.kind {
+                        rustc_middle::mir::StatementKind::Assign(box (lhs, rhs))
+                            if *lhs == place_switched_on =>
+                        {
+                            match rhs {
+                                Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), left, right) => {
+                                    let (branch_value_scalar, branch_value_ty, to_switch_on) =
+                                        find_branch_value_info(left, right)?;
+
+                                    Some(OptimizationInfo {
+                                        bin_op_stmt_idx: stmt_idx,
+                                        bb_idx,
+                                        can_remove_bin_op_stmt: place_switched_on_moved,
+                                        to_switch_on,
+                                        branch_value_scalar,
+                                        branch_value_ty,
+                                        op: *op,
+                                        values: values.clone().into_owned(),
+                                        targets: targets.clone(),
+                                    })
+                                }
+                                _ => None,
+                            }
+                        }
+                        _ => None,
+                    }
+                })
+            })
+            .collect()
+    }
+}
+
+fn find_branch_value_info<'tcx>(
+    left: &Operand<'tcx>,
+    right: &Operand<'tcx>,
+) -> Option<(Scalar, Ty<'tcx>, Place<'tcx>)> {
+    // check that either left or right is a constant.
+    // if any are, we can use the other to switch on, and the constant as a value in a switch
+    use Operand::*;
+    match (left, right) {
+        (Constant(branch_value), Copy(to_switch_on) | Move(to_switch_on))
+        | (Copy(to_switch_on) | Move(to_switch_on), Constant(branch_value)) => {
+            let branch_value_ty = branch_value.literal.ty;
+            // we only want to apply this optimization if we are matching on integrals (and chars), as it is not possible to switch on floats
+            if !branch_value_ty.is_integral() && !branch_value_ty.is_char() {
+                return None;
+            };
+            let branch_value_scalar = branch_value.literal.val.try_to_scalar()?;
+            Some((branch_value_scalar, branch_value_ty, *to_switch_on))
+        }
+        _ => None,
+    }
+}
+
+#[derive(Debug)]
+struct OptimizationInfo<'tcx> {
+    /// Basic block to apply the optimization
+    bb_idx: BasicBlock,
+    /// Statement index of Eq/Ne assignment that can be removed. None if the assignment can not be removed - i.e the statement is used later on
+    bin_op_stmt_idx: usize,
+    /// Can remove Eq/Ne assignment
+    can_remove_bin_op_stmt: bool,
+    /// Place that needs to be switched on. This place is of type integral
+    to_switch_on: Place<'tcx>,
+    /// Constant to use in switch target value
+    branch_value_scalar: Scalar,
+    /// Type of the constant value
+    branch_value_ty: Ty<'tcx>,
+    /// Either Eq or Ne
+    op: BinOp,
+    /// Current values used in the switch target. This needs to be replaced with the branch_value
+    values: Vec<u128>,
+    /// Current targets used in the switch
+    targets: Vec<BasicBlock>,
+}
index 1a22eee3a0371379823c70db890160590a973d5b..130409b9df5c00c2c14a39be32ba975b6f3cf8dd 100644 (file)
@@ -3,6 +3,7 @@
 use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_target::abi::VariantIdx;
 
+use std::convert::TryFrom;
 use std::iter::TrustedLen;
 
 /// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields.
@@ -52,14 +53,11 @@ pub fn expand_aggregate<'tcx>(
         .enumerate()
         .map(move |(i, (op, ty))| {
             let lhs_field = if let AggregateKind::Array(_) = kind {
-                // FIXME(eddyb) `offset` should be u64.
-                let offset = i as u32;
-                assert_eq!(offset as usize, i);
+                let offset = u64::try_from(i).unwrap();
                 tcx.mk_place_elem(
                     lhs,
                     ProjectionElem::ConstantIndex {
                         offset,
-                        // FIXME(eddyb) `min_length` doesn't appear to be used.
                         min_length: offset + 1,
                         from_end: false,
                     },
index 20c2f5688eb591ef64e840550b53d792a1be8047..642935d243d0d927dffd9575f84fb0a4b6e404b9 100644 (file)
@@ -1,6 +1,6 @@
 use crate::util::patch::MirPatch;
 use rustc_hir as hir;
-use rustc_hir::lang_items::{BoxFreeFnLangItem, DropTraitLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_index::vec::Idx;
 use rustc_middle::mir::*;
 use rustc_middle::traits::Reveal;
@@ -10,8 +10,6 @@
 use rustc_target::abi::VariantIdx;
 use std::fmt;
 
-use std::convert::TryInto;
-
 /// The value of an inserted drop flag.
 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
 pub enum DropFlagState {
@@ -150,7 +148,7 @@ pub trait DropElaborator<'a, 'tcx>: fmt::Debug {
     /// If this returns `None`, elements of `path` will not get a dedicated drop flag.
     ///
     /// This is only relevant for array patterns, which can move out of individual array elements.
-    fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option<Self::Path>;
+    fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option<Self::Path>;
 }
 
 #[derive(Debug)]
@@ -613,7 +611,7 @@ fn adt_switch_block(
     fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock {
         debug!("destructor_call_block({:?}, {:?})", self, succ);
         let tcx = self.tcx();
-        let drop_trait = tcx.require_lang_item(DropTraitLangItem, None);
+        let drop_trait = tcx.require_lang_item(LangItem::Drop, None);
         let drop_fn = tcx.associated_items(drop_trait).in_definition_order().next().unwrap();
         let ty = self.place_ty(self.place);
         let substs = tcx.mk_substs_trait(ty, &[]);
@@ -744,9 +742,6 @@ fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option<u64>) -> Basic
         let tcx = self.tcx();
 
         if let Some(size) = opt_size {
-            let size: u32 = size.try_into().unwrap_or_else(|_| {
-                bug!("move out check isn't implemented for array sizes bigger than u32::MAX");
-            });
             let fields: Vec<(Place<'tcx>, Option<D::Path>)> = (0..size)
                 .map(|i| {
                     (
@@ -971,7 +966,7 @@ fn unelaborated_free_block(
     ) -> BasicBlock {
         let tcx = self.tcx();
         let unit_temp = Place::from(self.new_temp(tcx.mk_unit()));
-        let free_func = tcx.require_lang_item(BoxFreeFnLangItem, Some(self.source_info.span));
+        let free_func = tcx.require_lang_item(LangItem::BoxFree, Some(self.source_info.span));
         let args = adt.variants[VariantIdx::new(0)]
             .fields
             .iter()
index 5f87cb364b891de29159e869aed9739529ee79d7..3a525d10b08175cda5f8976e181f073ba7b64e78 100644 (file)
@@ -609,8 +609,8 @@ pub(super) fn visit_primary_bindings(
 
             PatKind::Array { ref prefix, ref slice, ref suffix }
             | PatKind::Slice { ref prefix, ref slice, ref suffix } => {
-                let from = u32::try_from(prefix.len()).unwrap();
-                let to = u32::try_from(suffix.len()).unwrap();
+                let from = u64::try_from(prefix.len()).unwrap();
+                let to = u64::try_from(suffix.len()).unwrap();
                 for subpattern in prefix {
                     self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f);
                 }
index 87977d6fe8904e84a7996150c9ec992c82960e92..c4a87a554a3d0a41b357f1c06f360576b036b3e5 100644 (file)
@@ -10,7 +10,7 @@
 use crate::thir::pattern::compare_const_vals;
 use crate::thir::*;
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_hir::RangeEnd;
+use rustc_hir::{LangItem, RangeEnd};
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::*;
 use rustc_middle::ty::util::IntTypeExt;
@@ -359,8 +359,6 @@ fn non_scalar_compare(
         place: Place<'tcx>,
         mut ty: Ty<'tcx>,
     ) {
-        use rustc_hir::lang_items::EqTraitLangItem;
-
         let mut expect = self.literal_operand(source_info.span, value);
         let mut val = Operand::Copy(place);
 
@@ -414,7 +412,7 @@ fn non_scalar_compare(
             _ => bug!("non_scalar_compare called on non-reference type: {}", ty),
         };
 
-        let eq_def_id = self.hir.tcx().require_lang_item(EqTraitLangItem, None);
+        let eq_def_id = self.hir.tcx().require_lang_item(LangItem::PartialEq, None);
         let method = self.hir.trait_method(eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]);
 
         let bool_ty = self.hir.bool_ty();
@@ -537,10 +535,7 @@ pub(super) fn sort_candidate<'pat>(
                 Some(index)
             }
 
-            (
-                &TestKind::SwitchInt { switch_ty: _, ref options },
-                &PatKind::Range(range),
-            ) => {
+            (&TestKind::SwitchInt { switch_ty: _, ref options }, &PatKind::Range(range)) => {
                 let not_contained =
                     self.values_not_contained_in_range(range, options).unwrap_or(false);
 
index 605396c5eb639f722003f4e5b12528e7bde7bd16..c6d39947f7d153842790ca06a9786665c51e1363 100644 (file)
@@ -40,17 +40,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
             let elem =
-                ProjectionElem::ConstantIndex { offset: idx as u32, min_length, from_end: false };
+                ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
             let place = tcx.mk_place_elem(*place, elem);
             MatchPair::new(place, subpattern)
         }));
 
         if let Some(subslice_pat) = opt_slice {
-            let suffix_len = suffix.len() as u32;
+            let suffix_len = suffix.len() as u64;
             let subslice = tcx.mk_place_elem(
                 *place,
                 ProjectionElem::Subslice {
-                    from: prefix.len() as u32,
+                    from: prefix.len() as u64,
                     to: if exact_size { min_length - suffix_len } else { suffix_len },
                     from_end: !exact_size,
                 },
@@ -59,7 +59,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
 
         match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
-            let end_offset = (idx + 1) as u32;
+            let end_offset = (idx + 1) as u64;
             let elem = ProjectionElem::ConstantIndex {
                 offset: if exact_size { min_length - end_offset } else { end_offset },
                 min_length,
index d3c1aa50400e49ed2b33cd889e5e7668c5a7cd36..249cce0ba19942809c580e6a5d9db10f05c4e907 100644 (file)
@@ -6,7 +6,7 @@
 use rustc_errors::ErrorReported;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{GeneratorKind, HirIdMap, Node};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -145,8 +145,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
                 // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
                 // (as it's created inside the body itself, not passed in from outside).
                 let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() {
-                    let va_list_did =
-                        tcx.require_lang_item(lang_items::VaListTypeLangItem, Some(arg.span));
+                    let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(arg.span));
 
                     tcx.type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()])
                 } else {
@@ -877,7 +876,7 @@ fn args_and_body(
                     let mut projs = closure_env_projs.clone();
                     projs.push(ProjectionElem::Field(Field::new(i), ty));
                     match capture {
-                        ty::UpvarCapture::ByValue => {}
+                        ty::UpvarCapture::ByValue(_) => {}
                         ty::UpvarCapture::ByRef(..) => {
                             projs.push(ProjectionElem::Deref);
                         }
index c51c3bcf5628845808801358cf677a915c416336..066e46fec14567f40a049a4ea65f7bf515e94d96 100644 (file)
@@ -959,7 +959,7 @@ fn convert_var<'tcx>(
             // ...but the upvar might be an `&T` or `&mut T` capture, at which
             // point we need an implicit deref
             match cx.typeck_results().upvar_capture(upvar_id) {
-                ty::UpvarCapture::ByValue => field_kind,
+                ty::UpvarCapture::ByValue(_) => field_kind,
                 ty::UpvarCapture::ByRef(borrow) => ExprKind::Deref {
                     arg: Expr {
                         temp_lifetime,
@@ -1074,7 +1074,7 @@ fn capture_upvar<'tcx>(
         kind: convert_var(cx, closure_expr, var_hir_id),
     };
     match upvar_capture {
-        ty::UpvarCapture::ByValue => captured_var.to_ref(),
+        ty::UpvarCapture::ByValue(_) => captured_var.to_ref(),
         ty::UpvarCapture::ByRef(upvar_borrow) => {
             let borrow_kind = match upvar_borrow.kind {
                 ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
index 6dd7e0871b45ef8bb9b319dd805fa873d3fdb951..f6d3ccc1ae0a68fe98c86f803eecc1278e39a242 100644 (file)
@@ -1,5 +1,4 @@
 use rustc_hir as hir;
-use rustc_hir::lang_items::EqTraitLangItem;
 use rustc_index::vec::Idx;
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_middle::mir::Field;
@@ -164,7 +163,7 @@ fn to_pat(
                 // not *yet* implement `PartialEq`. So for now we leave this here.
                 let ty_is_partial_eq: bool = {
                     let partial_eq_trait_id =
-                        self.tcx().require_lang_item(EqTraitLangItem, Some(self.span));
+                        self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
                     let obligation: PredicateObligation<'_> = predicate_for_trait_def(
                         self.tcx(),
                         self.param_env,
index ebb3aa3866e43641faae1f0f2e0529339b001bb5..e07b8b86aef8e45cb84879be4841a8b13f04bc2b 100644 (file)
@@ -760,7 +760,7 @@ fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
                 (' ' | '\n' | '\t', _) if eat_ws => {
                     skips.push(pos);
                 }
-                ('\\', Some((next_pos, 'n' | 't' | '0' | '\\' | '\'' | '\"'))) => {
+                ('\\', Some((next_pos, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => {
                     skips.push(*next_pos);
                     let _ = s.next();
                 }
index de21f0b5e09b3ba4e586ead2b23c486d45a57cad..55525586479d91b5003305a06b0e44173b2f11fb 100644 (file)
@@ -945,7 +945,7 @@ fn compute(
                         let var = self.variable(var_hir_id, upvar.span);
                         self.acc(self.s.exit_ln, var, ACC_READ | ACC_USE);
                     }
-                    ty::UpvarCapture::ByValue => {}
+                    ty::UpvarCapture::ByValue(_) => {}
                 }
             }
         }
@@ -1610,7 +1610,7 @@ fn warn_about_unused_upvars(&self, entry_ln: LiveNode) {
                 closure_expr_id: self.ir.body_owner,
             };
             match self.typeck_results.upvar_capture(upvar_id) {
-                ty::UpvarCapture::ByValue => {}
+                ty::UpvarCapture::ByValue(_) => {}
                 ty::UpvarCapture::ByRef(..) => continue,
             };
             if self.used_on_entry(entry_ln, var) {
index d5ce82ae28f28d6a71179f40b2e556f9fed71369..6bc2110bfb3e6df08167fdd22fafa6b0f2cead41 100644 (file)
@@ -4,7 +4,7 @@
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::{self, LangItem};
 use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS;
 use rustc_middle::middle::lang_items::required;
 use rustc_middle::ty::TyCtxt;
@@ -24,7 +24,10 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem
     // They will never implicitly be added to the `missing` array unless we do
     // so here.
     if items.eh_personality().is_none() {
-        items.missing.push(lang_items::EhPersonalityLangItem);
+        items.missing.push(LangItem::EhPersonality);
+    }
+    if tcx.sess.target.target.options.is_like_emscripten && items.eh_catch_typeinfo().is_none() {
+        items.missing.push(LangItem::EhCatchTypeinfo);
     }
 
     {
@@ -58,9 +61,9 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) {
 
     for (name, &item) in WEAK_ITEMS_REFS.iter() {
         if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() {
-            if item == lang_items::PanicImplLangItem {
+            if item == LangItem::PanicImpl {
                 tcx.sess.err("`#[panic_handler]` function required, but not found");
-            } else if item == lang_items::OomLangItem {
+            } else if item == LangItem::Oom {
                 tcx.sess.err("`#[alloc_error_handler]` function required, but not found");
             } else {
                 tcx.sess.err(&format!("language item required, but not found: `{}`", name));
@@ -91,7 +94,7 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 
     fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) {
         let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
-        if let Some((lang_item, _)) = hir::lang_items::extract(check_name, &i.attrs) {
+        if let Some((lang_item, _)) = lang_items::extract(check_name, &i.attrs) {
             self.register(lang_item, i.span);
         }
         intravisit::walk_foreign_item(self, i)
index 80164840334a23ba3de4724190d8439ccd0b922e..d05f1a3f34b78337d924ba78ca9e22880331517e 100644 (file)
@@ -807,6 +807,8 @@ fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool {
         "select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"),
     borrowck_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather borrowck statistics (default: no)"),
+    cgu_partitioning_strategy: Option<String> = (None, parse_opt_string, [TRACKED],
+        "the codegen unit partitioning strategy to use"),
     chalk: bool = (false, parse_bool, [TRACKED],
         "enable the experimental Chalk-based trait solving engine"),
     codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
index 46612145bf02cb2b77702dfcc4eb58bcece6c497..5092b945f72c4cb53a21ac635029282a6ee9c82f 100644 (file)
         rust_2015_preview,
         rust_2018_preview,
         rust_begin_unwind,
+        rust_eh_catch_typeinfo,
         rust_eh_personality,
         rust_eh_register_frames,
         rust_eh_unregister_frames,
index 5f978c03248b2ab85337255e0d0ad95a2f451621..e07b8f7a75655a8493f5bd995a8bc20647341a7a 100644 (file)
@@ -1,10 +1,8 @@
-use crate::spec::{LinkerFlavor, Target, TargetResult};
+use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult};
 
 pub fn target() -> TargetResult {
     let mut base = super::hermit_base::opts();
     base.max_atomic_width = Some(128);
-    base.unsupported_abis = super::arm_base::unsupported_abis();
-    base.linker = Some("aarch64-hermit-gcc".to_string());
 
     Ok(Target {
         llvm_target: "aarch64-unknown-hermit".to_string(),
@@ -16,7 +14,7 @@ pub fn target() -> TargetResult {
         target_os: "hermit".to_string(),
         target_env: String::new(),
         target_vendor: "unknown".to_string(),
-        linker_flavor: LinkerFlavor::Gcc,
+        linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         options: base,
     })
 }
diff --git a/src/librustc_target/spec/avr_gnu_base.rs b/src/librustc_target/spec/avr_gnu_base.rs
new file mode 100644 (file)
index 0000000..ff559c2
--- /dev/null
@@ -0,0 +1,51 @@
+use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
+
+/// A base target for AVR devices using the GNU toolchain.
+///
+/// Requires GNU avr-gcc and avr-binutils on the host system.
+pub fn target(target_cpu: String) -> TargetResult {
+    Ok(Target {
+        arch: "avr".to_string(),
+        data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".to_string(),
+        llvm_target: "avr-unknown-unknown".to_string(),
+        target_endian: "little".to_string(),
+        target_pointer_width: "16".to_string(),
+        linker_flavor: LinkerFlavor::Gcc,
+        target_os: "unknown".to_string(),
+        target_env: "".to_string(),
+        target_vendor: "unknown".to_string(),
+        target_c_int_width: 16.to_string(),
+        options: TargetOptions {
+            cpu: target_cpu.clone(),
+            exe_suffix: ".elf".to_string(),
+
+            linker: Some("avr-gcc".to_owned()),
+            dynamic_linking: false,
+            executables: true,
+            linker_is_gnu: true,
+            has_rpath: false,
+            position_independent_executables: false,
+            eh_frame_header: false,
+            pre_link_args: vec![(
+                LinkerFlavor::Gcc,
+                vec![
+                    format!("-mmcu={}", target_cpu),
+                    // We want to be able to strip as much executable code as possible
+                    // from the linker command line, and this flag indicates to the
+                    // linker that it can avoid linking in dynamic libraries that don't
+                    // actually satisfy any symbols up to that point (as with many other
+                    // resolutions the linker does). This option only applies to all
+                    // following libraries so we're sure to pass it as one of the first
+                    // arguments.
+                    "-Wl,--as-needed".to_string(),
+                ],
+            )]
+            .into_iter()
+            .collect(),
+            late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])]
+                .into_iter()
+                .collect(),
+            ..TargetOptions::default()
+        },
+    })
+}
diff --git a/src/librustc_target/spec/avr_unknown_gnu_atmega328.rs b/src/librustc_target/spec/avr_unknown_gnu_atmega328.rs
new file mode 100644 (file)
index 0000000..5d22598
--- /dev/null
@@ -0,0 +1,5 @@
+use crate::spec::TargetResult;
+
+pub fn target() -> TargetResult {
+    super::avr_gnu_base::target("atmega328".to_owned())
+}
diff --git a/src/librustc_target/spec/avr_unknown_unknown.rs b/src/librustc_target/spec/avr_unknown_unknown.rs
deleted file mode 100644 (file)
index f90a8de..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-use crate::spec::{LinkerFlavor, Target, TargetResult};
-
-pub fn target() -> TargetResult {
-    Ok(Target {
-        llvm_target: "avr-unknown-unknown".to_string(),
-        target_endian: "little".to_string(),
-        target_pointer_width: "16".to_string(),
-        data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".to_string(),
-        arch: "avr".to_string(),
-        linker_flavor: LinkerFlavor::Gcc,
-        target_os: "unknown".to_string(),
-        target_env: "".to_string(),
-        target_vendor: "unknown".to_string(),
-        target_c_int_width: 16.to_string(),
-        options: super::freestanding_base::opts(),
-    })
-}
diff --git a/src/librustc_target/spec/freestanding_base.rs b/src/librustc_target/spec/freestanding_base.rs
deleted file mode 100644 (file)
index c338856..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};
-use std::default::Default;
-
-pub fn opts() -> TargetOptions {
-    let mut args = LinkArgs::new();
-
-    args.insert(
-        LinkerFlavor::Gcc,
-        vec![
-            // We want to be able to strip as much executable code as possible
-            // from the linker command line, and this flag indicates to the
-            // linker that it can avoid linking in dynamic libraries that don't
-            // actually satisfy any symbols up to that point (as with many other
-            // resolutions the linker does). This option only applies to all
-            // following libraries so we're sure to pass it as one of the first
-            // arguments.
-            "-Wl,--as-needed".to_string(),
-        ],
-    );
-
-    TargetOptions {
-        dynamic_linking: false,
-        executables: true,
-        linker_is_gnu: true,
-        has_rpath: false,
-        pre_link_args: args,
-        position_independent_executables: false,
-        eh_frame_header: false,
-        ..Default::default()
-    }
-}
index 18fb2aa3d5693f00ab0afbde139a8fb1ec5d8d84..e063c94cf2ca6b16281bdb96504b4184ec5ec11b 100644 (file)
@@ -16,7 +16,8 @@ pub fn opts() -> TargetOptions {
         pre_link_args,
         panic_strategy: PanicStrategy::Abort,
         position_independent_executables: true,
-        relocation_model: RelocModel::Static,
+        static_position_independent_executables: true,
+        relocation_model: RelocModel::Pic,
         target_family: None,
         tls_model: TlsModel::InitialExec,
         ..Default::default()
index 7f2dada714d8f721145d3968dc3ce4924a0befa5..01b9f75637fc4020a78bf797388d3f3a2137a140 100644 (file)
@@ -17,7 +17,8 @@ pub fn opts() -> TargetOptions {
         pre_link_args,
         panic_strategy: PanicStrategy::Abort,
         position_independent_executables: true,
-        relocation_model: RelocModel::Static,
+        static_position_independent_executables: true,
+        relocation_model: RelocModel::Pic,
         target_family: None,
         tls_model: TlsModel::InitialExec,
         ..Default::default()
index fa29ff3f8d80f6df5fd1d97ca1602ff97e37a62d..d6e8b304380ca51a383dc927839164bedb20239f 100644 (file)
 mod apple_base;
 mod apple_sdk_base;
 mod arm_base;
+mod avr_gnu_base;
 mod cloudabi_base;
 mod dragonfly_base;
 mod freebsd_base;
-mod freestanding_base;
 mod fuchsia_base;
 mod haiku_base;
 mod hermit_base;
@@ -581,7 +581,7 @@ fn $module() {
     ("aarch64-fuchsia", aarch64_fuchsia),
     ("x86_64-fuchsia", x86_64_fuchsia),
 
-    ("avr-unknown-unknown", avr_unknown_unknown),
+    ("avr-unknown-gnu-atmega328", avr_unknown_gnu_atmega328),
 
     ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc),
 
index dc895ad34a93205017fdd623c22e083b606e9dfa..4ec1b29bca4f1286fa01a53accd905e61a616f3c 100644 (file)
@@ -2,7 +2,7 @@
 use crate::traits::{self, TraitEngine, TraitEngineExt};
 
 use rustc_hir as hir;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::arena::ArenaAllocatable;
@@ -47,7 +47,7 @@ fn type_is_copy_modulo_regions(
             return ty.is_copy_modulo_regions(self.tcx.at(span), param_env);
         }
 
-        let copy_def_id = self.tcx.require_lang_item(lang_items::CopyTraitLangItem, None);
+        let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);
 
         // This can get called from typeck (by euv), and `moves_by_default`
         // rightly refuses to work with inference variables, but
index 7513ff6b37ef82087eceef0c02c42075f0182cfa..138293c95331f911adcac7712c4467059d038a36 100644 (file)
@@ -13,7 +13,7 @@
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
 use rustc_middle::ty::{
     self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty,
@@ -2015,8 +2015,7 @@ fn suggest_await_before_try(
         if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) {
             let body = self.tcx.hir().body(body_id);
             if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
-                let future_trait =
-                    self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None);
+                let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
 
                 let self_ty = self.resolve_vars_if_possible(&trait_ref.self_ty());
 
index a505d1b594c6310a633d1a032c3171f64fc6fd2f..c788e4f5c90b38ff88dddeb24ff731d3e7113999 100644 (file)
@@ -23,9 +23,7 @@
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorReported;
 use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::{
-    DiscriminantTypeLangItem, FnOnceOutputLangItem, FnOnceTraitLangItem, GeneratorTraitLangItem,
-};
+use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
 use rustc_middle::ty::subst::Subst;
@@ -1300,7 +1298,7 @@ fn confirm_generator_candidate<'cx, 'tcx>(
 
     let tcx = selcx.tcx();
 
-    let gen_def_id = tcx.require_lang_item(GeneratorTraitLangItem, None);
+    let gen_def_id = tcx.require_lang_item(LangItem::Generator, None);
 
     let predicate = super::util::generator_trait_ref_and_outputs(
         tcx,
@@ -1342,7 +1340,7 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>(
     let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
     let substs = tcx.mk_substs([self_ty.into()].iter());
 
-    let discriminant_def_id = tcx.require_lang_item(DiscriminantTypeLangItem, None);
+    let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None);
 
     let predicate = ty::ProjectionPredicate {
         projection_ty: ty::ProjectionTy { substs, item_def_id: discriminant_def_id },
@@ -1406,8 +1404,8 @@ fn confirm_callable_candidate<'cx, 'tcx>(
 
     debug!("confirm_callable_candidate({:?},{:?})", obligation, fn_sig);
 
-    let fn_once_def_id = tcx.require_lang_item(FnOnceTraitLangItem, None);
-    let fn_once_output_def_id = tcx.require_lang_item(FnOnceOutputLangItem, None);
+    let fn_once_def_id = tcx.require_lang_item(LangItem::FnOnce, None);
+    let fn_once_output_def_id = tcx.require_lang_item(LangItem::FnOnceOutput, None);
 
     let predicate = super::util::closure_trait_ref_and_return_type(
         tcx,
index a04636af5796a0d96b5ce4b036119344c344d6bd..3d6eb845136361427f6eda4a902c638ea5750fb2 100644 (file)
@@ -7,7 +7,7 @@
 //! [rustc dev guide]:
 //! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_infer::infer::InferOk;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
@@ -669,7 +669,7 @@ fn confirm_builtin_unsize_candidate(
 
                 // We can only make objects from sized types.
                 let tr = ty::TraitRef::new(
-                    tcx.require_lang_item(lang_items::SizedTraitLangItem, None),
+                    tcx.require_lang_item(LangItem::Sized, None),
                     tcx.mk_substs_trait(source, &[]),
                 );
                 nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx)));
index 377d163d1043913cc4e92035bc8a2901563db11c..78186a5e8a5813003c21a2c775e03ffdb4d5a730 100644 (file)
@@ -4,7 +4,7 @@
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
-use rustc_hir::lang_items::{StructuralPeqTraitLangItem, StructuralTeqTraitLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor};
 use rustc_span::Span;
@@ -75,7 +75,7 @@ fn type_marked_structural(
     let mut fulfillment_cx = traits::FulfillmentContext::new();
     // require `#[derive(PartialEq)]`
     let structural_peq_def_id =
-        infcx.tcx.require_lang_item(StructuralPeqTraitLangItem, Some(cause.span));
+        infcx.tcx.require_lang_item(LangItem::StructuralPeq, Some(cause.span));
     fulfillment_cx.register_bound(
         infcx,
         ty::ParamEnv::empty(),
@@ -86,7 +86,7 @@ fn type_marked_structural(
     // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around
     // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.)
     let structural_teq_def_id =
-        infcx.tcx.require_lang_item(StructuralTeqTraitLangItem, Some(cause.span));
+        infcx.tcx.require_lang_item(LangItem::StructuralTeq, Some(cause.span));
     fulfillment_cx.register_bound(
         infcx,
         ty::ParamEnv::empty(),
index d225b10834a6b33e3744690733743c5cc385c78f..0ac3c6ffe62c89e010a52a45671b348ab496e24d 100644 (file)
@@ -3,7 +3,7 @@
 use crate::traits;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
 use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
 use rustc_span::Span;
@@ -340,7 +340,7 @@ fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<
         if !subty.has_escaping_bound_vars() {
             let cause = self.cause(cause);
             let trait_ref = ty::TraitRef {
-                def_id: self.infcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None),
+                def_id: self.infcx.tcx.require_lang_item(LangItem::Sized, None),
                 substs: self.infcx.tcx.mk_substs_trait(subty, &[]),
             };
             self.out.push(traits::Obligation::new(
index 8d153e77f0b7d402e6791aeb48822bdd5703ae33..24ba071786607fc4daa5aa03e0661e8be2ab22c7 100644 (file)
@@ -1,27 +1,27 @@
 //! Queries for checking whether a type implements one of a few common traits.
 
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::DUMMY_SP;
 use rustc_trait_selection::traits;
 
 fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    is_item_raw(tcx, query, lang_items::CopyTraitLangItem)
+    is_item_raw(tcx, query, LangItem::Copy)
 }
 
 fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    is_item_raw(tcx, query, lang_items::SizedTraitLangItem)
+    is_item_raw(tcx, query, LangItem::Sized)
 }
 
 fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    is_item_raw(tcx, query, lang_items::FreezeTraitLangItem)
+    is_item_raw(tcx, query, LangItem::Freeze)
 }
 
 fn is_item_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
     query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
-    item: lang_items::LangItem,
+    item: LangItem,
 ) -> bool {
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(item, None);
index 15c19afdb4a207e2a8765d653a930aa3120fd81a..80dd26e9154b37c26166e0c016196639c8c621cf 100644 (file)
@@ -16,7 +16,7 @@
 use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{walk_generics, Visitor as _};
-use rustc_hir::lang_items::SizedTraitLangItem;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{Constness, GenericArg, GenericArgs};
 use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef};
 use rustc_middle::ty::GenericParamDefKind;
@@ -696,7 +696,7 @@ pub fn is_unsized(&self, ast_bounds: &[hir::GenericBound<'_>], span: Span) -> bo
             }
         }
 
-        let kind_id = tcx.lang_items().require(SizedTraitLangItem);
+        let kind_id = tcx.lang_items().require(LangItem::Sized);
         match unbound {
             Some(tpb) => {
                 // FIXME(#8559) currently requires the unbound to be built-in.
index 40088bc06900543a885943d1ff4e1c197da07eac..afd4413069ee10df5879f1eae2fb6aaba4198ab8 100644 (file)
@@ -28,7 +28,7 @@ pub fn check_match(
         };
 
         // Type check the descriminant and get its type.
-        let scrut_ty = if force_scrutinee_bool {
+        let scrutinee_ty = if force_scrutinee_bool {
             // Here we want to ensure:
             //
             // 1. That default match bindings are *not* accepted in the condition of an
@@ -55,7 +55,7 @@ pub fn check_match(
 
         // #55810: Type check patterns first so we get types for all bindings.
         for arm in arms {
-            self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true);
+            self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut.span), true);
         }
 
         // Now typecheck the blocks.
index 34693a73217884d9adf688fb4cdad80fa0509e8d..e41314e8ab03f438364ffe092411eb55eb41ff31 100644 (file)
@@ -35,7 +35,7 @@
 use rustc_ast as ast;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::cast::{CastKind, CastTy};
 use rustc_middle::ty::error::TypeError;
@@ -838,7 +838,7 @@ fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool {
-        let lang_item = self.tcx.require_lang_item(lang_items::SizedTraitLangItem, None);
+        let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
         traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span)
     }
 }
index c7f9e9d63e03c576a21a595dd72144437b5d5040..97f7e4537ce9584fda1bfa87d40c9cdab1917bb5 100644 (file)
@@ -5,7 +5,7 @@
 use crate::astconv::AstConv;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::{FutureTraitLangItem, GeneratorTraitLangItem};
+use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::LateBoundRegionConversionTime;
 use rustc_infer::infer::{InferOk, InferResult};
@@ -245,7 +245,7 @@ fn deduce_sig_from_projection(
         let trait_ref = projection.to_poly_trait_ref(tcx);
 
         let is_fn = tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some();
-        let gen_trait = tcx.require_lang_item(GeneratorTraitLangItem, cause_span);
+        let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span);
         let is_gen = gen_trait == trait_ref.def_id();
         if !is_fn && !is_gen {
             debug!("deduce_sig_from_projection: not fn or generator");
@@ -668,7 +668,7 @@ fn deduce_future_output_from_projection(
 
         // Check that this is a projection from the `Future` trait.
         let trait_ref = predicate.projection_ty.trait_ref(self.tcx);
-        let future_trait = self.tcx.require_lang_item(FutureTraitLangItem, Some(cause_span));
+        let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span));
         if trait_ref.def_id != future_trait {
             debug!("deduce_future_output_from_projection: not a future");
             return None;
index aa92d8b8b2b9b8d7e8f57bd57e70762c16bfbbee..5dc5480c3353c0140b3a6399d1cbfe58c8dd4e61 100644 (file)
@@ -6,7 +6,7 @@
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
-use rustc_hir::lang_items::CloneTraitLangItem;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{is_range_literal, Node};
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
@@ -465,7 +465,7 @@ pub fn check_ref(
                 if self.can_coerce(ref_ty, expected) {
                     let mut sugg_sp = sp;
                     if let hir::ExprKind::MethodCall(ref segment, sp, ref args, _) = expr.kind {
-                        let clone_trait = self.tcx.require_lang_item(CloneTraitLangItem, Some(sp));
+                        let clone_trait = self.tcx.require_lang_item(LangItem::Clone, Some(sp));
                         if let ([arg], Some(true), sym::clone) = (
                             &args[..],
                             self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
index ff0c788c18b51598cdce7442c08927ce2618ea65..1573fb967910b3ad33bba5c4b88dfeeff471898c 100644 (file)
@@ -25,7 +25,7 @@
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, QPath};
 use rustc_infer::infer;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -37,7 +37,7 @@
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_trait_selection::traits::{self, ObligationCauseCode};
+use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
 
 use std::fmt::Display;
 
@@ -926,8 +926,8 @@ fn report_extended_method_error(
                 // Try alternative arbitrary self types that could fulfill this call.
                 // FIXME: probe for all types that *could* be arbitrary self-types, not
                 // just this list.
-                try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, lang_items::OwnedBoxLangItem));
-                try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, lang_items::PinTypeLangItem));
+                try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, LangItem::OwnedBox));
+                try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, LangItem::Pin));
                 try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Arc));
                 try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Rc));
             }
@@ -1296,7 +1296,7 @@ fn report_unknown_field(
         kind_name: &str,
         ty_span: Span,
     ) {
-        if variant.recovered {
+        if variant.is_recovered() {
             self.set_tainted_by_errors();
             return;
         }
@@ -1509,6 +1509,58 @@ fn check_field(
         self.tcx().ty_error()
     }
 
+    fn suggest_await_on_field_access(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        field_ident: Ident,
+        base: &'tcx hir::Expr<'tcx>,
+        expr: &'tcx hir::Expr<'tcx>,
+        def_id: DefId,
+    ) {
+        let param_env = self.tcx().param_env(def_id);
+        let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+        // Future::Output
+        let item_def_id =
+            self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id;
+
+        let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
+        debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty);
+
+        let cause = self.misc(expr.span);
+        let mut selcx = SelectionContext::new(&self.infcx);
+
+        let mut obligations = vec![];
+        if let Some(projection_ty) = projection_ty {
+            let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
+                &mut selcx,
+                param_env,
+                projection_ty,
+                cause,
+                0,
+                &mut obligations,
+            );
+            debug!(
+                "suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}",
+                self.resolve_vars_if_possible(&normalized_ty),
+                normalized_ty.kind,
+            );
+            if let ty::Adt(def, _) = normalized_ty.kind {
+                // no field access on enum type
+                if !def.is_enum() {
+                    if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident)
+                    {
+                        err.span_suggestion_verbose(
+                            base.span.shrink_to_hi(),
+                            "consider awaiting before field access",
+                            ".await".to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
     fn ban_nonexisting_field(
         &self,
         field: Ident,
@@ -1516,6 +1568,10 @@ fn ban_nonexisting_field(
         expr: &'tcx hir::Expr<'tcx>,
         expr_t: Ty<'tcx>,
     ) {
+        debug!(
+            "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
+            field, base, expr, expr_t
+        );
         let mut err = self.no_such_field_err(field.span, field, expr_t);
 
         match expr_t.peel_refs().kind {
@@ -1531,6 +1587,9 @@ fn ban_nonexisting_field(
             ty::Param(param_ty) => {
                 self.point_at_param_definition(&mut err, param_ty);
             }
+            ty::Opaque(def_id, _) => {
+                self.suggest_await_on_field_access(&mut err, field, base, expr, def_id);
+            }
             _ => {}
         }
 
index e941c844a6d9058113a018e0d2fa6ab561275399..5cae66bc5dac6e3635d2eebc4f8039f5bc20cd1b 100644 (file)
@@ -9,7 +9,7 @@
 use rustc_hir::def::{DefKind, Namespace, Res};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_hir::intravisit;
-use rustc_hir::lang_items::FnOnceTraitLangItem;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, Node, QPath};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::hir::map as hir_map;
@@ -21,6 +21,7 @@
 use rustc_span::{source_map, FileName, Span};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::Obligation;
+use rustc_trait_selection::traits::SelectionContext;
 
 use std::cmp::Ordering;
 
@@ -36,7 +37,7 @@ fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
             ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true,
             // If it's not a simple function, look for things which implement `FnOnce`.
             _ => {
-                let fn_once = match tcx.lang_items().require(FnOnceTraitLangItem) {
+                let fn_once = match tcx.lang_items().require(LangItem::FnOnce) {
                     Ok(fn_once) => fn_once,
                     Err(..) => return false,
                 };
@@ -392,6 +393,13 @@ pub fn report_method_error<'b>(
                             actual.prefix_string(),
                             ty_str,
                         );
+                        if let Mode::MethodCall = mode {
+                            if let SelfSource::MethodCall(call) = source {
+                                self.suggest_await_before_method(
+                                    &mut err, item_name, actual, call, span,
+                                );
+                            }
+                        }
                         if let Some(span) =
                             tcx.sess.confused_type_with_std_module.borrow().get(&span)
                         {
@@ -854,6 +862,57 @@ fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
         }
     }
 
+    fn suggest_await_before_method(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        item_name: Ident,
+        ty: Ty<'tcx>,
+        call: &hir::Expr<'_>,
+        span: Span,
+    ) {
+        if let ty::Opaque(def_id, _) = ty.kind {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+            // Future::Output
+            let item_def_id = self
+                .tcx
+                .associated_items(future_trait)
+                .in_definition_order()
+                .next()
+                .unwrap()
+                .def_id;
+
+            let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
+            let cause = self.misc(span);
+            let mut selcx = SelectionContext::new(&self.infcx);
+            let mut obligations = vec![];
+            if let Some(projection_ty) = projection_ty {
+                let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
+                    &mut selcx,
+                    self.param_env,
+                    projection_ty,
+                    cause,
+                    0,
+                    &mut obligations,
+                );
+                debug!(
+                    "suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}",
+                    self.resolve_vars_if_possible(&normalized_ty),
+                    normalized_ty.kind,
+                );
+                let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true);
+                debug!("suggest_await_before_method: is_method_exist={}", method_exists);
+                if method_exists {
+                    err.span_suggestion_verbose(
+                        span.shrink_to_lo(),
+                        "consider awaiting before this method call",
+                        "await.".to_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+    }
+
     fn suggest_use_candidates(
         &self,
         err: &mut DiagnosticBuilder<'_>,
index 824e81a974ca61f00b8dd7c6bf1d08a4fad52448..031d48f8a6086f97f10334a2ced7fd121469c056 100644 (file)
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_hir::lang_items::{
-    FutureTraitLangItem, PinTypeLangItem, SizedTraitLangItem, VaListTypeLangItem,
-};
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, GenericArg, HirIdMap, ItemKind, Node, PatKind, QPath};
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::Idx;
@@ -1339,7 +1337,7 @@ fn check_fn<'a, 'tcx>(
     // (as it's created inside the body itself, not passed in from outside).
     let maybe_va_list = if fn_sig.c_variadic {
         let span = body.params.last().unwrap().span;
-        let va_list_did = tcx.require_lang_item(VaListTypeLangItem, Some(span));
+        let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span));
         let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span));
 
         Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()]))
@@ -3493,7 +3491,7 @@ pub fn require_type_is_sized(
         code: traits::ObligationCauseCode<'tcx>,
     ) {
         if !ty.references_error() {
-            let lang_item = self.tcx.require_lang_item(SizedTraitLangItem, None);
+            let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
             self.require_type_meets(ty, span, code, lang_item);
         }
     }
@@ -5265,7 +5263,7 @@ fn suggest_calling_boxed_future_when_appropriate(
             _ => {}
         }
         let boxed_found = self.tcx.mk_box(found);
-        let new_found = self.tcx.mk_lang_item(boxed_found, PinTypeLangItem).unwrap();
+        let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
         if let (true, Ok(snippet)) = (
             self.can_coerce(new_found, expected),
             self.sess().source_map().span_to_snippet(expr.span),
@@ -5422,7 +5420,7 @@ fn suggest_missing_await(
                 let sp = expr.span;
                 // Check for `Future` implementations by constructing a predicate to
                 // prove: `<T as Future>::Output == U`
-                let future_trait = self.tcx.require_lang_item(FutureTraitLangItem, Some(sp));
+                let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
                 let item_def_id = self
                     .tcx
                     .associated_items(future_trait)
index dc1ce2d89ba0eea10a3c92c451b236e75ad0a42c..d1864ee2b35afc051772e26c55949b0cca340c8c 100644 (file)
@@ -1080,7 +1080,7 @@ fn check_struct_pat_fields(
             .filter(|ident| !used_fields.contains_key(&ident))
             .collect::<Vec<_>>();
 
-        let inexistent_fields_err = if !inexistent_fields.is_empty() && !variant.recovered {
+        let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) {
             Some(self.error_inexistent_fields(
                 adt.variant_descr(),
                 &inexistent_fields,
index 221e5f72dc977b135d5a263212ecf5094d187285..484961dbdb8f23e840264c92e06d336468bc4417 100644 (file)
@@ -786,7 +786,7 @@ fn link_upvar_region(
                     return;
                 }
             }
-            ty::UpvarCapture::ByValue => {}
+            ty::UpvarCapture::ByValue(_) => {}
         }
         let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id);
         let ty = self.resolve_node_type(fn_hir_id);
index 030c0ab668a8005db6a0a7bbf374697cbfabf06d..9bb84c0786847eb8aa9db17784f6fe875d62ffc5 100644 (file)
@@ -42,6 +42,7 @@
 use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId};
 use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
 use rustc_span::{Span, Symbol};
+use std::collections::hash_map::Entry;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
@@ -124,7 +125,7 @@ fn analyze_closure(
                 closure_captures.insert(var_hir_id, upvar_id);
 
                 let capture_kind = match capture_clause {
-                    hir::CaptureBy::Value => ty::UpvarCapture::ByValue,
+                    hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None),
                     hir::CaptureBy::Ref => {
                         let origin = UpvarRegion(upvar_id, span);
                         let upvar_region = self.next_region_var(origin);
@@ -237,7 +238,7 @@ fn final_upvar_tys(&self, closure_id: hir::HirId) -> Vec<Ty<'tcx>> {
                     debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture);
 
                     match capture {
-                        ty::UpvarCapture::ByValue => upvar_ty,
+                        ty::UpvarCapture::ByValue(_) => upvar_ty,
                         ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref(
                             borrow.region,
                             ty::TypeAndMut { ty: upvar_ty, mutbl: borrow.kind.to_mutbl_lossy() },
@@ -300,15 +301,43 @@ fn adjust_upvar_borrow_kind_for_consume(
 
         debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id);
 
+        let usage_span = tcx.hir().span(place_with_id.hir_id);
+
         // To move out of an upvar, this must be a FnOnce closure
         self.adjust_closure_kind(
             upvar_id.closure_expr_id,
             ty::ClosureKind::FnOnce,
-            tcx.hir().span(place_with_id.hir_id),
+            usage_span,
             var_name(tcx, upvar_id.var_path.hir_id),
         );
 
-        self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue);
+        // In a case like `let pat = upvar`, don't use the span
+        // of the pattern, as this just looks confusing.
+        let by_value_span = match tcx.hir().get(place_with_id.hir_id) {
+            hir::Node::Pat(_) => None,
+            _ => Some(usage_span),
+        };
+
+        let new_capture = ty::UpvarCapture::ByValue(by_value_span);
+        match self.adjust_upvar_captures.entry(upvar_id) {
+            Entry::Occupied(mut e) => {
+                match e.get() {
+                    // We always overwrite `ByRef`, since we require
+                    // that the upvar be available by value.
+                    //
+                    // If we had a previous by-value usage without a specific
+                    // span, use ours instead. Otherwise, keep the first span
+                    // we encountered, since there isn't an obviously better one.
+                    ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => {
+                        e.insert(new_capture);
+                    }
+                    _ => {}
+                }
+            }
+            Entry::Vacant(e) => {
+                e.insert(new_capture);
+            }
+        }
     }
 
     /// Indicates that `place_with_id` is being directly mutated (e.g., assigned
@@ -404,7 +433,7 @@ fn adjust_upvar_borrow_kind(&mut self, upvar_id: ty::UpvarId, kind: ty::BorrowKi
         );
 
         match upvar_capture {
-            ty::UpvarCapture::ByValue => {
+            ty::UpvarCapture::ByValue(_) => {
                 // Upvar is already by-value, the strongest criteria.
             }
             ty::UpvarCapture::ByRef(mut upvar_borrow) => {
index 810bf59ea6c35ad936da1cbcdd6266520379b658..9c692edaa7fa4412f63ab57870af474933ffca1f 100644 (file)
@@ -9,7 +9,7 @@
 use rustc_hir::intravisit as hir_visit;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::itemlikevisit::ParItemLikeVisitor;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::ItemKind;
 use rustc_middle::hir::map as hir_map;
 use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
@@ -495,7 +495,7 @@ fn check_type_defn<'tcx, F>(
                 let last = idx == variant.fields.len() - 1;
                 fcx.register_bound(
                     field.ty,
-                    fcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None),
+                    fcx.tcx.require_lang_item(LangItem::Sized, None),
                     traits::ObligationCause::new(
                         field.span,
                         fcx.body_id,
@@ -718,7 +718,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo
         if forbid_unsized {
             fcx.register_bound(
                 item_ty,
-                fcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None),
+                fcx.tcx.require_lang_item(LangItem::Sized, None),
                 traits::ObligationCause::new(ty_span, fcx.body_id, traits::MiscObligation),
             );
         }
@@ -1223,7 +1223,7 @@ fn receiver_is_valid<'fcx, 'tcx>(
     // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it.
     autoderef.next();
 
-    let receiver_trait_def_id = fcx.tcx.require_lang_item(lang_items::ReceiverTraitLangItem, None);
+    let receiver_trait_def_id = fcx.tcx.require_lang_item(LangItem::Receiver, None);
 
     // Keep dereferencing `receiver_ty` until we get to `self_ty`.
     loop {
index 50e2d6a94bb71082b07763d69b10fddeca049eba..67f67e64dd47be2c81729dcfb7d2670c5f02345f 100644 (file)
@@ -331,7 +331,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     fn visit_upvar_capture_map(&mut self) {
         for (upvar_id, upvar_capture) in self.fcx.typeck_results.borrow().upvar_capture_map.iter() {
             let new_upvar_capture = match *upvar_capture {
-                ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue,
+                ty::UpvarCapture::ByValue(span) => ty::UpvarCapture::ByValue(span),
                 ty::UpvarCapture::ByRef(ref upvar_borrow) => {
                     ty::UpvarCapture::ByRef(ty::UpvarBorrow {
                         kind: upvar_borrow.kind,
index 56a737964c0479809caa9ba85642d015bec8aefb..0d3cac7f7f38a2cd7d6a9dc423e4da5a682bc86a 100644 (file)
@@ -4,9 +4,7 @@
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::lang_items::{
-    CoerceUnsizedTraitLangItem, DispatchFromDynTraitLangItem, UnsizeTraitLangItem,
-};
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::ItemKind;
 use rustc_infer::infer;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
@@ -149,7 +147,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
     let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
     let span = tcx.hir().span(impl_hir_id);
 
-    let dispatch_from_dyn_trait = tcx.require_lang_item(DispatchFromDynTraitLangItem, Some(span));
+    let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
 
     let source = tcx.type_of(impl_did);
     assert!(!source.has_escaping_bound_vars());
@@ -318,9 +316,9 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI
     let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did.expect_local());
     let span = tcx.hir().span(impl_hir_id);
 
-    let coerce_unsized_trait = tcx.require_lang_item(CoerceUnsizedTraitLangItem, Some(span));
+    let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
 
-    let unsize_trait = tcx.lang_items().require(UnsizeTraitLangItem).unwrap_or_else(|err| {
+    let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
         tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
     });
 
index 1b472810ccf1d1950a8abb5072ca0ef93545a4cc..7a3f7ec56a2ce78d6fd043334882ea0e60eaa28b 100644 (file)
@@ -70,6 +70,7 @@ pub fn provide(providers: &mut Providers) {
         generics_of,
         predicates_of,
         predicates_defined_on,
+        projection_ty_from_predicates,
         explicit_predicates_of,
         super_predicates_of,
         type_param_predicates,
@@ -2051,6 +2052,28 @@ fn extend<I: IntoIterator<Item = (ty::Predicate<'tcx>, Span)>>(&mut self, iter:
     result
 }
 
+fn projection_ty_from_predicates(
+    tcx: TyCtxt<'tcx>,
+    key: (
+        // ty_def_id
+        DefId,
+        // def_id of `N` in `<T as Trait>::N`
+        DefId,
+    ),
+) -> Option<ty::ProjectionTy<'tcx>> {
+    let (ty_def_id, item_def_id) = key;
+    let mut projection_ty = None;
+    for (predicate, _) in tcx.predicates_of(ty_def_id).predicates {
+        if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
+            if item_def_id == projection_predicate.projection_ty.item_def_id {
+                projection_ty = Some(projection_predicate.projection_ty);
+                break;
+            }
+        }
+    }
+    projection_ty
+}
+
 fn trait_associated_item_predicates(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
index d1b386c9d4d47279ca3915cb5256f94a8b03d1be..e774f2d095d9cc8cd6cb08ebf239d81022f1788c 100644 (file)
@@ -559,7 +559,7 @@ fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, fn_decl_span: Span) {
                     var_id,
                 ));
                 match upvar_capture {
-                    ty::UpvarCapture::ByValue => {
+                    ty::UpvarCapture::ByValue(_) => {
                         let mode = copy_or_move(&self.mc, &captured_place);
                         self.delegate.consume(&captured_place, mode);
                     }
index 9c44d27447db8614445e2073acb3a640db25f426..1ea1a0910695762bec2c78daf7a0005c513aba1f 100644 (file)
@@ -1,6 +1,6 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_middle::ty::{self, Region, RegionVid, TypeFoldable};
 use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult};
 
@@ -454,7 +454,7 @@ fn param_env_to_generics(
 
         // The `Sized` trait must be handled specially, since we only display it when
         // it is *not* required (i.e., '?Sized')
-        let sized_trait = self.cx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None);
+        let sized_trait = self.cx.tcx.require_lang_item(LangItem::Sized, None);
 
         let mut replacer = RegionReplacer { vid_to_region: &vid_to_region, tcx };
 
@@ -742,9 +742,9 @@ fn unstable_debug_sort<T: Debug>(&self, vec: &mut Vec<T>) {
     fn is_fn_ty(&self, tcx: TyCtxt<'_>, ty: &Type) -> bool {
         match &ty {
             &&Type::ResolvedPath { ref did, .. } => {
-                *did == tcx.require_lang_item(lang_items::FnTraitLangItem, None)
-                    || *did == tcx.require_lang_item(lang_items::FnMutTraitLangItem, None)
-                    || *did == tcx.require_lang_item(lang_items::FnOnceTraitLangItem, None)
+                *did == tcx.require_lang_item(LangItem::Fn, None)
+                    || *did == tcx.require_lang_item(LangItem::FnMut, None)
+                    || *did == tcx.require_lang_item(LangItem::FnOnce, None)
             }
             _ => false,
         }
index ab0b332ee193676b5152ffdf6634ead8d6ff7b3f..c039b181178a40f198eb48512e2cc11c82458cb8 100644 (file)
@@ -135,7 +135,7 @@ fn is_all(&self) -> bool {
 
     /// Renders the configuration for human display, as a short HTML description.
     pub(crate) fn render_short_html(&self) -> String {
-        let mut msg = Html(self, true).to_string();
+        let mut msg = Display(self, Format::ShortHtml).to_string();
         if self.should_capitalize_first_letter() {
             if let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) {
                 msg[i..i + 1].make_ascii_uppercase();
@@ -148,7 +148,11 @@ pub(crate) fn render_short_html(&self) -> String {
     pub(crate) fn render_long_html(&self) -> String {
         let on = if self.should_use_with_in_description() { "with" } else { "on" };
 
-        let mut msg = format!("This is supported {} <strong>{}</strong>", on, Html(self, false));
+        let mut msg = format!(
+            "This is supported {} <strong>{}</strong>",
+            on,
+            Display(self, Format::LongHtml)
+        );
         if self.should_append_only_to_description() {
             msg.push_str(" only");
         }
@@ -156,6 +160,17 @@ pub(crate) fn render_long_html(&self) -> String {
         msg
     }
 
+    /// Renders the configuration for long display, as a long plain text description.
+    pub(crate) fn render_long_plain(&self) -> String {
+        let on = if self.should_use_with_in_description() { "with" } else { "on" };
+
+        let mut msg = format!("This is supported {} {}", on, Display(self, Format::LongPlain));
+        if self.should_append_only_to_description() {
+            msg.push_str(" only");
+        }
+        msg
+    }
+
     fn should_capitalize_first_letter(&self) -> bool {
         match *self {
             Cfg::False | Cfg::True | Cfg::Not(..) => true,
@@ -286,9 +301,31 @@ fn bitor(mut self, other: Cfg) -> Cfg {
     }
 }
 
-/// Pretty-print wrapper for a `Cfg`. Also indicates whether the "short-form" rendering should be
-/// used.
-struct Html<'a>(&'a Cfg, bool);
+#[derive(Clone, Copy)]
+enum Format {
+    LongHtml,
+    LongPlain,
+    ShortHtml,
+}
+
+impl Format {
+    fn is_long(self) -> bool {
+        match self {
+            Format::LongHtml | Format::LongPlain => true,
+            Format::ShortHtml => false,
+        }
+    }
+
+    fn is_html(self) -> bool {
+        match self {
+            Format::LongHtml | Format::ShortHtml => true,
+            Format::LongPlain => false,
+        }
+    }
+}
+
+/// Pretty-print wrapper for a `Cfg`. Also indicates what form of rendering should be used.
+struct Display<'a>(&'a Cfg, Format);
 
 fn write_with_opt_paren<T: fmt::Display>(
     fmt: &mut fmt::Formatter<'_>,
@@ -305,7 +342,7 @@ fn write_with_opt_paren<T: fmt::Display>(
     Ok(())
 }
 
-impl<'a> fmt::Display for Html<'a> {
+impl<'a> fmt::Display for Display<'a> {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self.0 {
             Cfg::Not(ref child) => match **child {
@@ -314,31 +351,86 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
                         if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " };
                     for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
                         fmt.write_str(if i == 0 { "neither " } else { separator })?;
-                        write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg, self.1))?;
+                        write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
                     }
                     Ok(())
                 }
-                ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Html(simple, self.1)),
-                ref c => write!(fmt, "not ({})", Html(c, self.1)),
+                ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)),
+                ref c => write!(fmt, "not ({})", Display(c, self.1)),
             },
 
             Cfg::Any(ref sub_cfgs) => {
                 let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " };
+
+                let short_longhand = self.1.is_long() && {
+                    let all_crate_features = sub_cfgs
+                        .iter()
+                        .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
+                    let all_target_features = sub_cfgs
+                        .iter()
+                        .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_))));
+
+                    if all_crate_features {
+                        fmt.write_str("crate features ")?;
+                        true
+                    } else if all_target_features {
+                        fmt.write_str("target features ")?;
+                        true
+                    } else {
+                        false
+                    }
+                };
+
                 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
                     if i != 0 {
                         fmt.write_str(separator)?;
                     }
-                    write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg, self.1))?;
+                    if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) {
+                        if self.1.is_html() {
+                            write!(fmt, "<code>{}</code>", feat)?;
+                        } else {
+                            write!(fmt, "`{}`", feat)?;
+                        }
+                    } else {
+                        write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
+                    }
                 }
                 Ok(())
             }
 
             Cfg::All(ref sub_cfgs) => {
+                let short_longhand = self.1.is_long() && {
+                    let all_crate_features = sub_cfgs
+                        .iter()
+                        .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
+                    let all_target_features = sub_cfgs
+                        .iter()
+                        .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_))));
+
+                    if all_crate_features {
+                        fmt.write_str("crate features ")?;
+                        true
+                    } else if all_target_features {
+                        fmt.write_str("target features ")?;
+                        true
+                    } else {
+                        false
+                    }
+                };
+
                 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
                     if i != 0 {
                         fmt.write_str(" and ")?;
                     }
-                    write_with_opt_paren(fmt, !sub_cfg.is_simple(), Html(sub_cfg, self.1))?;
+                    if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) {
+                        if self.1.is_html() {
+                            write!(fmt, "<code>{}</code>", feat)?;
+                        } else {
+                            write!(fmt, "`{}`", feat)?;
+                        }
+                    } else {
+                        write_with_opt_paren(fmt, !sub_cfg.is_simple(), Display(sub_cfg, self.1))?;
+                    }
                 }
                 Ok(())
             }
@@ -406,26 +498,39 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
                     },
                     (sym::target_endian, Some(endian)) => return write!(fmt, "{}-endian", endian),
                     (sym::target_pointer_width, Some(bits)) => return write!(fmt, "{}-bit", bits),
-                    (sym::target_feature, Some(feat)) => {
-                        if self.1 {
-                            return write!(fmt, "<code>{}</code>", feat);
-                        } else {
+                    (sym::target_feature, Some(feat)) => match self.1 {
+                        Format::LongHtml => {
                             return write!(fmt, "target feature <code>{}</code>", feat);
                         }
-                    }
+                        Format::LongPlain => return write!(fmt, "target feature `{}`", feat),
+                        Format::ShortHtml => return write!(fmt, "<code>{}</code>", feat),
+                    },
+                    (sym::feature, Some(feat)) => match self.1 {
+                        Format::LongHtml => {
+                            return write!(fmt, "crate feature <code>{}</code>", feat);
+                        }
+                        Format::LongPlain => return write!(fmt, "crate feature `{}`", feat),
+                        Format::ShortHtml => return write!(fmt, "<code>{}</code>", feat),
+                    },
                     _ => "",
                 };
                 if !human_readable.is_empty() {
                     fmt.write_str(human_readable)
                 } else if let Some(v) = value {
-                    write!(
-                        fmt,
-                        "<code>{}=\"{}\"</code>",
-                        Escape(&name.as_str()),
-                        Escape(&v.as_str())
-                    )
-                } else {
+                    if self.1.is_html() {
+                        write!(
+                            fmt,
+                            r#"<code>{}="{}"</code>"#,
+                            Escape(&name.as_str()),
+                            Escape(&v.as_str())
+                        )
+                    } else {
+                        write!(fmt, r#"`{}="{}"`"#, name, v)
+                    }
+                } else if self.1.is_html() {
                     write!(fmt, "<code>{}</code>", Escape(&name.as_str()))
+                } else {
+                    write!(fmt, "`{}`", name)
                 }
             }
         }
index 932585918dfb3f1142cdf913893d21ba2bf61768..a458cdab3030320cca651fc6f9d1458436acbf81 100644 (file)
@@ -16,7 +16,7 @@
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use rustc_hir::lang_items;
+use rustc_hir::lang_items::LangItem;
 use rustc_hir::Mutability;
 use rustc_index::vec::IndexVec;
 use rustc_middle::middle::stability;
@@ -710,7 +710,7 @@ pub enum GenericBound {
 
 impl GenericBound {
     pub fn maybe_sized(cx: &DocContext<'_>) -> GenericBound {
-        let did = cx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None);
+        let did = cx.tcx.require_lang_item(LangItem::Sized, None);
         let empty = cx.tcx.intern_substs(&[]);
         let path = external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty);
         inline::record_extern_fqn(cx, did, TypeKind::Trait);
index 4f751decc8090a2bbeb16ca3f05a67b6112871dc..14df4e7aa8e59567d2594ae9a7069c51ee2dfb84 100644 (file)
@@ -83,9 +83,9 @@ pub struct Options {
     /// Codegen options strings to hand to the compiler.
     pub codegen_options_strs: Vec<String>,
     /// Debugging (`-Z`) options to pass to the compiler.
-    pub debugging_options: DebuggingOptions,
+    pub debugging_opts: DebuggingOptions,
     /// Debugging (`-Z`) options strings to pass to the compiler.
-    pub debugging_options_strs: Vec<String>,
+    pub debugging_opts_strs: Vec<String>,
     /// The target used to compile the crate against.
     pub target: TargetTriple,
     /// Edition used when reading the crate. Defaults to "2015". Also used by default when
@@ -318,9 +318,9 @@ fn println_condition(condition: Condition) {
         let error_format = config::parse_error_format(&matches, color, json_rendered);
 
         let codegen_options = build_codegen_options(matches, error_format);
-        let debugging_options = build_debugging_options(matches, error_format);
+        let debugging_opts = build_debugging_options(matches, error_format);
 
-        let diag = new_handler(error_format, None, &debugging_options);
+        let diag = new_handler(error_format, None, &debugging_opts);
 
         // check for deprecated options
         check_deprecated_options(&matches, &diag);
@@ -365,7 +365,7 @@ fn println_condition(condition: Condition) {
             .iter()
             .map(|s| SearchPath::from_cli_opt(s, error_format))
             .collect();
-        let externs = parse_externs(&matches, &debugging_options, error_format);
+        let externs = parse_externs(&matches, &debugging_opts, error_format);
         let extern_html_root_urls = match parse_extern_html_roots(&matches) {
             Ok(ex) => ex,
             Err(err) => {
@@ -546,7 +546,7 @@ fn println_condition(condition: Condition) {
         let persist_doctests = matches.opt_str("persist-doctests").map(PathBuf::from);
         let test_builder = matches.opt_str("test-builder").map(PathBuf::from);
         let codegen_options_strs = matches.opt_strs("C");
-        let debugging_options_strs = matches.opt_strs("Z");
+        let debugging_opts_strs = matches.opt_strs("Z");
         let lib_strs = matches.opt_strs("L");
         let extern_strs = matches.opt_strs("extern");
         let runtool = matches.opt_str("runtool");
@@ -569,8 +569,8 @@ fn println_condition(condition: Condition) {
             cfgs,
             codegen_options,
             codegen_options_strs,
-            debugging_options,
-            debugging_options_strs,
+            debugging_opts,
+            debugging_opts_strs,
             target,
             edition,
             maybe_sysroot,
index 7a0cf3717c93f77a1476a9ab975ca447da8e6963..89b217dc7d4f255d44a2794173f0ce10f2680656 100644 (file)
@@ -234,7 +234,7 @@ pub fn new_handler(
 /// It returns a tuple containing:
 ///  * Vector of tuples of lints' name and their associated "max" level
 ///  * HashMap of lint id with their associated "max" level
-pub fn init_lints<F>(
+pub(crate) fn init_lints<F>(
     mut allowed_lints: Vec<String>,
     lint_opts: Vec<(String, lint::Level)>,
     filter_call: F,
@@ -257,7 +257,7 @@ pub fn init_lints<F>(
         .filter_map(|lint| {
             // Permit feature-gated lints to avoid feature errors when trying to
             // allow all lints.
-            if lint.name == warnings_lint_name || lint.feature_gate.is_some() {
+            if lint.feature_gate.is_some() || allowed_lints.iter().any(|l| lint.name == l) {
                 None
             } else {
                 filter_call(lint)
@@ -294,7 +294,7 @@ pub fn run_core(
         externs,
         mut cfgs,
         codegen_options,
-        debugging_options,
+        debugging_opts,
         target,
         edition,
         maybe_sysroot,
@@ -328,19 +328,23 @@ pub fn run_core(
     let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name;
     let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name;
     let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name;
+    let renamed_and_removed_lints = rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name;
+    let unknown_lints = rustc_lint::builtin::UNKNOWN_LINTS.name;
 
     // In addition to those specific lints, we also need to allow those given through
     // command line, otherwise they'll get ignored and we don't want that.
-    let allowed_lints = vec![
+    let lints_to_show = vec![
         intra_link_resolution_failure_name.to_owned(),
         missing_docs.to_owned(),
         missing_doc_example.to_owned(),
         private_doc_tests.to_owned(),
         no_crate_level_docs.to_owned(),
         invalid_codeblock_attributes_name.to_owned(),
+        renamed_and_removed_lints.to_owned(),
+        unknown_lints.to_owned(),
     ];
 
-    let (lint_opts, lint_caps) = init_lints(allowed_lints, lint_opts, |lint| {
+    let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| {
         if lint.name == intra_link_resolution_failure_name
             || lint.name == invalid_codeblock_attributes_name
         {
@@ -358,13 +362,13 @@ pub fn run_core(
         search_paths: libs,
         crate_types,
         lint_opts: if !display_warnings { lint_opts } else { vec![] },
-        lint_cap: Some(lint_cap.unwrap_or_else(|| lint::Forbid)),
+        lint_cap,
         cg: codegen_options,
         externs,
         target_triple: target,
         unstable_features: UnstableFeatures::from_environment(),
         actually_rustdoc: true,
-        debugging_opts: debugging_options,
+        debugging_opts,
         error_format,
         edition,
         describe_lints,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
new file mode 100644 (file)
index 0000000..7b7c152
--- /dev/null
@@ -0,0 +1,1039 @@
+use rustc_ast as ast;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::ErrorReported;
+use rustc_feature::UnstableFeatures;
+use rustc_hir as hir;
+use rustc_hir::intravisit;
+use rustc_hir::{HirId, CRATE_HIR_ID};
+use rustc_interface::interface;
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::config::{self, CrateType};
+use rustc_session::{lint, DiagnosticOutput, Session};
+use rustc_span::edition::Edition;
+use rustc_span::source_map::SourceMap;
+use rustc_span::symbol::sym;
+use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP};
+use rustc_target::spec::TargetTriple;
+use tempfile::Builder as TempFileBuilder;
+
+use std::collections::HashMap;
+use std::env;
+use std::io::{self, Write};
+use std::panic;
+use std::path::PathBuf;
+use std::process::{self, Command, Stdio};
+use std::str;
+
+use crate::clean::Attributes;
+use crate::config::Options;
+use crate::core::init_lints;
+use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
+use crate::passes::span_of_attrs;
+
+#[derive(Clone, Default)]
+pub struct TestOptions {
+    /// Whether to disable the default `extern crate my_crate;` when creating doctests.
+    pub no_crate_inject: bool,
+    /// Whether to emit compilation warnings when compiling doctests. Setting this will suppress
+    /// the default `#![allow(unused)]`.
+    pub display_warnings: bool,
+    /// Additional crate-level attributes to add to doctests.
+    pub attrs: Vec<String>,
+}
+
+pub fn run(options: Options) -> Result<(), ErrorReported> {
+    let input = config::Input::File(options.input.clone());
+
+    let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name;
+
+    // In addition to those specific lints, we also need to allow those given through
+    // command line, otherwise they'll get ignored and we don't want that.
+    let allowed_lints = vec![invalid_codeblock_attributes_name.to_owned()];
+
+    let (lint_opts, lint_caps) = init_lints(allowed_lints, options.lint_opts.clone(), |lint| {
+        if lint.name == invalid_codeblock_attributes_name {
+            None
+        } else {
+            Some((lint.name_lower(), lint::Allow))
+        }
+    });
+
+    let crate_types =
+        if options.proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
+
+    let sessopts = config::Options {
+        maybe_sysroot: options.maybe_sysroot.clone(),
+        search_paths: options.libs.clone(),
+        crate_types,
+        lint_opts: if !options.display_warnings { lint_opts } else { vec![] },
+        lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)),
+        cg: options.codegen_options.clone(),
+        externs: options.externs.clone(),
+        unstable_features: UnstableFeatures::from_environment(),
+        actually_rustdoc: true,
+        debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() },
+        edition: options.edition,
+        target_triple: options.target.clone(),
+        ..config::Options::default()
+    };
+
+    let mut cfgs = options.cfgs.clone();
+    cfgs.push("doc".to_owned());
+    cfgs.push("doctest".to_owned());
+    let config = interface::Config {
+        opts: sessopts,
+        crate_cfg: interface::parse_cfgspecs(cfgs),
+        input,
+        input_path: None,
+        output_file: None,
+        output_dir: None,
+        file_loader: None,
+        diagnostic_output: DiagnosticOutput::Default,
+        stderr: None,
+        crate_name: options.crate_name.clone(),
+        lint_caps,
+        register_lints: None,
+        override_queries: None,
+        registry: rustc_driver::diagnostics_registry(),
+    };
+
+    let mut test_args = options.test_args.clone();
+    let display_warnings = options.display_warnings;
+
+    let tests = interface::run_compiler(config, |compiler| {
+        compiler.enter(|queries| {
+            let lower_to_hir = queries.lower_to_hir()?;
+
+            let mut opts = scrape_test_config(lower_to_hir.peek().0);
+            opts.display_warnings |= options.display_warnings;
+            let enable_per_target_ignores = options.enable_per_target_ignores;
+            let mut collector = Collector::new(
+                queries.crate_name()?.peek().to_string(),
+                options,
+                false,
+                opts,
+                Some(compiler.session().parse_sess.clone_source_map()),
+                None,
+                enable_per_target_ignores,
+            );
+
+            let mut global_ctxt = queries.global_ctxt()?.take();
+
+            global_ctxt.enter(|tcx| {
+                let krate = tcx.hir().krate();
+
+                let mut hir_collector = HirCollector {
+                    sess: compiler.session(),
+                    collector: &mut collector,
+                    map: tcx.hir(),
+                    codes: ErrorCodes::from(
+                        compiler.session().opts.unstable_features.is_nightly_build(),
+                    ),
+                    tcx,
+                };
+                hir_collector.visit_testable(
+                    "".to_string(),
+                    &krate.item.attrs,
+                    CRATE_HIR_ID,
+                    krate.item.span,
+                    |this| {
+                        intravisit::walk_crate(this, krate);
+                    },
+                );
+            });
+            compiler.session().abort_if_errors();
+
+            let ret: Result<_, ErrorReported> = Ok(collector.tests);
+            ret
+        })
+    });
+    let tests = match tests {
+        Ok(tests) => tests,
+        Err(ErrorReported) => return Err(ErrorReported),
+    };
+
+    test_args.insert(0, "rustdoctest".to_string());
+
+    testing::test_main(
+        &test_args,
+        tests,
+        Some(testing::Options::new().display_output(display_warnings)),
+    );
+
+    Ok(())
+}
+
+// Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade.
+fn scrape_test_config(krate: &::rustc_hir::Crate<'_>) -> TestOptions {
+    use rustc_ast_pretty::pprust;
+
+    let mut opts =
+        TestOptions { no_crate_inject: false, display_warnings: false, attrs: Vec::new() };
+
+    let test_attrs: Vec<_> = krate
+        .item
+        .attrs
+        .iter()
+        .filter(|a| a.has_name(sym::doc))
+        .flat_map(|a| a.meta_item_list().unwrap_or_else(Vec::new))
+        .filter(|a| a.has_name(sym::test))
+        .collect();
+    let attrs = test_attrs.iter().flat_map(|a| a.meta_item_list().unwrap_or(&[]));
+
+    for attr in attrs {
+        if attr.has_name(sym::no_crate_inject) {
+            opts.no_crate_inject = true;
+        }
+        if attr.has_name(sym::attr) {
+            if let Some(l) = attr.meta_item_list() {
+                for item in l {
+                    opts.attrs.push(pprust::meta_list_item_to_string(item));
+                }
+            }
+        }
+    }
+
+    opts
+}
+
+/// Documentation test failure modes.
+enum TestFailure {
+    /// The test failed to compile.
+    CompileError,
+    /// The test is marked `compile_fail` but compiled successfully.
+    UnexpectedCompilePass,
+    /// The test failed to compile (as expected) but the compiler output did not contain all
+    /// expected error codes.
+    MissingErrorCodes(Vec<String>),
+    /// The test binary was unable to be executed.
+    ExecutionError(io::Error),
+    /// The test binary exited with a non-zero exit code.
+    ///
+    /// This typically means an assertion in the test failed or another form of panic occurred.
+    ExecutionFailure(process::Output),
+    /// The test is marked `should_panic` but the test binary executed successfully.
+    UnexpectedRunPass,
+}
+
+enum DirState {
+    Temp(tempfile::TempDir),
+    Perm(PathBuf),
+}
+
+impl DirState {
+    fn path(&self) -> &std::path::Path {
+        match self {
+            DirState::Temp(t) => t.path(),
+            DirState::Perm(p) => p.as_path(),
+        }
+    }
+}
+
+fn run_test(
+    test: &str,
+    cratename: &str,
+    line: usize,
+    options: Options,
+    should_panic: bool,
+    no_run: bool,
+    as_test_harness: bool,
+    runtool: Option<String>,
+    runtool_args: Vec<String>,
+    target: TargetTriple,
+    compile_fail: bool,
+    mut error_codes: Vec<String>,
+    opts: &TestOptions,
+    edition: Edition,
+    outdir: DirState,
+    path: PathBuf,
+) -> Result<(), TestFailure> {
+    let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts, edition);
+
+    let output_file = outdir.path().join("rust_out");
+
+    let rustc_binary = options
+        .test_builder
+        .as_deref()
+        .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc"));
+    let mut compiler = Command::new(&rustc_binary);
+    compiler.arg("--crate-type").arg("bin");
+    for cfg in &options.cfgs {
+        compiler.arg("--cfg").arg(&cfg);
+    }
+    if let Some(sysroot) = options.maybe_sysroot {
+        compiler.arg("--sysroot").arg(sysroot);
+    }
+    compiler.arg("--edition").arg(&edition.to_string());
+    compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path);
+    compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize));
+    compiler.arg("-o").arg(&output_file);
+    if as_test_harness {
+        compiler.arg("--test");
+    }
+    for lib_str in &options.lib_strs {
+        compiler.arg("-L").arg(&lib_str);
+    }
+    for extern_str in &options.extern_strs {
+        compiler.arg("--extern").arg(&extern_str);
+    }
+    compiler.arg("-Ccodegen-units=1");
+    for codegen_options_str in &options.codegen_options_strs {
+        compiler.arg("-C").arg(&codegen_options_str);
+    }
+    for debugging_option_str in &options.debugging_opts_strs {
+        compiler.arg("-Z").arg(&debugging_option_str);
+    }
+    if no_run && !compile_fail {
+        compiler.arg("--emit=metadata");
+    }
+    compiler.arg("--target").arg(match target {
+        TargetTriple::TargetTriple(s) => s,
+        TargetTriple::TargetPath(path) => {
+            path.to_str().expect("target path must be valid unicode").to_string()
+        }
+    });
+
+    compiler.arg("-");
+    compiler.stdin(Stdio::piped());
+    compiler.stderr(Stdio::piped());
+
+    let mut child = compiler.spawn().expect("Failed to spawn rustc process");
+    {
+        let stdin = child.stdin.as_mut().expect("Failed to open stdin");
+        stdin.write_all(test.as_bytes()).expect("could write out test sources");
+    }
+    let output = child.wait_with_output().expect("Failed to read stdout");
+
+    struct Bomb<'a>(&'a str);
+    impl Drop for Bomb<'_> {
+        fn drop(&mut self) {
+            eprint!("{}", self.0);
+        }
+    }
+    let out = str::from_utf8(&output.stderr).unwrap();
+    let _bomb = Bomb(&out);
+    match (output.status.success(), compile_fail) {
+        (true, true) => {
+            return Err(TestFailure::UnexpectedCompilePass);
+        }
+        (true, false) => {}
+        (false, true) => {
+            if !error_codes.is_empty() {
+                error_codes.retain(|err| !out.contains(&format!("error[{}]: ", err)));
+
+                if !error_codes.is_empty() {
+                    return Err(TestFailure::MissingErrorCodes(error_codes));
+                }
+            }
+        }
+        (false, false) => {
+            return Err(TestFailure::CompileError);
+        }
+    }
+
+    if no_run {
+        return Ok(());
+    }
+
+    // Run the code!
+    let mut cmd;
+
+    if let Some(tool) = runtool {
+        cmd = Command::new(tool);
+        cmd.args(runtool_args);
+        cmd.arg(output_file);
+    } else {
+        cmd = Command::new(output_file);
+    }
+
+    match cmd.output() {
+        Err(e) => return Err(TestFailure::ExecutionError(e)),
+        Ok(out) => {
+            if should_panic && out.status.success() {
+                return Err(TestFailure::UnexpectedRunPass);
+            } else if !should_panic && !out.status.success() {
+                return Err(TestFailure::ExecutionFailure(out));
+            }
+        }
+    }
+
+    Ok(())
+}
+
+/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
+/// lines before the test code begins.
+pub fn make_test(
+    s: &str,
+    cratename: Option<&str>,
+    dont_insert_main: bool,
+    opts: &TestOptions,
+    edition: Edition,
+) -> (String, usize) {
+    let (crate_attrs, everything_else, crates) = partition_source(s);
+    let everything_else = everything_else.trim();
+    let mut line_offset = 0;
+    let mut prog = String::new();
+
+    if opts.attrs.is_empty() && !opts.display_warnings {
+        // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
+        // lints that are commonly triggered in doctests. The crate-level test attributes are
+        // commonly used to make tests fail in case they trigger warnings, so having this there in
+        // that case may cause some tests to pass when they shouldn't have.
+        prog.push_str("#![allow(unused)]\n");
+        line_offset += 1;
+    }
+
+    // Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
+    for attr in &opts.attrs {
+        prog.push_str(&format!("#![{}]\n", attr));
+        line_offset += 1;
+    }
+
+    // Now push any outer attributes from the example, assuming they
+    // are intended to be crate attributes.
+    prog.push_str(&crate_attrs);
+    prog.push_str(&crates);
+
+    // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
+    // crate already is included.
+    let result = rustc_driver::catch_fatal_errors(|| {
+        rustc_span::with_session_globals(edition, || {
+            use rustc_errors::emitter::EmitterWriter;
+            use rustc_errors::Handler;
+            use rustc_parse::maybe_new_parser_from_source_str;
+            use rustc_session::parse::ParseSess;
+            use rustc_span::source_map::FilePathMapping;
+
+            let filename = FileName::anon_source_code(s);
+            let source = crates + everything_else;
+
+            // Any errors in parsing should also appear when the doctest is compiled for real, so just
+            // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
+            let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+            let emitter =
+                EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
+            // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
+            let handler = Handler::with_emitter(false, None, box emitter);
+            let sess = ParseSess::with_span_handler(handler, sm);
+
+            let mut found_main = false;
+            let mut found_extern_crate = cratename.is_none();
+            let mut found_macro = false;
+
+            let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) {
+                Ok(p) => p,
+                Err(errs) => {
+                    for mut err in errs {
+                        err.cancel();
+                    }
+
+                    return (found_main, found_extern_crate, found_macro);
+                }
+            };
+
+            loop {
+                match parser.parse_item() {
+                    Ok(Some(item)) => {
+                        if !found_main {
+                            if let ast::ItemKind::Fn(..) = item.kind {
+                                if item.ident.name == sym::main {
+                                    found_main = true;
+                                }
+                            }
+                        }
+
+                        if !found_extern_crate {
+                            if let ast::ItemKind::ExternCrate(original) = item.kind {
+                                // This code will never be reached if `cratename` is none because
+                                // `found_extern_crate` is initialized to `true` if it is none.
+                                let cratename = cratename.unwrap();
+
+                                match original {
+                                    Some(name) => found_extern_crate = name.as_str() == cratename,
+                                    None => found_extern_crate = item.ident.as_str() == cratename,
+                                }
+                            }
+                        }
+
+                        if !found_macro {
+                            if let ast::ItemKind::MacCall(..) = item.kind {
+                                found_macro = true;
+                            }
+                        }
+
+                        if found_main && found_extern_crate {
+                            break;
+                        }
+                    }
+                    Ok(None) => break,
+                    Err(mut e) => {
+                        e.cancel();
+                        break;
+                    }
+                }
+            }
+
+            (found_main, found_extern_crate, found_macro)
+        })
+    });
+    let (already_has_main, already_has_extern_crate, found_macro) = match result {
+        Ok(result) => result,
+        Err(ErrorReported) => {
+            // If the parser panicked due to a fatal error, pass the test code through unchanged.
+            // The error will be reported during compilation.
+            return (s.to_owned(), 0);
+        }
+    };
+
+    // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
+    // see it. In that case, run the old text-based scan to see if they at least have a main
+    // function written inside a macro invocation. See
+    // https://github.com/rust-lang/rust/issues/56898
+    let already_has_main = if found_macro && !already_has_main {
+        s.lines()
+            .map(|line| {
+                let comment = line.find("//");
+                if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line }
+            })
+            .any(|code| code.contains("fn main"))
+    } else {
+        already_has_main
+    };
+
+    // Don't inject `extern crate std` because it's already injected by the
+    // compiler.
+    if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") {
+        if let Some(cratename) = cratename {
+            // Make sure its actually used if not included.
+            if s.contains(cratename) {
+                prog.push_str(&format!("extern crate {};\n", cratename));
+                line_offset += 1;
+            }
+        }
+    }
+
+    // FIXME: This code cannot yet handle no_std test cases yet
+    if dont_insert_main || already_has_main || prog.contains("![no_std]") {
+        prog.push_str(everything_else);
+    } else {
+        let returns_result = everything_else.trim_end().ends_with("(())");
+        let (main_pre, main_post) = if returns_result {
+            (
+                "fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {",
+                "}\n_inner().unwrap() }",
+            )
+        } else {
+            ("fn main() {\n", "\n}")
+        };
+        prog.extend([main_pre, everything_else, main_post].iter().cloned());
+        line_offset += 1;
+    }
+
+    debug!("final doctest:\n{}", prog);
+
+    (prog, line_offset)
+}
+
+// FIXME(aburka): use a real parser to deal with multiline attributes
+fn partition_source(s: &str) -> (String, String, String) {
+    #[derive(Copy, Clone, PartialEq)]
+    enum PartitionState {
+        Attrs,
+        Crates,
+        Other,
+    }
+    let mut state = PartitionState::Attrs;
+    let mut before = String::new();
+    let mut crates = String::new();
+    let mut after = String::new();
+
+    for line in s.lines() {
+        let trimline = line.trim();
+
+        // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be
+        // shunted into "everything else"
+        match state {
+            PartitionState::Attrs => {
+                state = if trimline.starts_with("#![")
+                    || trimline.chars().all(|c| c.is_whitespace())
+                    || (trimline.starts_with("//") && !trimline.starts_with("///"))
+                {
+                    PartitionState::Attrs
+                } else if trimline.starts_with("extern crate")
+                    || trimline.starts_with("#[macro_use] extern crate")
+                {
+                    PartitionState::Crates
+                } else {
+                    PartitionState::Other
+                };
+            }
+            PartitionState::Crates => {
+                state = if trimline.starts_with("extern crate")
+                    || trimline.starts_with("#[macro_use] extern crate")
+                    || trimline.chars().all(|c| c.is_whitespace())
+                    || (trimline.starts_with("//") && !trimline.starts_with("///"))
+                {
+                    PartitionState::Crates
+                } else {
+                    PartitionState::Other
+                };
+            }
+            PartitionState::Other => {}
+        }
+
+        match state {
+            PartitionState::Attrs => {
+                before.push_str(line);
+                before.push_str("\n");
+            }
+            PartitionState::Crates => {
+                crates.push_str(line);
+                crates.push_str("\n");
+            }
+            PartitionState::Other => {
+                after.push_str(line);
+                after.push_str("\n");
+            }
+        }
+    }
+
+    debug!("before:\n{}", before);
+    debug!("crates:\n{}", crates);
+    debug!("after:\n{}", after);
+
+    (before, after, crates)
+}
+
+pub trait Tester {
+    fn add_test(&mut self, test: String, config: LangString, line: usize);
+    fn get_line(&self) -> usize {
+        0
+    }
+    fn register_header(&mut self, _name: &str, _level: u32) {}
+}
+
+pub struct Collector {
+    pub tests: Vec<testing::TestDescAndFn>,
+
+    // The name of the test displayed to the user, separated by `::`.
+    //
+    // In tests from Rust source, this is the path to the item
+    // e.g., `["std", "vec", "Vec", "push"]`.
+    //
+    // In tests from a markdown file, this is the titles of all headers (h1~h6)
+    // of the sections that contain the code block, e.g., if the markdown file is
+    // written as:
+    //
+    // ``````markdown
+    // # Title
+    //
+    // ## Subtitle
+    //
+    // ```rust
+    // assert!(true);
+    // ```
+    // ``````
+    //
+    // the `names` vector of that test will be `["Title", "Subtitle"]`.
+    names: Vec<String>,
+
+    options: Options,
+    use_headers: bool,
+    enable_per_target_ignores: bool,
+    cratename: String,
+    opts: TestOptions,
+    position: Span,
+    source_map: Option<Lrc<SourceMap>>,
+    filename: Option<PathBuf>,
+    visited_tests: HashMap<(String, usize), usize>,
+}
+
+impl Collector {
+    pub fn new(
+        cratename: String,
+        options: Options,
+        use_headers: bool,
+        opts: TestOptions,
+        source_map: Option<Lrc<SourceMap>>,
+        filename: Option<PathBuf>,
+        enable_per_target_ignores: bool,
+    ) -> Collector {
+        Collector {
+            tests: Vec::new(),
+            names: Vec::new(),
+            options,
+            use_headers,
+            enable_per_target_ignores,
+            cratename,
+            opts,
+            position: DUMMY_SP,
+            source_map,
+            filename,
+            visited_tests: HashMap::new(),
+        }
+    }
+
+    fn generate_name(&self, line: usize, filename: &FileName) -> String {
+        let mut item_path = self.names.join("::");
+        if !item_path.is_empty() {
+            item_path.push(' ');
+        }
+        format!("{} - {}(line {})", filename, item_path, line)
+    }
+
+    pub fn set_position(&mut self, position: Span) {
+        self.position = position;
+    }
+
+    fn get_filename(&self) -> FileName {
+        if let Some(ref source_map) = self.source_map {
+            let filename = source_map.span_to_filename(self.position);
+            if let FileName::Real(ref filename) = filename {
+                if let Ok(cur_dir) = env::current_dir() {
+                    if let Ok(path) = filename.local_path().strip_prefix(&cur_dir) {
+                        return path.to_owned().into();
+                    }
+                }
+            }
+            filename
+        } else if let Some(ref filename) = self.filename {
+            filename.clone().into()
+        } else {
+            FileName::Custom("input".to_owned())
+        }
+    }
+}
+
+impl Tester for Collector {
+    fn add_test(&mut self, test: String, config: LangString, line: usize) {
+        let filename = self.get_filename();
+        let name = self.generate_name(line, &filename);
+        let cratename = self.cratename.to_string();
+        let opts = self.opts.clone();
+        let edition = config.edition.unwrap_or(self.options.edition);
+        let options = self.options.clone();
+        let runtool = self.options.runtool.clone();
+        let runtool_args = self.options.runtool_args.clone();
+        let target = self.options.target.clone();
+        let target_str = target.to_string();
+
+        // FIXME(#44940): if doctests ever support path remapping, then this filename
+        // needs to be the result of `SourceMap::span_to_unmapped_path`.
+        let path = match &filename {
+            FileName::Real(path) => path.local_path().to_path_buf(),
+            _ => PathBuf::from(r"doctest.rs"),
+        };
+
+        let outdir = if let Some(mut path) = options.persist_doctests.clone() {
+            // For example `module/file.rs` would become `module_file_rs`
+            let folder_name = filename
+                .to_string()
+                .chars()
+                .map(|c| if c == '/' || c == '.' { '_' } else { c })
+                .collect::<String>();
+
+            path.push(format!(
+                "{name}_{line}_{number}",
+                name = folder_name,
+                number = {
+                    // Increases the current test number, if this file already
+                    // exists or it creates a new entry with a test number of 0.
+                    self.visited_tests
+                        .entry((folder_name.clone(), line))
+                        .and_modify(|v| *v += 1)
+                        .or_insert(0)
+                },
+                line = line,
+            ));
+
+            std::fs::create_dir_all(&path)
+                .expect("Couldn't create directory for doctest executables");
+
+            DirState::Perm(path)
+        } else {
+            DirState::Temp(
+                TempFileBuilder::new()
+                    .prefix("rustdoctest")
+                    .tempdir()
+                    .expect("rustdoc needs a tempdir"),
+            )
+        };
+
+        debug!("creating test {}: {}", name, test);
+        self.tests.push(testing::TestDescAndFn {
+            desc: testing::TestDesc {
+                name: testing::DynTestName(name),
+                ignore: match config.ignore {
+                    Ignore::All => true,
+                    Ignore::None => false,
+                    Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)),
+                },
+                // compiler failures are test failures
+                should_panic: testing::ShouldPanic::No,
+                allow_fail: config.allow_fail,
+                test_type: testing::TestType::DocTest,
+            },
+            testfn: testing::DynTestFn(box move || {
+                let res = run_test(
+                    &test,
+                    &cratename,
+                    line,
+                    options,
+                    config.should_panic,
+                    config.no_run,
+                    config.test_harness,
+                    runtool,
+                    runtool_args,
+                    target,
+                    config.compile_fail,
+                    config.error_codes,
+                    &opts,
+                    edition,
+                    outdir,
+                    path,
+                );
+
+                if let Err(err) = res {
+                    match err {
+                        TestFailure::CompileError => {
+                            eprint!("Couldn't compile the test.");
+                        }
+                        TestFailure::UnexpectedCompilePass => {
+                            eprint!("Test compiled successfully, but it's marked `compile_fail`.");
+                        }
+                        TestFailure::UnexpectedRunPass => {
+                            eprint!("Test executable succeeded, but it's marked `should_panic`.");
+                        }
+                        TestFailure::MissingErrorCodes(codes) => {
+                            eprint!("Some expected error codes were not found: {:?}", codes);
+                        }
+                        TestFailure::ExecutionError(err) => {
+                            eprint!("Couldn't run the test: {}", err);
+                            if err.kind() == io::ErrorKind::PermissionDenied {
+                                eprint!(" - maybe your tempdir is mounted with noexec?");
+                            }
+                        }
+                        TestFailure::ExecutionFailure(out) => {
+                            let reason = if let Some(code) = out.status.code() {
+                                format!("exit code {}", code)
+                            } else {
+                                String::from("terminated by signal")
+                            };
+
+                            eprintln!("Test executable failed ({}).", reason);
+
+                            // FIXME(#12309): An unfortunate side-effect of capturing the test
+                            // executable's output is that the relative ordering between the test's
+                            // stdout and stderr is lost. However, this is better than the
+                            // alternative: if the test executable inherited the parent's I/O
+                            // handles the output wouldn't be captured at all, even on success.
+                            //
+                            // The ordering could be preserved if the test process' stderr was
+                            // redirected to stdout, but that functionality does not exist in the
+                            // standard library, so it may not be portable enough.
+                            let stdout = str::from_utf8(&out.stdout).unwrap_or_default();
+                            let stderr = str::from_utf8(&out.stderr).unwrap_or_default();
+
+                            if !stdout.is_empty() || !stderr.is_empty() {
+                                eprintln!();
+
+                                if !stdout.is_empty() {
+                                    eprintln!("stdout:\n{}", stdout);
+                                }
+
+                                if !stderr.is_empty() {
+                                    eprintln!("stderr:\n{}", stderr);
+                                }
+                            }
+                        }
+                    }
+
+                    panic::resume_unwind(box ());
+                }
+            }),
+        });
+    }
+
+    fn get_line(&self) -> usize {
+        if let Some(ref source_map) = self.source_map {
+            let line = self.position.lo().to_usize();
+            let line = source_map.lookup_char_pos(BytePos(line as u32)).line;
+            if line > 0 { line - 1 } else { line }
+        } else {
+            0
+        }
+    }
+
+    fn register_header(&mut self, name: &str, level: u32) {
+        if self.use_headers {
+            // We use these headings as test names, so it's good if
+            // they're valid identifiers.
+            let name = name
+                .chars()
+                .enumerate()
+                .map(|(i, c)| {
+                    if (i == 0 && rustc_lexer::is_id_start(c))
+                        || (i != 0 && rustc_lexer::is_id_continue(c))
+                    {
+                        c
+                    } else {
+                        '_'
+                    }
+                })
+                .collect::<String>();
+
+            // Here we try to efficiently assemble the header titles into the
+            // test name in the form of `h1::h2::h3::h4::h5::h6`.
+            //
+            // Suppose that originally `self.names` contains `[h1, h2, h3]`...
+            let level = level as usize;
+            if level <= self.names.len() {
+                // ... Consider `level == 2`. All headers in the lower levels
+                // are irrelevant in this new level. So we should reset
+                // `self.names` to contain headers until <h2>, and replace that
+                // slot with the new name: `[h1, name]`.
+                self.names.truncate(level);
+                self.names[level - 1] = name;
+            } else {
+                // ... On the other hand, consider `level == 5`. This means we
+                // need to extend `self.names` to contain five headers. We fill
+                // in the missing level (<h4>) with `_`. Thus `self.names` will
+                // become `[h1, h2, h3, "_", name]`.
+                if level - 1 > self.names.len() {
+                    self.names.resize(level - 1, "_".to_owned());
+                }
+                self.names.push(name);
+            }
+        }
+    }
+}
+
+struct HirCollector<'a, 'hir, 'tcx> {
+    sess: &'a Session,
+    collector: &'a mut Collector,
+    map: Map<'hir>,
+    codes: ErrorCodes,
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
+    fn visit_testable<F: FnOnce(&mut Self)>(
+        &mut self,
+        name: String,
+        attrs: &[ast::Attribute],
+        hir_id: HirId,
+        sp: Span,
+        nested: F,
+    ) {
+        let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs);
+        if let Some(ref cfg) = attrs.cfg {
+            if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
+                return;
+            }
+        }
+
+        let has_name = !name.is_empty();
+        if has_name {
+            self.collector.names.push(name);
+        }
+
+        attrs.collapse_doc_comments();
+        attrs.unindent_doc_comments();
+        // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
+        // anything else, this will combine them for us.
+        if let Some(doc) = attrs.collapsed_doc_value() {
+            // Use the outermost invocation, so that doctest names come from where the docs were written.
+            let span = attrs
+                .span
+                .map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
+                .unwrap_or(DUMMY_SP);
+            self.collector.set_position(span);
+            markdown::find_testable_code(
+                &doc,
+                self.collector,
+                self.codes,
+                self.collector.enable_per_target_ignores,
+                Some(&crate::html::markdown::ExtraInfo::new(
+                    &self.tcx,
+                    hir_id,
+                    span_of_attrs(&attrs).unwrap_or(sp),
+                )),
+            );
+        }
+
+        nested(self);
+
+        if has_name {
+            self.collector.names.pop();
+        }
+    }
+}
+
+impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> {
+    type Map = Map<'hir>;
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::All(self.map)
+    }
+
+    fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
+        let name = if let hir::ItemKind::Impl { ref self_ty, .. } = item.kind {
+            rustc_hir_pretty::id_to_string(&self.map, self_ty.hir_id)
+        } else {
+            item.ident.to_string()
+        };
+
+        self.visit_testable(name, &item.attrs, item.hir_id, item.span, |this| {
+            intravisit::walk_item(this, item);
+        });
+    }
+
+    fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) {
+        self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
+            intravisit::walk_trait_item(this, item);
+        });
+    }
+
+    fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) {
+        self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
+            intravisit::walk_impl_item(this, item);
+        });
+    }
+
+    fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) {
+        self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
+            intravisit::walk_foreign_item(this, item);
+        });
+    }
+
+    fn visit_variant(
+        &mut self,
+        v: &'hir hir::Variant<'_>,
+        g: &'hir hir::Generics<'_>,
+        item_id: hir::HirId,
+    ) {
+        self.visit_testable(v.ident.to_string(), &v.attrs, v.id, v.span, |this| {
+            intravisit::walk_variant(this, v, g, item_id);
+        });
+    }
+
+    fn visit_struct_field(&mut self, f: &'hir hir::StructField<'_>) {
+        self.visit_testable(f.ident.to_string(), &f.attrs, f.hir_id, f.span, |this| {
+            intravisit::walk_struct_field(this, f);
+        });
+    }
+
+    fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef<'_>) {
+        self.visit_testable(
+            macro_def.ident.to_string(),
+            &macro_def.attrs,
+            macro_def.hir_id,
+            macro_def.span,
+            |_| (),
+        );
+    }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs
new file mode 100644 (file)
index 0000000..a96186a
--- /dev/null
@@ -0,0 +1,279 @@
+use super::{make_test, TestOptions};
+use rustc_span::edition::DEFAULT_EDITION;
+
+#[test]
+fn make_test_basic() {
+    //basic use: wraps with `fn main`, adds `#![allow(unused)]`
+    let opts = TestOptions::default();
+    let input = "assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+fn main() {
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+}
+
+#[test]
+fn make_test_crate_name_no_use() {
+    // If you give a crate name but *don't* use it within the test, it won't bother inserting
+    // the `extern crate` statement.
+    let opts = TestOptions::default();
+    let input = "assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+fn main() {
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+}
+
+#[test]
+fn make_test_crate_name() {
+    // If you give a crate name and use it within the test, it will insert an `extern crate`
+    // statement before `fn main`.
+    let opts = TestOptions::default();
+    let input = "use asdf::qwop;
+assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+extern crate asdf;
+fn main() {
+use asdf::qwop;
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 3));
+}
+
+#[test]
+fn make_test_no_crate_inject() {
+    // Even if you do use the crate within the test, setting `opts.no_crate_inject` will skip
+    // adding it anyway.
+    let opts = TestOptions { no_crate_inject: true, display_warnings: false, attrs: vec![] };
+    let input = "use asdf::qwop;
+assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+fn main() {
+use asdf::qwop;
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+}
+
+#[test]
+fn make_test_ignore_std() {
+    // Even if you include a crate name, and use it in the doctest, we still won't include an
+    // `extern crate` statement if the crate is "std" -- that's included already by the
+    // compiler!
+    let opts = TestOptions::default();
+    let input = "use std::*;
+assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+fn main() {
+use std::*;
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+}
+
+#[test]
+fn make_test_manual_extern_crate() {
+    // When you manually include an `extern crate` statement in your doctest, `make_test`
+    // assumes you've included one for your own crate too.
+    let opts = TestOptions::default();
+    let input = "extern crate asdf;
+use asdf::qwop;
+assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+extern crate asdf;
+fn main() {
+use asdf::qwop;
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+}
+
+#[test]
+fn make_test_manual_extern_crate_with_macro_use() {
+    let opts = TestOptions::default();
+    let input = "#[macro_use] extern crate asdf;
+use asdf::qwop;
+assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+#[macro_use] extern crate asdf;
+fn main() {
+use asdf::qwop;
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+}
+
+#[test]
+fn make_test_opts_attrs() {
+    // If you supplied some doctest attributes with `#![doc(test(attr(...)))]`, it will use
+    // those instead of the stock `#![allow(unused)]`.
+    let mut opts = TestOptions::default();
+    opts.attrs.push("feature(sick_rad)".to_string());
+    let input = "use asdf::qwop;
+assert_eq!(2+2, 4);";
+    let expected = "#![feature(sick_rad)]
+extern crate asdf;
+fn main() {
+use asdf::qwop;
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 3));
+
+    // Adding more will also bump the returned line offset.
+    opts.attrs.push("feature(hella_dope)".to_string());
+    let expected = "#![feature(sick_rad)]
+#![feature(hella_dope)]
+extern crate asdf;
+fn main() {
+use asdf::qwop;
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 4));
+}
+
+#[test]
+fn make_test_crate_attrs() {
+    // Including inner attributes in your doctest will apply them to the whole "crate", pasting
+    // them outside the generated main function.
+    let opts = TestOptions::default();
+    let input = "#![feature(sick_rad)]
+assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+#![feature(sick_rad)]
+fn main() {
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+}
+
+#[test]
+fn make_test_with_main() {
+    // Including your own `fn main` wrapper lets the test use it verbatim.
+    let opts = TestOptions::default();
+    let input = "fn main() {
+    assert_eq!(2+2, 4);
+}";
+    let expected = "#![allow(unused)]
+fn main() {
+    assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 1));
+}
+
+#[test]
+fn make_test_fake_main() {
+    // ... but putting it in a comment will still provide a wrapper.
+    let opts = TestOptions::default();
+    let input = "//Ceci n'est pas une `fn main`
+assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+//Ceci n'est pas une `fn main`
+fn main() {
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+}
+
+#[test]
+fn make_test_dont_insert_main() {
+    // Even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper.
+    let opts = TestOptions::default();
+    let input = "//Ceci n'est pas une `fn main`
+assert_eq!(2+2, 4);";
+    let expected = "#![allow(unused)]
+//Ceci n'est pas une `fn main`
+assert_eq!(2+2, 4);"
+        .to_string();
+    let output = make_test(input, None, true, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 1));
+}
+
+#[test]
+fn make_test_display_warnings() {
+    // If the user is asking to display doctest warnings, suppress the default `allow(unused)`.
+    let mut opts = TestOptions::default();
+    opts.display_warnings = true;
+    let input = "assert_eq!(2+2, 4);";
+    let expected = "fn main() {
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 1));
+}
+
+#[test]
+fn make_test_issues_21299_33731() {
+    let opts = TestOptions::default();
+
+    let input = "// fn main
+assert_eq!(2+2, 4);";
+
+    let expected = "#![allow(unused)]
+// fn main
+fn main() {
+assert_eq!(2+2, 4);
+}"
+    .to_string();
+
+    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 2));
+
+    let input = "extern crate hella_qwop;
+assert_eq!(asdf::foo, 4);";
+
+    let expected = "#![allow(unused)]
+extern crate hella_qwop;
+extern crate asdf;
+fn main() {
+assert_eq!(asdf::foo, 4);
+}"
+    .to_string();
+
+    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 3));
+}
+
+#[test]
+fn make_test_main_in_macro() {
+    let opts = TestOptions::default();
+    let input = "#[macro_use] extern crate my_crate;
+test_wrapper! {
+    fn main() {}
+}";
+    let expected = "#![allow(unused)]
+#[macro_use] extern crate my_crate;
+test_wrapper! {
+    fn main() {}
+}"
+    .to_string();
+
+    let output = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION);
+    assert_eq!(output, (expected, 1));
+}
index b2589e5b806e8673947695f64cc28eb62417a786..56499f736e163bf0b3d07107a10708b94e10b3f8 100644 (file)
@@ -34,9 +34,9 @@
 use std::ops::Range;
 use std::str;
 
+use crate::doctest;
 use crate::html::highlight;
 use crate::html::toc::TocBuilder;
-use crate::test;
 
 use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag};
 
@@ -243,7 +243,7 @@ fn next(&mut self) -> Option<Self::Item> {
                 .collect::<Vec<Cow<'_, str>>>()
                 .join("\n");
             let krate = krate.as_ref().map(|s| &**s);
-            let (test, _) = test::make_test(&test, krate, false, &Default::default(), edition);
+            let (test, _) = doctest::make_test(&test, krate, false, &Default::default(), edition);
             let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };
 
             let edition_string = format!("&amp;edition={}", edition);
@@ -568,7 +568,7 @@ fn next(&mut self) -> Option<Self::Item> {
     }
 }
 
-pub fn find_testable_code<T: test::Tester>(
+pub fn find_testable_code<T: doctest::Tester>(
     doc: &str,
     tests: &mut T,
     error_codes: ErrorCodes,
index d17961521c87d33f14b2899a8f2bd13978dbcf3d..15afe9257d187cc5b203800bc4074fe663eb2305 100644 (file)
@@ -1066,7 +1066,11 @@ fn to_json_string(&self) -> String {
                 krates
                     .iter()
                     .map(|s| {
-                        format!("<li><a href=\"{}index.html\">{}</li>", ensure_trailing_slash(s), s)
+                        format!(
+                            "<li><a class=\"mod\" href=\"{}index.html\">{}</a></li>",
+                            ensure_trailing_slash(s),
+                            s
+                        )
                     })
                     .collect::<String>()
             );
@@ -2126,8 +2130,8 @@ fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering
 fn stability_tags(item: &clean::Item) -> String {
     let mut tags = String::new();
 
-    fn tag_html(class: &str, contents: &str) -> String {
-        format!(r#"<span class="stab {}">{}</span>"#, class, contents)
+    fn tag_html(class: &str, title: &str, contents: &str) -> String {
+        format!(r#"<span class="stab {}" title="{}">{}</span>"#, class, Escape(title), contents)
     }
 
     // The trailing space after each tag is to space it properly against the rest of the docs.
@@ -2136,7 +2140,7 @@ fn tag_html(class: &str, contents: &str) -> String {
         if !stability::deprecation_in_effect(depr.is_since_rustc_version, depr.since.as_deref()) {
             message = "Deprecation planned";
         }
-        tags += &tag_html("deprecated", message);
+        tags += &tag_html("deprecated", "", message);
     }
 
     // The "rustc_private" crates are permanently unstable so it makes no sense
@@ -2147,11 +2151,11 @@ fn tag_html(class: &str, contents: &str) -> String {
         .map(|s| s.level == stability::Unstable && s.feature != "rustc_private")
         == Some(true)
     {
-        tags += &tag_html("unstable", "Experimental");
+        tags += &tag_html("unstable", "", "Experimental");
     }
 
     if let Some(ref cfg) = item.attrs.cfg {
-        tags += &tag_html("portability", &cfg.render_short_html());
+        tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
     }
 
     tags
index 462a696dee6ef3593ccd7df291ccbfd22e2111d1..881b27a5d6184ac8c269c9a0a6a7953d91325482 100644 (file)
@@ -344,6 +344,7 @@ function defocusSearchBar() {
     }
 
     function getHelpElement() {
+        buildHelperPopup();
         return document.getElementById("help");
     }
 
@@ -1576,14 +1577,21 @@ function defocusSearchBar() {
         }
 
         function showResults(results) {
-            if (results.others.length === 1 &&
-                getCurrentValue("rustdoc-go-to-only-result") === "true") {
+            var search = getSearchElement();
+            if (results.others.length === 1
+                && getCurrentValue("rustdoc-go-to-only-result") === "true"
+                // By default, the search DOM element is "empty" (meaning it has no children not
+                // text content). Once a search has been run, it won't be empty, even if you press
+                // ESC or empty the search input (which also "cancels" the search).
+                && (!search.firstChild || search.firstChild.innerText !== getSearchLoadingText()))
+            {
                 var elem = document.createElement("a");
                 elem.href = results.others[0].href;
                 elem.style.display = "none";
                 // For firefox, we need the element to be in the DOM so it can be clicked.
                 document.body.appendChild(elem);
                 elem.click();
+                return;
             }
             var query = getQuery(search_input.value);
 
@@ -1602,7 +1610,6 @@ function defocusSearchBar() {
                 "</div><div id=\"results\">" +
                 ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
 
-            var search = getSearchElement();
             search.innerHTML = output;
             showSearchResults(search);
             var tds = search.getElementsByTagName("td");
@@ -2679,6 +2686,10 @@ function defocusSearchBar() {
         }
     }
 
+    function getSearchLoadingText() {
+        return "Loading search results...";
+    }
+
     if (search_input) {
         search_input.onfocus = function() {
             putBackSearch(this);
@@ -2688,7 +2699,7 @@ function defocusSearchBar() {
     var params = getQueryStringParams();
     if (params && params.search) {
         var search = getSearchElement();
-        search.innerHTML = "<h3 style=\"text-align: center;\">Loading search results...</h3>";
+        search.innerHTML = "<h3 style=\"text-align: center;\">" + getSearchLoadingText() + "</h3>";
         showSearchResults(search);
     }
 
@@ -2797,8 +2808,8 @@ function defocusSearchBar() {
 
         var infos = [
             "Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \
-             restrict the search to a given type.",
-            "Accepted types are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
+             restrict the search to a given item kind.",
+            "Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
              <code>enum</code>, <code>trait</code>, <code>type</code>, <code>macro</code>, \
              and <code>const</code>.",
             "Search functions by type signature (e.g., <code>vec -&gt; usize</code> or \
@@ -2818,12 +2829,12 @@ function defocusSearchBar() {
 
         popup.appendChild(container);
         insertAfter(popup, getSearchElement());
+        // So that it's only built once and then it'll do nothing when called!
+        buildHelperPopup = function() {};
     }
 
     onHashChange(null);
     window.onhashchange = onHashChange;
-
-    buildHelperPopup();
 }());
 
 // This is required in firefox. Explanations: when going back in the history, firefox doesn't re-run
index 346ceacc928c472b368ebbc401f564170eb7691a..55dcaec8ae1e160191a494709b973623b3650fb1 100644 (file)
@@ -1290,7 +1290,7 @@ h4 > .notable-traits {
 #theme-choices > button {
        border: none;
        width: 100%;
-       padding: 4px;
+       padding: 4px 8px;
        text-align: center;
        background: rgba(0,0,0,0);
 }
index 6e8db1e9eb7926d079ee51cea67a6872cdc091f1..f7d474b5562be41b42b202f0181f1ad59e738b8d 100644 (file)
@@ -495,6 +495,7 @@ kbd {
 #theme-picker, #settings-menu, .help-button {
        border-color: #5c6773;
        background-color: #0f1419;
+       color: #fff;
 }
 
 #theme-picker > img, #settings-menu > img {
@@ -513,7 +514,7 @@ kbd {
 }
 
 #theme-choices > button:not(:first-child) {
-       border-top-color: #c5c5c5;
+       border-top-color: #5c6773;
 }
 
 #theme-choices > button:hover, #theme-choices > button:focus {
index eeb1f0a3d4a0bfa7448dfc4575f7a23fb36dbd3a..f5a853377684385708c93ce73f039874c4b9116c 100644 (file)
@@ -389,6 +389,7 @@ kbd {
 #theme-picker, #settings-menu, .help-button {
        border-color: #e0e0e0;
        background: #f0f0f0;
+       color: #000;
 }
 
 #theme-picker:hover, #theme-picker:focus,
index 6d79924a06b5b93dc23d6099b25c56330e7c9982..8658b39b4557815f3200e0e07adabe02d6706198 100644 (file)
 mod doctree;
 #[macro_use]
 mod error;
+mod doctest;
 mod fold;
 crate mod formats;
 pub mod html;
 mod json;
 mod markdown;
 mod passes;
-mod test;
 mod theme;
 mod visit_ast;
 mod visit_lib;
@@ -472,11 +472,11 @@ fn run_renderer<T: formats::FormatRenderer>(
 }
 
 fn main_options(options: config::Options) -> MainResult {
-    let diag = core::new_handler(options.error_format, None, &options.debugging_options);
+    let diag = core::new_handler(options.error_format, None, &options.debugging_opts);
 
     match (options.should_test, options.markdown_input()) {
         (true, true) => return wrap_return(&diag, markdown::test(options)),
-        (true, false) => return test::run(options),
+        (true, false) => return doctest::run(options),
         (false, true) => {
             return wrap_return(
                 &diag,
@@ -488,7 +488,7 @@ fn main_options(options: config::Options) -> MainResult {
 
     // need to move these items separately because we lose them by the time the closure is called,
     // but we can't crates the Handler ahead of time because it's not Send
-    let diag_opts = (options.error_format, options.edition, options.debugging_options.clone());
+    let diag_opts = (options.error_format, options.edition, options.debugging_opts.clone());
     let show_coverage = options.show_coverage;
 
     // First, parse the crate and extract all relevant information.
index 89d184e35cb064a177c3a81a3961a97add895e12..3a87e1c46a6155e5a16a74a955178074f0357b87 100644 (file)
@@ -7,10 +7,10 @@
 use rustc_span::source_map::DUMMY_SP;
 
 use crate::config::{Options, RenderOptions};
+use crate::doctest::{Collector, TestOptions};
 use crate::html::escape::Escape;
 use crate::html::markdown;
 use crate::html::markdown::{find_testable_code, ErrorCodes, IdMap, Markdown, MarkdownWithToc};
-use crate::test::{Collector, TestOptions};
 
 /// Separate any lines at the start of the file that begin with `# ` or `%`.
 fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) {
index bf091a0a624ddabb9be98d87e3d3066affeb4a7c..65d116b9c670c8d396252673def660394c62b839 100644 (file)
@@ -829,16 +829,13 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                             }
                             let candidates =
                                 candidates.map(|candidate| candidate.map(|(res, _)| res));
-                            let candidates = [TypeNS, ValueNS, MacroNS]
-                                .iter()
-                                .filter_map(|&ns| candidates[ns].map(|res| (res, ns)));
                             ambiguity_error(
                                 cx,
                                 &item,
                                 path_str,
                                 &dox,
                                 link_range,
-                                candidates.collect(),
+                                candidates.present_items().collect(),
                             );
                             continue;
                         }
@@ -880,7 +877,7 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                         fragment = Some(path.to_owned());
                     } else {
                         // `[char]` when a `char` module is in scope
-                        let candidates = vec![(res, TypeNS), (prim, TypeNS)];
+                        let candidates = vec![res, prim];
                         ambiguity_error(cx, &item, path_str, &dox, link_range, candidates);
                         continue;
                     }
@@ -898,20 +895,8 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                         specified.article(),
                         specified.descr()
                     );
-                    let suggestion = resolved.display_for(path_str);
-                    let help_msg =
-                        format!("to link to the {}, use its disambiguator", resolved.descr());
                     diag.note(&note);
-                    if let Some(sp) = sp {
-                        diag.span_suggestion(
-                            sp,
-                            &help_msg,
-                            suggestion,
-                            Applicability::MaybeIncorrect,
-                        );
-                    } else {
-                        diag.help(&format!("{}: {}", help_msg, suggestion));
-                    }
+                    suggest_disambiguator(resolved, diag, path_str, &dox, sp, &link_range);
                 });
             };
             if let Res::PrimTy(_) = res {
@@ -1047,17 +1032,32 @@ fn from_str(link: &str) -> Result<(Self, &str), ()> {
         }
     }
 
-    fn display_for(self, path_str: &str) -> String {
+    /// WARNING: panics on `Res::Err`
+    fn from_res(res: Res) -> Self {
+        match res {
+            Res::Def(kind, _) => Disambiguator::Kind(kind),
+            Res::PrimTy(_) => Disambiguator::Primitive,
+            _ => Disambiguator::Namespace(res.ns().expect("can't call `from_res` on Res::err")),
+        }
+    }
+
+    /// Return (description of the change, suggestion)
+    fn display_for(self, path_str: &str) -> (&'static str, String) {
+        const PREFIX: &str = "prefix with the item kind";
+        const FUNCTION: &str = "add parentheses";
+        const MACRO: &str = "add an exclamation mark";
+
         let kind = match self {
-            Disambiguator::Primitive => return format!("prim@{}", path_str),
+            Disambiguator::Primitive => return (PREFIX, format!("prim@{}", path_str)),
             Disambiguator::Kind(kind) => kind,
             Disambiguator::Namespace(_) => panic!("display_for cannot be used on namespaces"),
         };
         if kind == DefKind::Macro(MacroKind::Bang) {
-            return format!("{}!", path_str);
+            return (MACRO, format!("{}!", path_str));
         } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
-            return format!("{}()", path_str);
+            return (FUNCTION, format!("{}()", path_str));
         }
+
         let prefix = match kind {
             DefKind::Struct => "struct",
             DefKind::Enum => "enum",
@@ -1079,7 +1079,9 @@ fn display_for(self, path_str: &str) -> String {
                 Namespace::MacroNS => "macro",
             },
         };
-        format!("{}@{}", prefix, path_str)
+
+        // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
+        (PREFIX, format!("{}@{}", prefix, path_str))
     }
 
     fn ns(self) -> Namespace {
@@ -1247,12 +1249,12 @@ fn ambiguity_error(
     path_str: &str,
     dox: &str,
     link_range: Option<Range<usize>>,
-    candidates: Vec<(Res, Namespace)>,
+    candidates: Vec<Res>,
 ) {
     let mut msg = format!("`{}` is ", path_str);
 
     match candidates.as_slice() {
-        [(first_def, _), (second_def, _)] => {
+        [first_def, second_def] => {
             msg += &format!(
                 "both {} {} and {} {}",
                 first_def.article(),
@@ -1263,7 +1265,7 @@ fn ambiguity_error(
         }
         _ => {
             let mut candidates = candidates.iter().peekable();
-            while let Some((res, _)) = candidates.next() {
+            while let Some(res) = candidates.next() {
                 if candidates.peek().is_some() {
                     msg += &format!("{} {}, ", res.article(), res.descr());
                 } else {
@@ -1276,52 +1278,38 @@ fn ambiguity_error(
     report_diagnostic(cx, &msg, item, dox, link_range.clone(), |diag, sp| {
         if let Some(sp) = sp {
             diag.span_label(sp, "ambiguous link");
+        } else {
+            diag.note("ambiguous link");
+        }
 
-            let link_range = link_range.expect("must have a link range if we have a span");
-
-            for (res, ns) in candidates {
-                let (action, mut suggestion) = match res {
-                    Res::Def(DefKind::AssocFn | DefKind::Fn, _) => {
-                        ("add parentheses", format!("{}()", path_str))
-                    }
-                    Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
-                        ("add an exclamation mark", format!("{}!", path_str))
-                    }
-                    _ => {
-                        let type_ = match (res, ns) {
-                            (Res::PrimTy(_), _) => "prim",
-                            (Res::Def(DefKind::Const, _), _) => "const",
-                            (Res::Def(DefKind::Static, _), _) => "static",
-                            (Res::Def(DefKind::Struct, _), _) => "struct",
-                            (Res::Def(DefKind::Enum, _), _) => "enum",
-                            (Res::Def(DefKind::Union, _), _) => "union",
-                            (Res::Def(DefKind::Trait, _), _) => "trait",
-                            (Res::Def(DefKind::Mod, _), _) => "module",
-                            (_, TypeNS) => "type",
-                            (_, ValueNS) => "value",
-                            (Res::Def(DefKind::Macro(MacroKind::Derive), _), MacroNS) => "derive",
-                            (_, MacroNS) => "macro",
-                        };
-
-                        // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
-                        ("prefix with the item type", format!("{}@{}", type_, path_str))
-                    }
-                };
+        for res in candidates {
+            let disambiguator = Disambiguator::from_res(res);
+            suggest_disambiguator(disambiguator, diag, path_str, dox, sp, &link_range);
+        }
+    });
+}
 
-                if dox.bytes().nth(link_range.start) == Some(b'`') {
-                    suggestion = format!("`{}`", suggestion);
-                }
+fn suggest_disambiguator(
+    disambiguator: Disambiguator,
+    diag: &mut DiagnosticBuilder<'_>,
+    path_str: &str,
+    dox: &str,
+    sp: Option<rustc_span::Span>,
+    link_range: &Option<Range<usize>>,
+) {
+    let (action, mut suggestion) = disambiguator.display_for(path_str);
+    let help = format!("to link to the {}, {}", disambiguator.descr(), action);
 
-                // FIXME: Create a version of this suggestion for when we don't have the span.
-                diag.span_suggestion(
-                    sp,
-                    &format!("to link to the {}, {}", res.descr(), action),
-                    suggestion,
-                    Applicability::MaybeIncorrect,
-                );
-            }
+    if let Some(sp) = sp {
+        let link_range = link_range.as_ref().expect("must have a link range if we have a span");
+        if dox.bytes().nth(link_range.start) == Some(b'`') {
+            suggestion = format!("`{}`", suggestion);
         }
-    });
+
+        diag.span_suggestion(sp, &help, suggestion, Applicability::MaybeIncorrect);
+    } else {
+        diag.help(&format!("{}: {}", help, suggestion));
+    }
 }
 
 fn privacy_error(
index a465a5f681f5c16e5e1da949b604aa7757e0b846..cbbe86dc433f33456a7343750e21f044bbf51eda 100644 (file)
@@ -1,7 +1,7 @@
 //! This pass is overloaded and runs two different lints.
 //!
-//! - MISSING_DOC_CODE_EXAMPLES: this looks for public items missing doc-tests
-//! - PRIVATE_DOC_TESTS: this looks for private items with doc-tests.
+//! - MISSING_DOC_CODE_EXAMPLES: this lint is **UNSTABLE** and looks for public items missing doc-tests
+//! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doc-tests.
 
 use super::{span_of_attrs, Pass};
 use crate::clean;
@@ -54,7 +54,7 @@ pub(crate) fn new() -> Tests {
     }
 }
 
-impl crate::test::Tester for Tests {
+impl crate::doctest::Tester for Tests {
     fn add_test(&mut self, _: String, _: LangString, _: usize) {
         self.found_tests += 1;
     }
@@ -89,7 +89,9 @@ pub fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
 
     find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None);
 
-    if tests.found_tests == 0 {
+    if tests.found_tests == 0
+        && rustc_feature::UnstableFeatures::from_environment().is_nightly_build()
+    {
         if should_have_doc_example(&item.inner) {
             debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
             let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
@@ -100,9 +102,7 @@ pub fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
                 |lint| lint.build("missing code example in this documentation").emit(),
             );
         }
-    } else if rustc_feature::UnstableFeatures::from_environment().is_nightly_build()
-        && tests.found_tests > 0
-        && !cx.renderinfo.borrow().access_levels.is_public(item.def_id)
+    } else if tests.found_tests > 0 && !cx.renderinfo.borrow().access_levels.is_public(item.def_id)
     {
         cx.tcx.struct_span_lint_hir(
             lint::builtin::PRIVATE_DOC_TESTS,
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
deleted file mode 100644 (file)
index 693d5b9..0000000
+++ /dev/null
@@ -1,1039 +0,0 @@
-use rustc_ast as ast;
-use rustc_data_structures::sync::Lrc;
-use rustc_errors::ErrorReported;
-use rustc_feature::UnstableFeatures;
-use rustc_hir as hir;
-use rustc_hir::intravisit;
-use rustc_hir::{HirId, CRATE_HIR_ID};
-use rustc_interface::interface;
-use rustc_middle::hir::map::Map;
-use rustc_middle::ty::TyCtxt;
-use rustc_session::config::{self, CrateType};
-use rustc_session::{lint, DiagnosticOutput, Session};
-use rustc_span::edition::Edition;
-use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::sym;
-use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP};
-use rustc_target::spec::TargetTriple;
-use tempfile::Builder as TempFileBuilder;
-
-use std::collections::HashMap;
-use std::env;
-use std::io::{self, Write};
-use std::panic;
-use std::path::PathBuf;
-use std::process::{self, Command, Stdio};
-use std::str;
-
-use crate::clean::Attributes;
-use crate::config::Options;
-use crate::core::init_lints;
-use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
-use crate::passes::span_of_attrs;
-
-#[derive(Clone, Default)]
-pub struct TestOptions {
-    /// Whether to disable the default `extern crate my_crate;` when creating doctests.
-    pub no_crate_inject: bool,
-    /// Whether to emit compilation warnings when compiling doctests. Setting this will suppress
-    /// the default `#![allow(unused)]`.
-    pub display_warnings: bool,
-    /// Additional crate-level attributes to add to doctests.
-    pub attrs: Vec<String>,
-}
-
-pub fn run(options: Options) -> Result<(), ErrorReported> {
-    let input = config::Input::File(options.input.clone());
-
-    let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name;
-
-    // In addition to those specific lints, we also need to allow those given through
-    // command line, otherwise they'll get ignored and we don't want that.
-    let allowed_lints = vec![invalid_codeblock_attributes_name.to_owned()];
-
-    let (lint_opts, lint_caps) = init_lints(allowed_lints, options.lint_opts.clone(), |lint| {
-        if lint.name == invalid_codeblock_attributes_name {
-            None
-        } else {
-            Some((lint.name_lower(), lint::Allow))
-        }
-    });
-
-    let crate_types =
-        if options.proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
-
-    let sessopts = config::Options {
-        maybe_sysroot: options.maybe_sysroot.clone(),
-        search_paths: options.libs.clone(),
-        crate_types,
-        lint_opts: if !options.display_warnings { lint_opts } else { vec![] },
-        lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)),
-        cg: options.codegen_options.clone(),
-        externs: options.externs.clone(),
-        unstable_features: UnstableFeatures::from_environment(),
-        actually_rustdoc: true,
-        debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() },
-        edition: options.edition,
-        target_triple: options.target.clone(),
-        ..config::Options::default()
-    };
-
-    let mut cfgs = options.cfgs.clone();
-    cfgs.push("doc".to_owned());
-    cfgs.push("doctest".to_owned());
-    let config = interface::Config {
-        opts: sessopts,
-        crate_cfg: interface::parse_cfgspecs(cfgs),
-        input,
-        input_path: None,
-        output_file: None,
-        output_dir: None,
-        file_loader: None,
-        diagnostic_output: DiagnosticOutput::Default,
-        stderr: None,
-        crate_name: options.crate_name.clone(),
-        lint_caps,
-        register_lints: None,
-        override_queries: None,
-        registry: rustc_driver::diagnostics_registry(),
-    };
-
-    let mut test_args = options.test_args.clone();
-    let display_warnings = options.display_warnings;
-
-    let tests = interface::run_compiler(config, |compiler| {
-        compiler.enter(|queries| {
-            let lower_to_hir = queries.lower_to_hir()?;
-
-            let mut opts = scrape_test_config(lower_to_hir.peek().0);
-            opts.display_warnings |= options.display_warnings;
-            let enable_per_target_ignores = options.enable_per_target_ignores;
-            let mut collector = Collector::new(
-                queries.crate_name()?.peek().to_string(),
-                options,
-                false,
-                opts,
-                Some(compiler.session().parse_sess.clone_source_map()),
-                None,
-                enable_per_target_ignores,
-            );
-
-            let mut global_ctxt = queries.global_ctxt()?.take();
-
-            global_ctxt.enter(|tcx| {
-                let krate = tcx.hir().krate();
-
-                let mut hir_collector = HirCollector {
-                    sess: compiler.session(),
-                    collector: &mut collector,
-                    map: tcx.hir(),
-                    codes: ErrorCodes::from(
-                        compiler.session().opts.unstable_features.is_nightly_build(),
-                    ),
-                    tcx,
-                };
-                hir_collector.visit_testable(
-                    "".to_string(),
-                    &krate.item.attrs,
-                    CRATE_HIR_ID,
-                    krate.item.span,
-                    |this| {
-                        intravisit::walk_crate(this, krate);
-                    },
-                );
-            });
-            compiler.session().abort_if_errors();
-
-            let ret: Result<_, ErrorReported> = Ok(collector.tests);
-            ret
-        })
-    });
-    let tests = match tests {
-        Ok(tests) => tests,
-        Err(ErrorReported) => return Err(ErrorReported),
-    };
-
-    test_args.insert(0, "rustdoctest".to_string());
-
-    testing::test_main(
-        &test_args,
-        tests,
-        Some(testing::Options::new().display_output(display_warnings)),
-    );
-
-    Ok(())
-}
-
-// Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade.
-fn scrape_test_config(krate: &::rustc_hir::Crate<'_>) -> TestOptions {
-    use rustc_ast_pretty::pprust;
-
-    let mut opts =
-        TestOptions { no_crate_inject: false, display_warnings: false, attrs: Vec::new() };
-
-    let test_attrs: Vec<_> = krate
-        .item
-        .attrs
-        .iter()
-        .filter(|a| a.has_name(sym::doc))
-        .flat_map(|a| a.meta_item_list().unwrap_or_else(Vec::new))
-        .filter(|a| a.has_name(sym::test))
-        .collect();
-    let attrs = test_attrs.iter().flat_map(|a| a.meta_item_list().unwrap_or(&[]));
-
-    for attr in attrs {
-        if attr.has_name(sym::no_crate_inject) {
-            opts.no_crate_inject = true;
-        }
-        if attr.has_name(sym::attr) {
-            if let Some(l) = attr.meta_item_list() {
-                for item in l {
-                    opts.attrs.push(pprust::meta_list_item_to_string(item));
-                }
-            }
-        }
-    }
-
-    opts
-}
-
-/// Documentation test failure modes.
-enum TestFailure {
-    /// The test failed to compile.
-    CompileError,
-    /// The test is marked `compile_fail` but compiled successfully.
-    UnexpectedCompilePass,
-    /// The test failed to compile (as expected) but the compiler output did not contain all
-    /// expected error codes.
-    MissingErrorCodes(Vec<String>),
-    /// The test binary was unable to be executed.
-    ExecutionError(io::Error),
-    /// The test binary exited with a non-zero exit code.
-    ///
-    /// This typically means an assertion in the test failed or another form of panic occurred.
-    ExecutionFailure(process::Output),
-    /// The test is marked `should_panic` but the test binary executed successfully.
-    UnexpectedRunPass,
-}
-
-enum DirState {
-    Temp(tempfile::TempDir),
-    Perm(PathBuf),
-}
-
-impl DirState {
-    fn path(&self) -> &std::path::Path {
-        match self {
-            DirState::Temp(t) => t.path(),
-            DirState::Perm(p) => p.as_path(),
-        }
-    }
-}
-
-fn run_test(
-    test: &str,
-    cratename: &str,
-    line: usize,
-    options: Options,
-    should_panic: bool,
-    no_run: bool,
-    as_test_harness: bool,
-    runtool: Option<String>,
-    runtool_args: Vec<String>,
-    target: TargetTriple,
-    compile_fail: bool,
-    mut error_codes: Vec<String>,
-    opts: &TestOptions,
-    edition: Edition,
-    outdir: DirState,
-    path: PathBuf,
-) -> Result<(), TestFailure> {
-    let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts, edition);
-
-    let output_file = outdir.path().join("rust_out");
-
-    let rustc_binary = options
-        .test_builder
-        .as_deref()
-        .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc"));
-    let mut compiler = Command::new(&rustc_binary);
-    compiler.arg("--crate-type").arg("bin");
-    for cfg in &options.cfgs {
-        compiler.arg("--cfg").arg(&cfg);
-    }
-    if let Some(sysroot) = options.maybe_sysroot {
-        compiler.arg("--sysroot").arg(sysroot);
-    }
-    compiler.arg("--edition").arg(&edition.to_string());
-    compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path);
-    compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize));
-    compiler.arg("-o").arg(&output_file);
-    if as_test_harness {
-        compiler.arg("--test");
-    }
-    for lib_str in &options.lib_strs {
-        compiler.arg("-L").arg(&lib_str);
-    }
-    for extern_str in &options.extern_strs {
-        compiler.arg("--extern").arg(&extern_str);
-    }
-    compiler.arg("-Ccodegen-units=1");
-    for codegen_options_str in &options.codegen_options_strs {
-        compiler.arg("-C").arg(&codegen_options_str);
-    }
-    for debugging_option_str in &options.debugging_options_strs {
-        compiler.arg("-Z").arg(&debugging_option_str);
-    }
-    if no_run && !compile_fail {
-        compiler.arg("--emit=metadata");
-    }
-    compiler.arg("--target").arg(match target {
-        TargetTriple::TargetTriple(s) => s,
-        TargetTriple::TargetPath(path) => {
-            path.to_str().expect("target path must be valid unicode").to_string()
-        }
-    });
-
-    compiler.arg("-");
-    compiler.stdin(Stdio::piped());
-    compiler.stderr(Stdio::piped());
-
-    let mut child = compiler.spawn().expect("Failed to spawn rustc process");
-    {
-        let stdin = child.stdin.as_mut().expect("Failed to open stdin");
-        stdin.write_all(test.as_bytes()).expect("could write out test sources");
-    }
-    let output = child.wait_with_output().expect("Failed to read stdout");
-
-    struct Bomb<'a>(&'a str);
-    impl Drop for Bomb<'_> {
-        fn drop(&mut self) {
-            eprint!("{}", self.0);
-        }
-    }
-    let out = str::from_utf8(&output.stderr).unwrap();
-    let _bomb = Bomb(&out);
-    match (output.status.success(), compile_fail) {
-        (true, true) => {
-            return Err(TestFailure::UnexpectedCompilePass);
-        }
-        (true, false) => {}
-        (false, true) => {
-            if !error_codes.is_empty() {
-                error_codes.retain(|err| !out.contains(&format!("error[{}]: ", err)));
-
-                if !error_codes.is_empty() {
-                    return Err(TestFailure::MissingErrorCodes(error_codes));
-                }
-            }
-        }
-        (false, false) => {
-            return Err(TestFailure::CompileError);
-        }
-    }
-
-    if no_run {
-        return Ok(());
-    }
-
-    // Run the code!
-    let mut cmd;
-
-    if let Some(tool) = runtool {
-        cmd = Command::new(tool);
-        cmd.args(runtool_args);
-        cmd.arg(output_file);
-    } else {
-        cmd = Command::new(output_file);
-    }
-
-    match cmd.output() {
-        Err(e) => return Err(TestFailure::ExecutionError(e)),
-        Ok(out) => {
-            if should_panic && out.status.success() {
-                return Err(TestFailure::UnexpectedRunPass);
-            } else if !should_panic && !out.status.success() {
-                return Err(TestFailure::ExecutionFailure(out));
-            }
-        }
-    }
-
-    Ok(())
-}
-
-/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
-/// lines before the test code begins.
-pub fn make_test(
-    s: &str,
-    cratename: Option<&str>,
-    dont_insert_main: bool,
-    opts: &TestOptions,
-    edition: Edition,
-) -> (String, usize) {
-    let (crate_attrs, everything_else, crates) = partition_source(s);
-    let everything_else = everything_else.trim();
-    let mut line_offset = 0;
-    let mut prog = String::new();
-
-    if opts.attrs.is_empty() && !opts.display_warnings {
-        // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
-        // lints that are commonly triggered in doctests. The crate-level test attributes are
-        // commonly used to make tests fail in case they trigger warnings, so having this there in
-        // that case may cause some tests to pass when they shouldn't have.
-        prog.push_str("#![allow(unused)]\n");
-        line_offset += 1;
-    }
-
-    // Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
-    for attr in &opts.attrs {
-        prog.push_str(&format!("#![{}]\n", attr));
-        line_offset += 1;
-    }
-
-    // Now push any outer attributes from the example, assuming they
-    // are intended to be crate attributes.
-    prog.push_str(&crate_attrs);
-    prog.push_str(&crates);
-
-    // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
-    // crate already is included.
-    let result = rustc_driver::catch_fatal_errors(|| {
-        rustc_span::with_session_globals(edition, || {
-            use rustc_errors::emitter::EmitterWriter;
-            use rustc_errors::Handler;
-            use rustc_parse::maybe_new_parser_from_source_str;
-            use rustc_session::parse::ParseSess;
-            use rustc_span::source_map::FilePathMapping;
-
-            let filename = FileName::anon_source_code(s);
-            let source = crates + everything_else;
-
-            // Any errors in parsing should also appear when the doctest is compiled for real, so just
-            // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
-            let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-            let emitter =
-                EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
-            // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
-            let handler = Handler::with_emitter(false, None, box emitter);
-            let sess = ParseSess::with_span_handler(handler, sm);
-
-            let mut found_main = false;
-            let mut found_extern_crate = cratename.is_none();
-            let mut found_macro = false;
-
-            let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) {
-                Ok(p) => p,
-                Err(errs) => {
-                    for mut err in errs {
-                        err.cancel();
-                    }
-
-                    return (found_main, found_extern_crate, found_macro);
-                }
-            };
-
-            loop {
-                match parser.parse_item() {
-                    Ok(Some(item)) => {
-                        if !found_main {
-                            if let ast::ItemKind::Fn(..) = item.kind {
-                                if item.ident.name == sym::main {
-                                    found_main = true;
-                                }
-                            }
-                        }
-
-                        if !found_extern_crate {
-                            if let ast::ItemKind::ExternCrate(original) = item.kind {
-                                // This code will never be reached if `cratename` is none because
-                                // `found_extern_crate` is initialized to `true` if it is none.
-                                let cratename = cratename.unwrap();
-
-                                match original {
-                                    Some(name) => found_extern_crate = name.as_str() == cratename,
-                                    None => found_extern_crate = item.ident.as_str() == cratename,
-                                }
-                            }
-                        }
-
-                        if !found_macro {
-                            if let ast::ItemKind::MacCall(..) = item.kind {
-                                found_macro = true;
-                            }
-                        }
-
-                        if found_main && found_extern_crate {
-                            break;
-                        }
-                    }
-                    Ok(None) => break,
-                    Err(mut e) => {
-                        e.cancel();
-                        break;
-                    }
-                }
-            }
-
-            (found_main, found_extern_crate, found_macro)
-        })
-    });
-    let (already_has_main, already_has_extern_crate, found_macro) = match result {
-        Ok(result) => result,
-        Err(ErrorReported) => {
-            // If the parser panicked due to a fatal error, pass the test code through unchanged.
-            // The error will be reported during compilation.
-            return (s.to_owned(), 0);
-        }
-    };
-
-    // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
-    // see it. In that case, run the old text-based scan to see if they at least have a main
-    // function written inside a macro invocation. See
-    // https://github.com/rust-lang/rust/issues/56898
-    let already_has_main = if found_macro && !already_has_main {
-        s.lines()
-            .map(|line| {
-                let comment = line.find("//");
-                if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line }
-            })
-            .any(|code| code.contains("fn main"))
-    } else {
-        already_has_main
-    };
-
-    // Don't inject `extern crate std` because it's already injected by the
-    // compiler.
-    if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") {
-        if let Some(cratename) = cratename {
-            // Make sure its actually used if not included.
-            if s.contains(cratename) {
-                prog.push_str(&format!("extern crate {};\n", cratename));
-                line_offset += 1;
-            }
-        }
-    }
-
-    // FIXME: This code cannot yet handle no_std test cases yet
-    if dont_insert_main || already_has_main || prog.contains("![no_std]") {
-        prog.push_str(everything_else);
-    } else {
-        let returns_result = everything_else.trim_end().ends_with("(())");
-        let (main_pre, main_post) = if returns_result {
-            (
-                "fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {",
-                "}\n_inner().unwrap() }",
-            )
-        } else {
-            ("fn main() {\n", "\n}")
-        };
-        prog.extend([main_pre, everything_else, main_post].iter().cloned());
-        line_offset += 1;
-    }
-
-    debug!("final doctest:\n{}", prog);
-
-    (prog, line_offset)
-}
-
-// FIXME(aburka): use a real parser to deal with multiline attributes
-fn partition_source(s: &str) -> (String, String, String) {
-    #[derive(Copy, Clone, PartialEq)]
-    enum PartitionState {
-        Attrs,
-        Crates,
-        Other,
-    }
-    let mut state = PartitionState::Attrs;
-    let mut before = String::new();
-    let mut crates = String::new();
-    let mut after = String::new();
-
-    for line in s.lines() {
-        let trimline = line.trim();
-
-        // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be
-        // shunted into "everything else"
-        match state {
-            PartitionState::Attrs => {
-                state = if trimline.starts_with("#![")
-                    || trimline.chars().all(|c| c.is_whitespace())
-                    || (trimline.starts_with("//") && !trimline.starts_with("///"))
-                {
-                    PartitionState::Attrs
-                } else if trimline.starts_with("extern crate")
-                    || trimline.starts_with("#[macro_use] extern crate")
-                {
-                    PartitionState::Crates
-                } else {
-                    PartitionState::Other
-                };
-            }
-            PartitionState::Crates => {
-                state = if trimline.starts_with("extern crate")
-                    || trimline.starts_with("#[macro_use] extern crate")
-                    || trimline.chars().all(|c| c.is_whitespace())
-                    || (trimline.starts_with("//") && !trimline.starts_with("///"))
-                {
-                    PartitionState::Crates
-                } else {
-                    PartitionState::Other
-                };
-            }
-            PartitionState::Other => {}
-        }
-
-        match state {
-            PartitionState::Attrs => {
-                before.push_str(line);
-                before.push_str("\n");
-            }
-            PartitionState::Crates => {
-                crates.push_str(line);
-                crates.push_str("\n");
-            }
-            PartitionState::Other => {
-                after.push_str(line);
-                after.push_str("\n");
-            }
-        }
-    }
-
-    debug!("before:\n{}", before);
-    debug!("crates:\n{}", crates);
-    debug!("after:\n{}", after);
-
-    (before, after, crates)
-}
-
-pub trait Tester {
-    fn add_test(&mut self, test: String, config: LangString, line: usize);
-    fn get_line(&self) -> usize {
-        0
-    }
-    fn register_header(&mut self, _name: &str, _level: u32) {}
-}
-
-pub struct Collector {
-    pub tests: Vec<testing::TestDescAndFn>,
-
-    // The name of the test displayed to the user, separated by `::`.
-    //
-    // In tests from Rust source, this is the path to the item
-    // e.g., `["std", "vec", "Vec", "push"]`.
-    //
-    // In tests from a markdown file, this is the titles of all headers (h1~h6)
-    // of the sections that contain the code block, e.g., if the markdown file is
-    // written as:
-    //
-    // ``````markdown
-    // # Title
-    //
-    // ## Subtitle
-    //
-    // ```rust
-    // assert!(true);
-    // ```
-    // ``````
-    //
-    // the `names` vector of that test will be `["Title", "Subtitle"]`.
-    names: Vec<String>,
-
-    options: Options,
-    use_headers: bool,
-    enable_per_target_ignores: bool,
-    cratename: String,
-    opts: TestOptions,
-    position: Span,
-    source_map: Option<Lrc<SourceMap>>,
-    filename: Option<PathBuf>,
-    visited_tests: HashMap<(String, usize), usize>,
-}
-
-impl Collector {
-    pub fn new(
-        cratename: String,
-        options: Options,
-        use_headers: bool,
-        opts: TestOptions,
-        source_map: Option<Lrc<SourceMap>>,
-        filename: Option<PathBuf>,
-        enable_per_target_ignores: bool,
-    ) -> Collector {
-        Collector {
-            tests: Vec::new(),
-            names: Vec::new(),
-            options,
-            use_headers,
-            enable_per_target_ignores,
-            cratename,
-            opts,
-            position: DUMMY_SP,
-            source_map,
-            filename,
-            visited_tests: HashMap::new(),
-        }
-    }
-
-    fn generate_name(&self, line: usize, filename: &FileName) -> String {
-        let mut item_path = self.names.join("::");
-        if !item_path.is_empty() {
-            item_path.push(' ');
-        }
-        format!("{} - {}(line {})", filename, item_path, line)
-    }
-
-    pub fn set_position(&mut self, position: Span) {
-        self.position = position;
-    }
-
-    fn get_filename(&self) -> FileName {
-        if let Some(ref source_map) = self.source_map {
-            let filename = source_map.span_to_filename(self.position);
-            if let FileName::Real(ref filename) = filename {
-                if let Ok(cur_dir) = env::current_dir() {
-                    if let Ok(path) = filename.local_path().strip_prefix(&cur_dir) {
-                        return path.to_owned().into();
-                    }
-                }
-            }
-            filename
-        } else if let Some(ref filename) = self.filename {
-            filename.clone().into()
-        } else {
-            FileName::Custom("input".to_owned())
-        }
-    }
-}
-
-impl Tester for Collector {
-    fn add_test(&mut self, test: String, config: LangString, line: usize) {
-        let filename = self.get_filename();
-        let name = self.generate_name(line, &filename);
-        let cratename = self.cratename.to_string();
-        let opts = self.opts.clone();
-        let edition = config.edition.unwrap_or(self.options.edition);
-        let options = self.options.clone();
-        let runtool = self.options.runtool.clone();
-        let runtool_args = self.options.runtool_args.clone();
-        let target = self.options.target.clone();
-        let target_str = target.to_string();
-
-        // FIXME(#44940): if doctests ever support path remapping, then this filename
-        // needs to be the result of `SourceMap::span_to_unmapped_path`.
-        let path = match &filename {
-            FileName::Real(path) => path.local_path().to_path_buf(),
-            _ => PathBuf::from(r"doctest.rs"),
-        };
-
-        let outdir = if let Some(mut path) = options.persist_doctests.clone() {
-            // For example `module/file.rs` would become `module_file_rs`
-            let folder_name = filename
-                .to_string()
-                .chars()
-                .map(|c| if c == '/' || c == '.' { '_' } else { c })
-                .collect::<String>();
-
-            path.push(format!(
-                "{name}_{line}_{number}",
-                name = folder_name,
-                number = {
-                    // Increases the current test number, if this file already
-                    // exists or it creates a new entry with a test number of 0.
-                    self.visited_tests
-                        .entry((folder_name.clone(), line))
-                        .and_modify(|v| *v += 1)
-                        .or_insert(0)
-                },
-                line = line,
-            ));
-
-            std::fs::create_dir_all(&path)
-                .expect("Couldn't create directory for doctest executables");
-
-            DirState::Perm(path)
-        } else {
-            DirState::Temp(
-                TempFileBuilder::new()
-                    .prefix("rustdoctest")
-                    .tempdir()
-                    .expect("rustdoc needs a tempdir"),
-            )
-        };
-
-        debug!("creating test {}: {}", name, test);
-        self.tests.push(testing::TestDescAndFn {
-            desc: testing::TestDesc {
-                name: testing::DynTestName(name),
-                ignore: match config.ignore {
-                    Ignore::All => true,
-                    Ignore::None => false,
-                    Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)),
-                },
-                // compiler failures are test failures
-                should_panic: testing::ShouldPanic::No,
-                allow_fail: config.allow_fail,
-                test_type: testing::TestType::DocTest,
-            },
-            testfn: testing::DynTestFn(box move || {
-                let res = run_test(
-                    &test,
-                    &cratename,
-                    line,
-                    options,
-                    config.should_panic,
-                    config.no_run,
-                    config.test_harness,
-                    runtool,
-                    runtool_args,
-                    target,
-                    config.compile_fail,
-                    config.error_codes,
-                    &opts,
-                    edition,
-                    outdir,
-                    path,
-                );
-
-                if let Err(err) = res {
-                    match err {
-                        TestFailure::CompileError => {
-                            eprint!("Couldn't compile the test.");
-                        }
-                        TestFailure::UnexpectedCompilePass => {
-                            eprint!("Test compiled successfully, but it's marked `compile_fail`.");
-                        }
-                        TestFailure::UnexpectedRunPass => {
-                            eprint!("Test executable succeeded, but it's marked `should_panic`.");
-                        }
-                        TestFailure::MissingErrorCodes(codes) => {
-                            eprint!("Some expected error codes were not found: {:?}", codes);
-                        }
-                        TestFailure::ExecutionError(err) => {
-                            eprint!("Couldn't run the test: {}", err);
-                            if err.kind() == io::ErrorKind::PermissionDenied {
-                                eprint!(" - maybe your tempdir is mounted with noexec?");
-                            }
-                        }
-                        TestFailure::ExecutionFailure(out) => {
-                            let reason = if let Some(code) = out.status.code() {
-                                format!("exit code {}", code)
-                            } else {
-                                String::from("terminated by signal")
-                            };
-
-                            eprintln!("Test executable failed ({}).", reason);
-
-                            // FIXME(#12309): An unfortunate side-effect of capturing the test
-                            // executable's output is that the relative ordering between the test's
-                            // stdout and stderr is lost. However, this is better than the
-                            // alternative: if the test executable inherited the parent's I/O
-                            // handles the output wouldn't be captured at all, even on success.
-                            //
-                            // The ordering could be preserved if the test process' stderr was
-                            // redirected to stdout, but that functionality does not exist in the
-                            // standard library, so it may not be portable enough.
-                            let stdout = str::from_utf8(&out.stdout).unwrap_or_default();
-                            let stderr = str::from_utf8(&out.stderr).unwrap_or_default();
-
-                            if !stdout.is_empty() || !stderr.is_empty() {
-                                eprintln!();
-
-                                if !stdout.is_empty() {
-                                    eprintln!("stdout:\n{}", stdout);
-                                }
-
-                                if !stderr.is_empty() {
-                                    eprintln!("stderr:\n{}", stderr);
-                                }
-                            }
-                        }
-                    }
-
-                    panic::resume_unwind(box ());
-                }
-            }),
-        });
-    }
-
-    fn get_line(&self) -> usize {
-        if let Some(ref source_map) = self.source_map {
-            let line = self.position.lo().to_usize();
-            let line = source_map.lookup_char_pos(BytePos(line as u32)).line;
-            if line > 0 { line - 1 } else { line }
-        } else {
-            0
-        }
-    }
-
-    fn register_header(&mut self, name: &str, level: u32) {
-        if self.use_headers {
-            // We use these headings as test names, so it's good if
-            // they're valid identifiers.
-            let name = name
-                .chars()
-                .enumerate()
-                .map(|(i, c)| {
-                    if (i == 0 && rustc_lexer::is_id_start(c))
-                        || (i != 0 && rustc_lexer::is_id_continue(c))
-                    {
-                        c
-                    } else {
-                        '_'
-                    }
-                })
-                .collect::<String>();
-
-            // Here we try to efficiently assemble the header titles into the
-            // test name in the form of `h1::h2::h3::h4::h5::h6`.
-            //
-            // Suppose that originally `self.names` contains `[h1, h2, h3]`...
-            let level = level as usize;
-            if level <= self.names.len() {
-                // ... Consider `level == 2`. All headers in the lower levels
-                // are irrelevant in this new level. So we should reset
-                // `self.names` to contain headers until <h2>, and replace that
-                // slot with the new name: `[h1, name]`.
-                self.names.truncate(level);
-                self.names[level - 1] = name;
-            } else {
-                // ... On the other hand, consider `level == 5`. This means we
-                // need to extend `self.names` to contain five headers. We fill
-                // in the missing level (<h4>) with `_`. Thus `self.names` will
-                // become `[h1, h2, h3, "_", name]`.
-                if level - 1 > self.names.len() {
-                    self.names.resize(level - 1, "_".to_owned());
-                }
-                self.names.push(name);
-            }
-        }
-    }
-}
-
-struct HirCollector<'a, 'hir, 'tcx> {
-    sess: &'a Session,
-    collector: &'a mut Collector,
-    map: Map<'hir>,
-    codes: ErrorCodes,
-    tcx: TyCtxt<'tcx>,
-}
-
-impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
-    fn visit_testable<F: FnOnce(&mut Self)>(
-        &mut self,
-        name: String,
-        attrs: &[ast::Attribute],
-        hir_id: HirId,
-        sp: Span,
-        nested: F,
-    ) {
-        let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs);
-        if let Some(ref cfg) = attrs.cfg {
-            if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
-                return;
-            }
-        }
-
-        let has_name = !name.is_empty();
-        if has_name {
-            self.collector.names.push(name);
-        }
-
-        attrs.collapse_doc_comments();
-        attrs.unindent_doc_comments();
-        // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
-        // anything else, this will combine them for us.
-        if let Some(doc) = attrs.collapsed_doc_value() {
-            // Use the outermost invocation, so that doctest names come from where the docs were written.
-            let span = attrs
-                .span
-                .map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
-                .unwrap_or(DUMMY_SP);
-            self.collector.set_position(span);
-            markdown::find_testable_code(
-                &doc,
-                self.collector,
-                self.codes,
-                self.collector.enable_per_target_ignores,
-                Some(&crate::html::markdown::ExtraInfo::new(
-                    &self.tcx,
-                    hir_id,
-                    span_of_attrs(&attrs).unwrap_or(sp),
-                )),
-            );
-        }
-
-        nested(self);
-
-        if has_name {
-            self.collector.names.pop();
-        }
-    }
-}
-
-impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> {
-    type Map = Map<'hir>;
-
-    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-        intravisit::NestedVisitorMap::All(self.map)
-    }
-
-    fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
-        let name = if let hir::ItemKind::Impl { ref self_ty, .. } = item.kind {
-            rustc_hir_pretty::id_to_string(&self.map, self_ty.hir_id)
-        } else {
-            item.ident.to_string()
-        };
-
-        self.visit_testable(name, &item.attrs, item.hir_id, item.span, |this| {
-            intravisit::walk_item(this, item);
-        });
-    }
-
-    fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) {
-        self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
-            intravisit::walk_trait_item(this, item);
-        });
-    }
-
-    fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) {
-        self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
-            intravisit::walk_impl_item(this, item);
-        });
-    }
-
-    fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) {
-        self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
-            intravisit::walk_foreign_item(this, item);
-        });
-    }
-
-    fn visit_variant(
-        &mut self,
-        v: &'hir hir::Variant<'_>,
-        g: &'hir hir::Generics<'_>,
-        item_id: hir::HirId,
-    ) {
-        self.visit_testable(v.ident.to_string(), &v.attrs, v.id, v.span, |this| {
-            intravisit::walk_variant(this, v, g, item_id);
-        });
-    }
-
-    fn visit_struct_field(&mut self, f: &'hir hir::StructField<'_>) {
-        self.visit_testable(f.ident.to_string(), &f.attrs, f.hir_id, f.span, |this| {
-            intravisit::walk_struct_field(this, f);
-        });
-    }
-
-    fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef<'_>) {
-        self.visit_testable(
-            macro_def.ident.to_string(),
-            &macro_def.attrs,
-            macro_def.hir_id,
-            macro_def.span,
-            |_| (),
-        );
-    }
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/src/librustdoc/test/tests.rs b/src/librustdoc/test/tests.rs
deleted file mode 100644 (file)
index a96186a..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-use super::{make_test, TestOptions};
-use rustc_span::edition::DEFAULT_EDITION;
-
-#[test]
-fn make_test_basic() {
-    //basic use: wraps with `fn main`, adds `#![allow(unused)]`
-    let opts = TestOptions::default();
-    let input = "assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-fn main() {
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-}
-
-#[test]
-fn make_test_crate_name_no_use() {
-    // If you give a crate name but *don't* use it within the test, it won't bother inserting
-    // the `extern crate` statement.
-    let opts = TestOptions::default();
-    let input = "assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-fn main() {
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-}
-
-#[test]
-fn make_test_crate_name() {
-    // If you give a crate name and use it within the test, it will insert an `extern crate`
-    // statement before `fn main`.
-    let opts = TestOptions::default();
-    let input = "use asdf::qwop;
-assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-extern crate asdf;
-fn main() {
-use asdf::qwop;
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 3));
-}
-
-#[test]
-fn make_test_no_crate_inject() {
-    // Even if you do use the crate within the test, setting `opts.no_crate_inject` will skip
-    // adding it anyway.
-    let opts = TestOptions { no_crate_inject: true, display_warnings: false, attrs: vec![] };
-    let input = "use asdf::qwop;
-assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-fn main() {
-use asdf::qwop;
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-}
-
-#[test]
-fn make_test_ignore_std() {
-    // Even if you include a crate name, and use it in the doctest, we still won't include an
-    // `extern crate` statement if the crate is "std" -- that's included already by the
-    // compiler!
-    let opts = TestOptions::default();
-    let input = "use std::*;
-assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-fn main() {
-use std::*;
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-}
-
-#[test]
-fn make_test_manual_extern_crate() {
-    // When you manually include an `extern crate` statement in your doctest, `make_test`
-    // assumes you've included one for your own crate too.
-    let opts = TestOptions::default();
-    let input = "extern crate asdf;
-use asdf::qwop;
-assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-extern crate asdf;
-fn main() {
-use asdf::qwop;
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-}
-
-#[test]
-fn make_test_manual_extern_crate_with_macro_use() {
-    let opts = TestOptions::default();
-    let input = "#[macro_use] extern crate asdf;
-use asdf::qwop;
-assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-#[macro_use] extern crate asdf;
-fn main() {
-use asdf::qwop;
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-}
-
-#[test]
-fn make_test_opts_attrs() {
-    // If you supplied some doctest attributes with `#![doc(test(attr(...)))]`, it will use
-    // those instead of the stock `#![allow(unused)]`.
-    let mut opts = TestOptions::default();
-    opts.attrs.push("feature(sick_rad)".to_string());
-    let input = "use asdf::qwop;
-assert_eq!(2+2, 4);";
-    let expected = "#![feature(sick_rad)]
-extern crate asdf;
-fn main() {
-use asdf::qwop;
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 3));
-
-    // Adding more will also bump the returned line offset.
-    opts.attrs.push("feature(hella_dope)".to_string());
-    let expected = "#![feature(sick_rad)]
-#![feature(hella_dope)]
-extern crate asdf;
-fn main() {
-use asdf::qwop;
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 4));
-}
-
-#[test]
-fn make_test_crate_attrs() {
-    // Including inner attributes in your doctest will apply them to the whole "crate", pasting
-    // them outside the generated main function.
-    let opts = TestOptions::default();
-    let input = "#![feature(sick_rad)]
-assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-#![feature(sick_rad)]
-fn main() {
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-}
-
-#[test]
-fn make_test_with_main() {
-    // Including your own `fn main` wrapper lets the test use it verbatim.
-    let opts = TestOptions::default();
-    let input = "fn main() {
-    assert_eq!(2+2, 4);
-}";
-    let expected = "#![allow(unused)]
-fn main() {
-    assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 1));
-}
-
-#[test]
-fn make_test_fake_main() {
-    // ... but putting it in a comment will still provide a wrapper.
-    let opts = TestOptions::default();
-    let input = "//Ceci n'est pas une `fn main`
-assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-//Ceci n'est pas une `fn main`
-fn main() {
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-}
-
-#[test]
-fn make_test_dont_insert_main() {
-    // Even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper.
-    let opts = TestOptions::default();
-    let input = "//Ceci n'est pas une `fn main`
-assert_eq!(2+2, 4);";
-    let expected = "#![allow(unused)]
-//Ceci n'est pas une `fn main`
-assert_eq!(2+2, 4);"
-        .to_string();
-    let output = make_test(input, None, true, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 1));
-}
-
-#[test]
-fn make_test_display_warnings() {
-    // If the user is asking to display doctest warnings, suppress the default `allow(unused)`.
-    let mut opts = TestOptions::default();
-    opts.display_warnings = true;
-    let input = "assert_eq!(2+2, 4);";
-    let expected = "fn main() {
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 1));
-}
-
-#[test]
-fn make_test_issues_21299_33731() {
-    let opts = TestOptions::default();
-
-    let input = "// fn main
-assert_eq!(2+2, 4);";
-
-    let expected = "#![allow(unused)]
-// fn main
-fn main() {
-assert_eq!(2+2, 4);
-}"
-    .to_string();
-
-    let output = make_test(input, None, false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 2));
-
-    let input = "extern crate hella_qwop;
-assert_eq!(asdf::foo, 4);";
-
-    let expected = "#![allow(unused)]
-extern crate hella_qwop;
-extern crate asdf;
-fn main() {
-assert_eq!(asdf::foo, 4);
-}"
-    .to_string();
-
-    let output = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 3));
-}
-
-#[test]
-fn make_test_main_in_macro() {
-    let opts = TestOptions::default();
-    let input = "#[macro_use] extern crate my_crate;
-test_wrapper! {
-    fn main() {}
-}";
-    let expected = "#![allow(unused)]
-#[macro_use] extern crate my_crate;
-test_wrapper! {
-    fn main() {}
-}"
-    .to_string();
-
-    let output = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION);
-    assert_eq!(output, (expected, 1));
-}
index 4234ce4bac07dd84a56c8ef32ce2d17ec0ed8fb0..f695b75c24d114e703996b8261cf12543fe53549 100644 (file)
@@ -12,7 +12,7 @@
 # source tarball for a stable release you'll likely see `1.x.0` for rustc and
 # `0.(x+1).0` for Cargo where they were released on `date`.
 
-date: 2020-07-16
+date: 2020-08-26
 rustc: beta
 cargo: beta
 
index e39f74c916c4ad4a7a233a10c701b2ba1ec39621..8dd1f3c873ff51613f9c3d07bb2d81456af39839 100644 (file)
@@ -96,6 +96,17 @@ pub unsafe fn sym_static() {
     asm!("adr x0, {}", sym extern_static);
 }
 
+// Regression test for #75761
+// CHECK-LABEL: issue_75761:
+// CHECK: str {{.*}}x30
+// CHECK: //APP
+// CHECK: //NO_APP
+// CHECK: ldr {{.*}}x30
+#[no_mangle]
+pub unsafe fn issue_75761() {
+    asm!("", out("v0") _, out("x30") _);
+}
+
 macro_rules! check {
     ($func:ident $ty:ident $class:ident $mov:literal $modifier:literal) => {
         #[no_mangle]
@@ -553,8 +564,3 @@ pub unsafe fn $func(x: $ty) -> $ty {
 // CHECK: fmov s0, s0
 // CHECK: //NO_APP
 check_reg!(v0_f64x2 f64x2 "s0" "fmov");
-
-// Regression test for #75761
-pub unsafe fn issue_75761() {
-    asm!("", out("v0") _, out("x30") _);
-}
index 6d25ca56f1488340855427af287ed4f724e0f2da..0f15729158df16399717e0b85864b7ab09bd0426 100644 (file)
@@ -1,4 +1,4 @@
-// compile-flags: -O --target=avr-unknown-unknown --crate-type=rlib
+// compile-flags: -O --target=avr-unknown-gnu-atmega328 --crate-type=rlib
 // needs-llvm-components: avr
 
 // This test validates that function pointers can be stored in global variables
index 3e5cdad7ab936b865fc98b782a5b5d6ff9b22e30..b9ef2f329414e8db0ca087e0bb5fac2c41bd87d9 100644 (file)
@@ -11,3 +11,5 @@
 fn panic_impl(info: &PanicInfo) -> ! { loop {} }
 #[lang = "eh_personality"]
 fn eh_personality() {}
+#[lang = "eh_catch_typeinfo"]
+static EH_CATCH_TYPEINFO: u8 = 0;
diff --git a/src/test/mir-opt/if-condition-int.rs b/src/test/mir-opt/if-condition-int.rs
new file mode 100644 (file)
index 0000000..b34389a
--- /dev/null
@@ -0,0 +1,65 @@
+// compile-flags: -O
+// EMIT_MIR if_condition_int.opt_u32.SimplifyComparisonIntegral.diff
+// EMIT_MIR if_condition_int.opt_negative.SimplifyComparisonIntegral.diff
+// EMIT_MIR if_condition_int.opt_char.SimplifyComparisonIntegral.diff
+// EMIT_MIR if_condition_int.opt_i8.SimplifyComparisonIntegral.diff
+// EMIT_MIR if_condition_int.dont_opt_bool.SimplifyComparisonIntegral.diff
+// EMIT_MIR if_condition_int.opt_multiple_ifs.SimplifyComparisonIntegral.diff
+// EMIT_MIR if_condition_int.dont_remove_comparison.SimplifyComparisonIntegral.diff
+// EMIT_MIR if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff
+
+fn opt_u32(x: u32) -> u32 {
+    if x == 42 { 0 } else { 1 }
+}
+
+// don't opt: it is already optimal to switch on the bool
+fn dont_opt_bool(x: bool) -> u32 {
+    if x { 0 } else { 1 }
+}
+
+fn opt_char(x: char) -> u32 {
+    if x == 'x' { 0 } else { 1 }
+}
+
+fn opt_i8(x: i8) -> u32 {
+    if x == 42 { 0 } else { 1 }
+}
+
+fn opt_negative(x: i32) -> u32 {
+    if x == -42 { 0 } else { 1 }
+}
+
+fn opt_multiple_ifs(x: u32) -> u32 {
+    if x == 42 {
+        0
+    } else if x != 21 {
+        1
+    } else {
+        2
+    }
+}
+
+// test that we optimize, but do not remove the b statement, as that is used later on
+fn dont_remove_comparison(a: i8) -> i32 {
+    let b = a == 17;
+    match b {
+        false => 10 + b as i32,
+        true => 100 + b as i32,
+    }
+}
+
+// test that we do not optimize on floats
+fn dont_opt_floats(a: f32) -> i32 {
+    if a == -42.0 { 0 } else { 1 }
+}
+
+fn main() {
+    opt_u32(0);
+    opt_char('0');
+    opt_i8(22);
+    dont_opt_bool(false);
+    opt_negative(0);
+    opt_multiple_ifs(0);
+    dont_remove_comparison(11);
+    dont_opt_floats(1.0);
+}
diff --git a/src/test/mir-opt/if_condition_int.dont_opt_bool.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.dont_opt_bool.SimplifyComparisonIntegral.diff
new file mode 100644 (file)
index 0000000..993ff66
--- /dev/null
@@ -0,0 +1,30 @@
+- // MIR for `dont_opt_bool` before SimplifyComparisonIntegral
++ // MIR for `dont_opt_bool` after SimplifyComparisonIntegral
+  
+  fn dont_opt_bool(_1: bool) -> u32 {
+      debug x => _1;                       // in scope 0 at $DIR/if-condition-int.rs:16:18: 16:19
+      let mut _0: u32;                     // return place in scope 0 at $DIR/if-condition-int.rs:16:30: 16:33
+      let mut _2: bool;                    // in scope 0 at $DIR/if-condition-int.rs:17:8: 17:9
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/if-condition-int.rs:17:8: 17:9
+          _2 = _1;                         // scope 0 at $DIR/if-condition-int.rs:17:8: 17:9
+          switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:17:5: 17:26
+      }
+  
+      bb1: {
+          _0 = const 1_u32;                // scope 0 at $DIR/if-condition-int.rs:17:23: 17:24
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:17:5: 17:26
+      }
+  
+      bb2: {
+          _0 = const 0_u32;                // scope 0 at $DIR/if-condition-int.rs:17:12: 17:13
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:17:5: 17:26
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/if-condition-int.rs:18:1: 18:2
+          return;                          // scope 0 at $DIR/if-condition-int.rs:18:2: 18:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff
new file mode 100644 (file)
index 0000000..8ae9168
--- /dev/null
@@ -0,0 +1,37 @@
+- // MIR for `dont_opt_floats` before SimplifyComparisonIntegral
++ // MIR for `dont_opt_floats` after SimplifyComparisonIntegral
+  
+  fn dont_opt_floats(_1: f32) -> i32 {
+      debug a => _1;                       // in scope 0 at $DIR/if-condition-int.rs:52:20: 52:21
+      let mut _0: i32;                     // return place in scope 0 at $DIR/if-condition-int.rs:52:31: 52:34
+      let mut _2: bool;                    // in scope 0 at $DIR/if-condition-int.rs:53:8: 53:18
+      let mut _3: f32;                     // in scope 0 at $DIR/if-condition-int.rs:53:8: 53:9
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/if-condition-int.rs:53:8: 53:18
+          StorageLive(_3);                 // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9
+          _3 = _1;                         // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9
+          _2 = Eq(move _3, const -42f32);  // scope 0 at $DIR/if-condition-int.rs:53:8: 53:18
+                                           // mir::Constant
+                                           // + span: $DIR/if-condition-int.rs:53:13: 53:18
+                                           // + literal: Const { ty: f32, val: Value(Scalar(0xc2280000)) }
+          StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:53:17: 53:18
+          switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35
+      }
+  
+      bb1: {
+          _0 = const 1_i32;                // scope 0 at $DIR/if-condition-int.rs:53:32: 53:33
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35
+      }
+  
+      bb2: {
+          _0 = const 0_i32;                // scope 0 at $DIR/if-condition-int.rs:53:21: 53:22
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/if-condition-int.rs:54:1: 54:2
+          return;                          // scope 0 at $DIR/if-condition-int.rs:54:2: 54:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/if_condition_int.dont_remove_comparison.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.dont_remove_comparison.SimplifyComparisonIntegral.diff
new file mode 100644 (file)
index 0000000..b590be5
--- /dev/null
@@ -0,0 +1,58 @@
+- // MIR for `dont_remove_comparison` before SimplifyComparisonIntegral
++ // MIR for `dont_remove_comparison` after SimplifyComparisonIntegral
+  
+  fn dont_remove_comparison(_1: i8) -> i32 {
+      debug a => _1;                       // in scope 0 at $DIR/if-condition-int.rs:43:27: 43:28
+      let mut _0: i32;                     // return place in scope 0 at $DIR/if-condition-int.rs:43:37: 43:40
+      let _2: bool;                        // in scope 0 at $DIR/if-condition-int.rs:44:9: 44:10
+      let mut _3: i8;                      // in scope 0 at $DIR/if-condition-int.rs:44:13: 44:14
+      let mut _4: i32;                     // in scope 0 at $DIR/if-condition-int.rs:46:23: 46:31
+      let mut _5: bool;                    // in scope 0 at $DIR/if-condition-int.rs:46:23: 46:24
+      let mut _6: i32;                     // in scope 0 at $DIR/if-condition-int.rs:47:23: 47:31
+      let mut _7: bool;                    // in scope 0 at $DIR/if-condition-int.rs:47:23: 47:24
+      scope 1 {
+          debug b => _2;                   // in scope 1 at $DIR/if-condition-int.rs:44:9: 44:10
+      }
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/if-condition-int.rs:44:9: 44:10
+          StorageLive(_3);                 // scope 0 at $DIR/if-condition-int.rs:44:13: 44:14
+          _3 = _1;                         // scope 0 at $DIR/if-condition-int.rs:44:13: 44:14
+-         _2 = Eq(move _3, const 17_i8);   // scope 0 at $DIR/if-condition-int.rs:44:13: 44:20
+-         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:44:19: 44:20
+-         switchInt(_2) -> [false: bb2, otherwise: bb1]; // scope 1 at $DIR/if-condition-int.rs:46:9: 46:14
++         _2 = Eq(_3, const 17_i8);        // scope 0 at $DIR/if-condition-int.rs:44:13: 44:20
++         nop;                             // scope 0 at $DIR/if-condition-int.rs:44:19: 44:20
++         switchInt(move _3) -> [17_i8: bb1, otherwise: bb2]; // scope 1 at $DIR/if-condition-int.rs:46:9: 46:14
+      }
+  
+      bb1: {
++         StorageDead(_3);                 // scope 1 at $DIR/if-condition-int.rs:46:9: 46:14
+          StorageLive(_6);                 // scope 1 at $DIR/if-condition-int.rs:47:23: 47:31
+          StorageLive(_7);                 // scope 1 at $DIR/if-condition-int.rs:47:23: 47:24
+          _7 = _2;                         // scope 1 at $DIR/if-condition-int.rs:47:23: 47:24
+          _6 = move _7 as i32 (Misc);      // scope 1 at $DIR/if-condition-int.rs:47:23: 47:31
+          StorageDead(_7);                 // scope 1 at $DIR/if-condition-int.rs:47:30: 47:31
+          _0 = Add(const 100_i32, move _6); // scope 1 at $DIR/if-condition-int.rs:47:17: 47:31
+          StorageDead(_6);                 // scope 1 at $DIR/if-condition-int.rs:47:30: 47:31
+          goto -> bb3;                     // scope 1 at $DIR/if-condition-int.rs:45:5: 48:6
+      }
+  
+      bb2: {
++         StorageDead(_3);                 // scope 1 at $DIR/if-condition-int.rs:46:9: 46:14
+          StorageLive(_4);                 // scope 1 at $DIR/if-condition-int.rs:46:23: 46:31
+          StorageLive(_5);                 // scope 1 at $DIR/if-condition-int.rs:46:23: 46:24
+          _5 = _2;                         // scope 1 at $DIR/if-condition-int.rs:46:23: 46:24
+          _4 = move _5 as i32 (Misc);      // scope 1 at $DIR/if-condition-int.rs:46:23: 46:31
+          StorageDead(_5);                 // scope 1 at $DIR/if-condition-int.rs:46:30: 46:31
+          _0 = Add(const 10_i32, move _4); // scope 1 at $DIR/if-condition-int.rs:46:18: 46:31
+          StorageDead(_4);                 // scope 1 at $DIR/if-condition-int.rs:46:30: 46:31
+          goto -> bb3;                     // scope 1 at $DIR/if-condition-int.rs:45:5: 48:6
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/if-condition-int.rs:49:1: 49:2
+          return;                          // scope 0 at $DIR/if-condition-int.rs:49:2: 49:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/if_condition_int.opt_char.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_char.SimplifyComparisonIntegral.diff
new file mode 100644 (file)
index 0000000..ae09600
--- /dev/null
@@ -0,0 +1,39 @@
+- // MIR for `opt_char` before SimplifyComparisonIntegral
++ // MIR for `opt_char` after SimplifyComparisonIntegral
+  
+  fn opt_char(_1: char) -> u32 {
+      debug x => _1;                       // in scope 0 at $DIR/if-condition-int.rs:20:13: 20:14
+      let mut _0: u32;                     // return place in scope 0 at $DIR/if-condition-int.rs:20:25: 20:28
+      let mut _2: bool;                    // in scope 0 at $DIR/if-condition-int.rs:21:8: 21:16
+      let mut _3: char;                    // in scope 0 at $DIR/if-condition-int.rs:21:8: 21:9
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/if-condition-int.rs:21:8: 21:16
+          StorageLive(_3);                 // scope 0 at $DIR/if-condition-int.rs:21:8: 21:9
+          _3 = _1;                         // scope 0 at $DIR/if-condition-int.rs:21:8: 21:9
+-         _2 = Eq(move _3, const 'x');     // scope 0 at $DIR/if-condition-int.rs:21:8: 21:16
+-         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:21:15: 21:16
+-         switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33
++         _2 = Eq(_3, const 'x');          // scope 0 at $DIR/if-condition-int.rs:21:8: 21:16
++         nop;                             // scope 0 at $DIR/if-condition-int.rs:21:15: 21:16
++         switchInt(move _3) -> ['x': bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33
+      }
+  
+      bb1: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33
+          _0 = const 1_u32;                // scope 0 at $DIR/if-condition-int.rs:21:30: 21:31
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33
+      }
+  
+      bb2: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33
+          _0 = const 0_u32;                // scope 0 at $DIR/if-condition-int.rs:21:19: 21:20
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/if-condition-int.rs:22:1: 22:2
+          return;                          // scope 0 at $DIR/if-condition-int.rs:22:2: 22:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/if_condition_int.opt_i8.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_i8.SimplifyComparisonIntegral.diff
new file mode 100644 (file)
index 0000000..8d59e51
--- /dev/null
@@ -0,0 +1,39 @@
+- // MIR for `opt_i8` before SimplifyComparisonIntegral
++ // MIR for `opt_i8` after SimplifyComparisonIntegral
+  
+  fn opt_i8(_1: i8) -> u32 {
+      debug x => _1;                       // in scope 0 at $DIR/if-condition-int.rs:24:11: 24:12
+      let mut _0: u32;                     // return place in scope 0 at $DIR/if-condition-int.rs:24:21: 24:24
+      let mut _2: bool;                    // in scope 0 at $DIR/if-condition-int.rs:25:8: 25:15
+      let mut _3: i8;                      // in scope 0 at $DIR/if-condition-int.rs:25:8: 25:9
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/if-condition-int.rs:25:8: 25:15
+          StorageLive(_3);                 // scope 0 at $DIR/if-condition-int.rs:25:8: 25:9
+          _3 = _1;                         // scope 0 at $DIR/if-condition-int.rs:25:8: 25:9
+-         _2 = Eq(move _3, const 42_i8);   // scope 0 at $DIR/if-condition-int.rs:25:8: 25:15
+-         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:25:14: 25:15
+-         switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32
++         _2 = Eq(_3, const 42_i8);        // scope 0 at $DIR/if-condition-int.rs:25:8: 25:15
++         nop;                             // scope 0 at $DIR/if-condition-int.rs:25:14: 25:15
++         switchInt(move _3) -> [42_i8: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32
+      }
+  
+      bb1: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32
+          _0 = const 1_u32;                // scope 0 at $DIR/if-condition-int.rs:25:29: 25:30
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32
+      }
+  
+      bb2: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32
+          _0 = const 0_u32;                // scope 0 at $DIR/if-condition-int.rs:25:18: 25:19
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/if-condition-int.rs:26:1: 26:2
+          return;                          // scope 0 at $DIR/if-condition-int.rs:26:2: 26:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/if_condition_int.opt_multiple_ifs.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_multiple_ifs.SimplifyComparisonIntegral.diff
new file mode 100644 (file)
index 0000000..c497566
--- /dev/null
@@ -0,0 +1,65 @@
+- // MIR for `opt_multiple_ifs` before SimplifyComparisonIntegral
++ // MIR for `opt_multiple_ifs` after SimplifyComparisonIntegral
+  
+  fn opt_multiple_ifs(_1: u32) -> u32 {
+      debug x => _1;                       // in scope 0 at $DIR/if-condition-int.rs:32:21: 32:22
+      let mut _0: u32;                     // return place in scope 0 at $DIR/if-condition-int.rs:32:32: 32:35
+      let mut _2: bool;                    // in scope 0 at $DIR/if-condition-int.rs:33:8: 33:15
+      let mut _3: u32;                     // in scope 0 at $DIR/if-condition-int.rs:33:8: 33:9
+      let mut _4: bool;                    // in scope 0 at $DIR/if-condition-int.rs:35:15: 35:22
+      let mut _5: u32;                     // in scope 0 at $DIR/if-condition-int.rs:35:15: 35:16
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/if-condition-int.rs:33:8: 33:15
+          StorageLive(_3);                 // scope 0 at $DIR/if-condition-int.rs:33:8: 33:9
+          _3 = _1;                         // scope 0 at $DIR/if-condition-int.rs:33:8: 33:9
+-         _2 = Eq(move _3, const 42_u32);  // scope 0 at $DIR/if-condition-int.rs:33:8: 33:15
+-         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:33:14: 33:15
+-         switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6
++         _2 = Eq(_3, const 42_u32);       // scope 0 at $DIR/if-condition-int.rs:33:8: 33:15
++         nop;                             // scope 0 at $DIR/if-condition-int.rs:33:14: 33:15
++         switchInt(move _3) -> [42_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6
+      }
+  
+      bb1: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6
+          StorageLive(_4);                 // scope 0 at $DIR/if-condition-int.rs:35:15: 35:22
+          StorageLive(_5);                 // scope 0 at $DIR/if-condition-int.rs:35:15: 35:16
+          _5 = _1;                         // scope 0 at $DIR/if-condition-int.rs:35:15: 35:16
+-         _4 = Ne(move _5, const 21_u32);  // scope 0 at $DIR/if-condition-int.rs:35:15: 35:22
+-         StorageDead(_5);                 // scope 0 at $DIR/if-condition-int.rs:35:21: 35:22
+-         switchInt(_4) -> [false: bb3, otherwise: bb4]; // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6
++         _4 = Ne(_5, const 21_u32);       // scope 0 at $DIR/if-condition-int.rs:35:15: 35:22
++         nop;                             // scope 0 at $DIR/if-condition-int.rs:35:21: 35:22
++         switchInt(move _5) -> [21_u32: bb3, otherwise: bb4]; // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6
+      }
+  
+      bb2: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6
+          _0 = const 0_u32;                // scope 0 at $DIR/if-condition-int.rs:34:9: 34:10
+          goto -> bb6;                     // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6
+      }
+  
+      bb3: {
++         StorageDead(_5);                 // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6
+          _0 = const 2_u32;                // scope 0 at $DIR/if-condition-int.rs:38:9: 38:10
+          goto -> bb5;                     // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6
+      }
+  
+      bb4: {
++         StorageDead(_5);                 // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6
+          _0 = const 1_u32;                // scope 0 at $DIR/if-condition-int.rs:36:9: 36:10
+          goto -> bb5;                     // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6
+      }
+  
+      bb5: {
+          StorageDead(_4);                 // scope 0 at $DIR/if-condition-int.rs:39:5: 39:6
+          goto -> bb6;                     // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6
+      }
+  
+      bb6: {
+          StorageDead(_2);                 // scope 0 at $DIR/if-condition-int.rs:40:1: 40:2
+          return;                          // scope 0 at $DIR/if-condition-int.rs:40:2: 40:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/if_condition_int.opt_negative.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_negative.SimplifyComparisonIntegral.diff
new file mode 100644 (file)
index 0000000..d7f544e
--- /dev/null
@@ -0,0 +1,39 @@
+- // MIR for `opt_negative` before SimplifyComparisonIntegral
++ // MIR for `opt_negative` after SimplifyComparisonIntegral
+  
+  fn opt_negative(_1: i32) -> u32 {
+      debug x => _1;                       // in scope 0 at $DIR/if-condition-int.rs:28:17: 28:18
+      let mut _0: u32;                     // return place in scope 0 at $DIR/if-condition-int.rs:28:28: 28:31
+      let mut _2: bool;                    // in scope 0 at $DIR/if-condition-int.rs:29:8: 29:16
+      let mut _3: i32;                     // in scope 0 at $DIR/if-condition-int.rs:29:8: 29:9
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/if-condition-int.rs:29:8: 29:16
+          StorageLive(_3);                 // scope 0 at $DIR/if-condition-int.rs:29:8: 29:9
+          _3 = _1;                         // scope 0 at $DIR/if-condition-int.rs:29:8: 29:9
+-         _2 = Eq(move _3, const -42_i32); // scope 0 at $DIR/if-condition-int.rs:29:8: 29:16
+-         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:29:15: 29:16
+-         switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33
++         _2 = Eq(_3, const -42_i32);      // scope 0 at $DIR/if-condition-int.rs:29:8: 29:16
++         nop;                             // scope 0 at $DIR/if-condition-int.rs:29:15: 29:16
++         switchInt(move _3) -> [-42_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33
+      }
+  
+      bb1: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33
+          _0 = const 1_u32;                // scope 0 at $DIR/if-condition-int.rs:29:30: 29:31
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33
+      }
+  
+      bb2: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33
+          _0 = const 0_u32;                // scope 0 at $DIR/if-condition-int.rs:29:19: 29:20
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/if-condition-int.rs:30:1: 30:2
+          return;                          // scope 0 at $DIR/if-condition-int.rs:30:2: 30:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/if_condition_int.opt_u32.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_u32.SimplifyComparisonIntegral.diff
new file mode 100644 (file)
index 0000000..51e00e6
--- /dev/null
@@ -0,0 +1,39 @@
+- // MIR for `opt_u32` before SimplifyComparisonIntegral
++ // MIR for `opt_u32` after SimplifyComparisonIntegral
+  
+  fn opt_u32(_1: u32) -> u32 {
+      debug x => _1;                       // in scope 0 at $DIR/if-condition-int.rs:11:12: 11:13
+      let mut _0: u32;                     // return place in scope 0 at $DIR/if-condition-int.rs:11:23: 11:26
+      let mut _2: bool;                    // in scope 0 at $DIR/if-condition-int.rs:12:8: 12:15
+      let mut _3: u32;                     // in scope 0 at $DIR/if-condition-int.rs:12:8: 12:9
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/if-condition-int.rs:12:8: 12:15
+          StorageLive(_3);                 // scope 0 at $DIR/if-condition-int.rs:12:8: 12:9
+          _3 = _1;                         // scope 0 at $DIR/if-condition-int.rs:12:8: 12:9
+-         _2 = Eq(move _3, const 42_u32);  // scope 0 at $DIR/if-condition-int.rs:12:8: 12:15
+-         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:12:14: 12:15
+-         switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32
++         _2 = Eq(_3, const 42_u32);       // scope 0 at $DIR/if-condition-int.rs:12:8: 12:15
++         nop;                             // scope 0 at $DIR/if-condition-int.rs:12:14: 12:15
++         switchInt(move _3) -> [42_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32
+      }
+  
+      bb1: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32
+          _0 = const 1_u32;                // scope 0 at $DIR/if-condition-int.rs:12:29: 12:30
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32
+      }
+  
+      bb2: {
++         StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32
+          _0 = const 0_u32;                // scope 0 at $DIR/if-condition-int.rs:12:18: 12:19
+          goto -> bb3;                     // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/if-condition-int.rs:13:1: 13:2
+          return;                          // scope 0 at $DIR/if-condition-int.rs:13:2: 13:2
+      }
+  }
+  
index b0fd65f88e7de9588ca35a263ecd4d5810fb2b8d..8182021a2cce453996a4389f23ff936d8ae8b971 100644 (file)
@@ -23,15 +23,15 @@ struct drop_check {
 extern "C" {
     void rust_catch_callback(void (*cb)(), bool* rust_ok);
 
-    static void callback() {
+    void throw_cxx_exception() {
         println("throwing C++ exception");
         throw exception();
     }
 
-    void throw_cxx_exception() {
+    void test_cxx_exception() {
         bool rust_ok = false;
         try {
-            rust_catch_callback(callback, &rust_ok);
+            rust_catch_callback(throw_cxx_exception, &rust_ok);
             assert(false && "unreachable");
         } catch (exception e) {
             println("caught C++ exception");
index 9c2045c8c89f72c24dcfe78d61d095578b5ae5a8..b5c8c1962a8ecc229af0ca5c8d265518aa4820e9 100644 (file)
@@ -1,5 +1,5 @@
-// Tests that C++ exceptions can unwind through Rust code, run destructors and
-// are ignored by catch_unwind. Also tests that Rust panics can unwind through
+// Tests that C++ exceptions can unwind through Rust code run destructors and
+// are caught by catch_unwind. Also tests that Rust panics can unwind through
 // C++ code.
 
 // For linking libstdc++ on MinGW
@@ -17,7 +17,7 @@ fn drop(&mut self) {
 }
 
 extern "C" {
-    fn throw_cxx_exception();
+    fn test_cxx_exception();
 
     #[unwind(allowed)]
     fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool);
@@ -26,15 +26,12 @@ fn drop(&mut self) {
 #[no_mangle]
 #[unwind(allowed)]
 extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) {
-    let _caught_unwind = catch_unwind(AssertUnwindSafe(|| {
-        let _drop = DropCheck(rust_ok);
-        cb();
-        unreachable!("should have unwound instead of returned");
-    }));
-    unreachable!("catch_unwind should not have caught foreign exception");
+    let _drop = DropCheck(rust_ok);
+    cb();
+    unreachable!("should have unwound instead of returned");
 }
 
-fn throw_rust_panic() {
+fn test_rust_panic() {
     #[unwind(allowed)]
     extern "C" fn callback() {
         println!("throwing rust panic");
@@ -60,6 +57,6 @@ extern "C" fn callback() {
 }
 
 fn main() {
-    unsafe { throw_cxx_exception() };
-    throw_rust_panic();
+    unsafe { test_cxx_exception() };
+    test_rust_panic();
 }
index 726db8746371e65c0bdc69f0cd49789fe08389e3..7d339c5a5dab456572616eb8e171cde52dde90ce 100644 (file)
@@ -14,3 +14,8 @@ pub fn panic_handler(_: &core::panic::PanicInfo) -> ! {
 extern "C" fn __rust_drop_panic() -> ! {
     loop {}
 }
+
+#[no_mangle]
+extern "C" fn __rust_foreign_exception() -> ! {
+    loop {}
+}
index 34276fbcf2058cf80e3483113f03619ea4090ecc..548d3e2544a00aa12699ecccf231a56f94e4ef6e 100644 (file)
 
 /// [struct@char]
 //~^ ERROR incompatible link
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 //~| NOTE resolved to a module
 pub mod char {}
 
 pub mod inner {
     //! [struct@char]
     //~^ ERROR incompatible link
-    //~| HELP use its disambiguator
+    //~| HELP prefix with the item kind
     //~| NOTE resolved to a builtin type
 }
index b28a44266665648d2cc0eda8ed6aac966cdbd291..53dccfbf1a2c48a7d2c533ee69bb0f842145e38d 100644 (file)
@@ -9,11 +9,11 @@ note: the lint level is defined here
    |
 LL | #![deny(broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
-help: to link to the module, prefix with the item type
+help: to link to the module, prefix with the item kind
    |
-LL | /// [module@char]
-   |      ^^^^^^^^^^^
-help: to link to the builtin type, prefix with the item type
+LL | /// [mod@char]
+   |      ^^^^^^^^
+help: to link to the builtin type, prefix with the item kind
    |
 LL | /// [prim@char]
    |      ^^^^^^^^^
@@ -24,11 +24,11 @@ error: `char` is both a module and a builtin type
 LL | /// [type@char]
    |      ^^^^^^^^^ ambiguous link
    |
-help: to link to the module, prefix with the item type
+help: to link to the module, prefix with the item kind
    |
-LL | /// [module@char]
-   |      ^^^^^^^^^^^
-help: to link to the builtin type, prefix with the item type
+LL | /// [mod@char]
+   |      ^^^^^^^^
+help: to link to the builtin type, prefix with the item kind
    |
 LL | /// [prim@char]
    |      ^^^^^^^^^
@@ -37,17 +37,25 @@ error: incompatible link kind for `char`
   --> $DIR/intra-link-prim-conflict.rs:19:6
    |
 LL | /// [struct@char]
-   |      ^^^^^^^^^^^ help: to link to the module, use its disambiguator: `mod@char`
+   |      ^^^^^^^^^^^
    |
    = note: this link resolved to a module, which is not a struct
+help: to link to the module, prefix with the item kind
+   |
+LL | /// [mod@char]
+   |      ^^^^^^^^
 
 error: incompatible link kind for `char`
   --> $DIR/intra-link-prim-conflict.rs:26:10
    |
 LL |     //! [struct@char]
-   |          ^^^^^^^^^^^ help: to link to the builtin type, use its disambiguator: `prim@char`
+   |          ^^^^^^^^^^^
    |
    = note: this link resolved to a builtin type, which is not a struct
+help: to link to the builtin type, prefix with the item kind
+   |
+LL |     //! [prim@char]
+   |          ^^^^^^^^^
 
 error: aborting due to 4 previous errors
 
index 35262c1b6122e0d478cdf22586e0b2660152bccf..7912c046f1c78f8e481f9c2a1bdecc6fa8b990d5 100644 (file)
@@ -9,7 +9,7 @@ note: the lint level is defined here
    |
 LL | #![deny(broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
-help: to link to the struct, prefix with the item type
+help: to link to the struct, prefix with the item kind
    |
 LL | /// [`struct@ambiguous`] is ambiguous.
    |      ^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ error: `ambiguous` is both a struct and a function
 LL | /// [ambiguous] is ambiguous.
    |      ^^^^^^^^^ ambiguous link
    |
-help: to link to the struct, prefix with the item type
+help: to link to the struct, prefix with the item kind
    |
 LL | /// [struct@ambiguous] is ambiguous.
    |      ^^^^^^^^^^^^^^^^
@@ -39,7 +39,7 @@ error: `multi_conflict` is a struct, a function, and a macro
 LL | /// [`multi_conflict`] is a three-way conflict.
    |      ^^^^^^^^^^^^^^^^ ambiguous link
    |
-help: to link to the struct, prefix with the item type
+help: to link to the struct, prefix with the item kind
    |
 LL | /// [`struct@multi_conflict`] is a three-way conflict.
    |      ^^^^^^^^^^^^^^^^^^^^^^^
@@ -58,11 +58,11 @@ error: `type_and_value` is both a module and a constant
 LL | /// Ambiguous [type_and_value].
    |                ^^^^^^^^^^^^^^ ambiguous link
    |
-help: to link to the module, prefix with the item type
+help: to link to the module, prefix with the item kind
    |
-LL | /// Ambiguous [module@type_and_value].
-   |                ^^^^^^^^^^^^^^^^^^^^^
-help: to link to the constant, prefix with the item type
+LL | /// Ambiguous [mod@type_and_value].
+   |                ^^^^^^^^^^^^^^^^^^
+help: to link to the constant, prefix with the item kind
    |
 LL | /// Ambiguous [const@type_and_value].
    |                ^^^^^^^^^^^^^^^^^^^^
@@ -73,7 +73,7 @@ error: `foo::bar` is both an enum and a function
 LL | /// Ambiguous non-implied shortcut link [`foo::bar`].
    |                                          ^^^^^^^^^^ ambiguous link
    |
-help: to link to the enum, prefix with the item type
+help: to link to the enum, prefix with the item kind
    |
 LL | /// Ambiguous non-implied shortcut link [`enum@foo::bar`].
    |                                          ^^^^^^^^^^^^^^^
index 1a7a2fce7a3f22afb2718df3b32777a713c1fa21..54e507adfe550e3036fea159d9fbb75efb4fbb71 100644 (file)
@@ -14,55 +14,55 @@ trait T {}
 /// Link to [struct@S]
 //~^ ERROR incompatible link kind for `S`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [mod@S]
 //~^ ERROR incompatible link kind for `S`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [union@S]
 //~^ ERROR incompatible link kind for `S`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [trait@S]
 //~^ ERROR incompatible link kind for `S`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [struct@T]
 //~^ ERROR incompatible link kind for `T`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [derive@m]
 //~^ ERROR incompatible link kind for `m`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP add an exclamation mark
 
 /// Link to [const@s]
 //~^ ERROR incompatible link kind for `s`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [static@c]
 //~^ ERROR incompatible link kind for `c`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [fn@c]
 //~^ ERROR incompatible link kind for `c`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [c()]
 //~^ ERROR incompatible link kind for `c`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP prefix with the item kind
 
 /// Link to [const@f]
 //~^ ERROR incompatible link kind for `f`
 //~| NOTE this link resolved
-//~| HELP use its disambiguator
+//~| HELP add parentheses
 pub fn f() {}
index 9edf838f9d88b7d6e62f32e154c82f8b48bb2d55..27b94af0378c240cf4e296ce62cd708e210a2cf1 100644 (file)
@@ -2,7 +2,7 @@ error: incompatible link kind for `S`
   --> $DIR/intra-links-disambiguator-mismatch.rs:14:14
    |
 LL | /// Link to [struct@S]
-   |              ^^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |              ^^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/intra-links-disambiguator-mismatch.rs:1:9
@@ -10,44 +10,64 @@ note: the lint level is defined here
 LL | #![deny(broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
    = note: this link resolved to an enum, which is not a struct
+help: to link to the enum, prefix with the item kind
+   |
+LL | /// Link to [enum@S]
+   |              ^^^^^^
 
 error: incompatible link kind for `S`
   --> $DIR/intra-links-disambiguator-mismatch.rs:19:14
    |
 LL | /// Link to [mod@S]
-   |              ^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |              ^^^^^
    |
    = note: this link resolved to an enum, which is not a module
+help: to link to the enum, prefix with the item kind
+   |
+LL | /// Link to [enum@S]
+   |              ^^^^^^
 
 error: incompatible link kind for `S`
   --> $DIR/intra-links-disambiguator-mismatch.rs:24:14
    |
 LL | /// Link to [union@S]
-   |              ^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |              ^^^^^^^
    |
    = note: this link resolved to an enum, which is not a union
+help: to link to the enum, prefix with the item kind
+   |
+LL | /// Link to [enum@S]
+   |              ^^^^^^
 
 error: incompatible link kind for `S`
   --> $DIR/intra-links-disambiguator-mismatch.rs:29:14
    |
 LL | /// Link to [trait@S]
-   |              ^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |              ^^^^^^^
    |
    = note: this link resolved to an enum, which is not a trait
+help: to link to the enum, prefix with the item kind
+   |
+LL | /// Link to [enum@S]
+   |              ^^^^^^
 
 error: incompatible link kind for `T`
   --> $DIR/intra-links-disambiguator-mismatch.rs:34:14
    |
 LL | /// Link to [struct@T]
-   |              ^^^^^^^^ help: to link to the trait, use its disambiguator: `trait@T`
+   |              ^^^^^^^^
    |
    = note: this link resolved to a trait, which is not a struct
+help: to link to the trait, prefix with the item kind
+   |
+LL | /// Link to [trait@T]
+   |              ^^^^^^^
 
 error: incompatible link kind for `m`
   --> $DIR/intra-links-disambiguator-mismatch.rs:39:14
    |
 LL | /// Link to [derive@m]
-   |              ^^^^^^^^ help: to link to the macro, use its disambiguator: `m!`
+   |              ^^^^^^^^ help: to link to the macro, add an exclamation mark: `m!`
    |
    = note: this link resolved to a macro, which is not a derive macro
 
@@ -55,39 +75,55 @@ error: incompatible link kind for `s`
   --> $DIR/intra-links-disambiguator-mismatch.rs:44:14
    |
 LL | /// Link to [const@s]
-   |              ^^^^^^^ help: to link to the static, use its disambiguator: `static@s`
+   |              ^^^^^^^
    |
    = note: this link resolved to a static, which is not a constant
+help: to link to the static, prefix with the item kind
+   |
+LL | /// Link to [static@s]
+   |              ^^^^^^^^
 
 error: incompatible link kind for `c`
   --> $DIR/intra-links-disambiguator-mismatch.rs:49:14
    |
 LL | /// Link to [static@c]
-   |              ^^^^^^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |              ^^^^^^^^
    |
    = note: this link resolved to a constant, which is not a static
+help: to link to the constant, prefix with the item kind
+   |
+LL | /// Link to [const@c]
+   |              ^^^^^^^
 
 error: incompatible link kind for `c`
   --> $DIR/intra-links-disambiguator-mismatch.rs:54:14
    |
 LL | /// Link to [fn@c]
-   |              ^^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |              ^^^^
    |
    = note: this link resolved to a constant, which is not a function
+help: to link to the constant, prefix with the item kind
+   |
+LL | /// Link to [const@c]
+   |              ^^^^^^^
 
 error: incompatible link kind for `c`
   --> $DIR/intra-links-disambiguator-mismatch.rs:59:14
    |
 LL | /// Link to [c()]
-   |              ^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |              ^^^
    |
    = note: this link resolved to a constant, which is not a function
+help: to link to the constant, prefix with the item kind
+   |
+LL | /// Link to [const@c]
+   |              ^^^^^^^
 
 error: incompatible link kind for `f`
   --> $DIR/intra-links-disambiguator-mismatch.rs:64:14
    |
 LL | /// Link to [const@f]
-   |              ^^^^^^^ help: to link to the function, use its disambiguator: `f()`
+   |              ^^^^^^^ help: to link to the function, add parentheses: `f()`
    |
    = note: this link resolved to a function, which is not a constant
 
diff --git a/src/test/rustdoc-ui/unknown-renamed-lints.rs b/src/test/rustdoc-ui/unknown-renamed-lints.rs
new file mode 100644 (file)
index 0000000..7faa82e
--- /dev/null
@@ -0,0 +1,8 @@
+#![deny(unknown_lints)]
+//~^ NOTE lint level is defined
+#![deny(renamed_and_removed_lints)]
+//~^ NOTE lint level is defined
+#![deny(x)]
+//~^ ERROR unknown lint
+#![deny(intra_doc_link_resolution_failure)]
+//~^ ERROR lint `intra_doc_link_resolution_failure` has been renamed
diff --git a/src/test/rustdoc-ui/unknown-renamed-lints.stderr b/src/test/rustdoc-ui/unknown-renamed-lints.stderr
new file mode 100644 (file)
index 0000000..f0917f1
--- /dev/null
@@ -0,0 +1,28 @@
+error: unknown lint: `x`
+  --> $DIR/unknown-renamed-lints.rs:5:9
+   |
+LL | #![deny(x)]
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/unknown-renamed-lints.rs:1:9
+   |
+LL | #![deny(unknown_lints)]
+   |         ^^^^^^^^^^^^^
+
+error: lint `intra_doc_link_resolution_failure` has been renamed to `broken_intra_doc_links`
+  --> $DIR/unknown-renamed-lints.rs:7:9
+   |
+LL | #![deny(intra_doc_link_resolution_failure)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `broken_intra_doc_links`
+   |
+note: the lint level is defined here
+  --> $DIR/unknown-renamed-lints.rs:3:9
+   |
+LL | #![deny(renamed_and_removed_lints)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: Compilation failed, aborting rustdoc
+
+error: aborting due to 3 previous errors
+
index 9ccc5d7882eb8d5e781d7bef316eeb20c3b386ba..47ba362c97789fe481b3d061442624a81cf49454 100644 (file)
@@ -3,22 +3,34 @@
 #![crate_name = "foo"]
 #![feature(doc_cfg)]
 
+// @has 'foo/index.html'
+// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync$'
+// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate feature `sync` only'
+
 // @has 'foo/struct.Foo.html'
-// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" only.'
+// @has '-' '//*[@class="stab portability"]' 'sync'
 #[doc(cfg(feature = "sync"))]
 #[doc(cfg(feature = "sync"))]
 pub struct Foo;
 
+// @has 'foo/bar/index.html'
+// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync$'
+// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate feature `sync` only'
+
 // @has 'foo/bar/struct.Bar.html'
-// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" only.'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync only.'
 #[doc(cfg(feature = "sync"))]
 pub mod bar {
     #[doc(cfg(feature = "sync"))]
     pub struct Bar;
 }
 
+// @has 'foo/baz/index.html'
+// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync and send$'
+// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate features `sync` and `send` only'
+
 // @has 'foo/baz/struct.Baz.html'
-// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" only.'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on crate features sync and send only.'
 #[doc(cfg(all(feature = "sync", feature = "send")))]
 pub mod baz {
     #[doc(cfg(feature = "sync"))]
@@ -26,15 +38,19 @@ pub mod baz {
 }
 
 // @has 'foo/qux/struct.Qux.html'
-// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" only.'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on crate features sync and send only.'
 #[doc(cfg(feature = "sync"))]
 pub mod qux {
     #[doc(cfg(all(feature = "sync", feature = "send")))]
     pub struct Qux;
 }
 
+// @has 'foo/quux/index.html'
+// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync and send and foo and bar$'
+// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate feature `sync` and crate feature `send` and `foo` and `bar` only'
+
 // @has 'foo/quux/struct.Quux.html'
-// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" and foo and bar only.'
+// @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync and crate feature send and foo and bar only.'
 #[doc(cfg(all(feature = "sync", feature = "send", foo)))]
 pub mod quux {
     #[doc(cfg(all(feature = "send", feature = "sync", bar)))]
index 13b45df64eabeae90a487c58341c964c3207a067..e383a9126f7bc2b065285b356d754ec97f78f195 100644 (file)
@@ -6,6 +6,26 @@
 
 struct T;
 
+struct Tuple(i32);
+
+struct Struct {
+    a: i32
+}
+
+impl Struct {
+    fn method(&self) {}
+}
+
+impl Future for Struct {
+    type Output = Struct;
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
+}
+
+impl Future for Tuple {
+    type Output = Tuple;
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
+}
+
 impl Future for T {
     type Output = Result<(), ()>;
 
@@ -23,10 +43,31 @@ async fn bar() -> Result<(), ()> {
     Ok(())
 }
 
+async fn struct_() -> Struct {
+    Struct { a: 1 }
+}
+
+async fn tuple() -> Tuple {
+    Tuple(1i32)
+}
+
 async fn baz() -> Result<(), ()> {
     let t = T;
     t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+
+    let _: i32 = tuple().0; //~ ERROR no field `0`
+
+    let _: i32 = struct_().a; //~ ERROR no field `a`
+
+    struct_().method(); //~ ERROR no method named
+
     Ok(())
 }
 
+async fn match_() {
+    match tuple() {
+        Tuple(_) => {} //~ ERROR mismatched types
+    }
+}
+
 fn main() {}
index e71f4e7136dad26f44fef9f604ffe14ec57c30de..69b6e8c3cf5699ff8039c570dd4ad4f5ff63467c 100644 (file)
@@ -1,5 +1,5 @@
 error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
-  --> $DIR/issue-61076.rs:22:5
+  --> $DIR/issue-61076.rs:42:5
    |
 LL |     foo()?;
    |     ^^^^^^
@@ -11,7 +11,7 @@ LL |     foo()?;
    = note: required by `std::ops::Try::into_result`
 
 error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
-  --> $DIR/issue-61076.rs:28:5
+  --> $DIR/issue-61076.rs:56:5
    |
 LL |     t?;
    |     ^^
@@ -22,6 +22,56 @@ LL |     t?;
    = help: the trait `std::ops::Try` is not implemented for `T`
    = note: required by `std::ops::Try::into_result`
 
-error: aborting due to 2 previous errors
+error[E0609]: no field `0` on type `impl std::future::Future`
+  --> $DIR/issue-61076.rs:58:26
+   |
+LL |     let _: i32 = tuple().0;
+   |                          ^
+   |
+help: consider awaiting before field access
+   |
+LL |     let _: i32 = tuple().await.0;
+   |                         ^^^^^^
+
+error[E0609]: no field `a` on type `impl std::future::Future`
+  --> $DIR/issue-61076.rs:60:28
+   |
+LL |     let _: i32 = struct_().a;
+   |                            ^
+   |
+help: consider awaiting before field access
+   |
+LL |     let _: i32 = struct_().await.a;
+   |                           ^^^^^^
+
+error[E0599]: no method named `method` found for opaque type `impl std::future::Future` in the current scope
+  --> $DIR/issue-61076.rs:62:15
+   |
+LL |     struct_().method();
+   |               ^^^^^^ method not found in `impl std::future::Future`
+   |
+help: consider awaiting before this method call
+   |
+LL |     struct_().await.method();
+   |               ^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/issue-61076.rs:69:9
+   |
+LL | async fn tuple() -> Tuple {
+   |                     ----- the `Output` of this `async fn`'s expected opaque type
+...
+LL |         Tuple(_) => {}
+   |         ^^^^^^^^ expected opaque type, found struct `Tuple`
+   |
+   = note: expected opaque type `impl std::future::Future`
+                   found struct `Tuple`
+help: consider awaiting on the future
+   |
+LL |     match tuple().await {
+   |                  ^^^^^^
+
+error: aborting due to 6 previous errors
 
-For more information about this error, try `rustc --explain E0277`.
+Some errors have detailed explanations: E0277, E0308, E0599, E0609.
+For more information about an error, try `rustc --explain E0277`.
index c8c2702ec447efd85b05c5d797ecbecb00e586aa..b7a976a0af6967188eb6f1537723d863cb466930 100644 (file)
@@ -8,7 +8,7 @@ fn full(self) {}
 
     async fn crash(self) {
         Self::partial(self.0);
-        Self::full(self); //~ ERROR use of moved value: `self`
+        Self::full(self); //~ ERROR use of partially moved value: `self`
     }
 }
 
index 9177b83dd48d776ac5d7601808db5a6e0233177a..e2a73539874e03577b0d46da8d5797da04ef7abb 100644 (file)
@@ -1,12 +1,12 @@
-error[E0382]: use of moved value: `self`
+error[E0382]: use of partially moved value: `self`
   --> $DIR/issue-66958-non-copy-infered-type-arg.rs:11:20
    |
 LL |         Self::partial(self.0);
-   |                       ------ value moved here
+   |                       ------ value partially moved here
 LL |         Self::full(self);
    |                    ^^^^ value used here after partial move
    |
-   = note: move occurs because `self.0` has type `S`, which does not implement the `Copy` trait
+   = note: partial move occurs because `self.0` has type `S`, which does not implement the `Copy` trait
 
 error: aborting due to previous error
 
index 2a7a721324d69f18b0687912188389550afc8bef..489bf70d920a3b0ae3ee2ec88b44f32e1e1ffeed 100644 (file)
@@ -8,26 +8,26 @@ LL |     drop(m);
 LL |     match m { _ => { } } // #53114: should eventually be accepted too
    |           ^ value used here after move
 
-error[E0382]: use of moved value: `mm`
+error[E0382]: use of partially moved value: `mm`
   --> $DIR/issue-53114-borrow-checks.rs:27:11
    |
 LL |     match mm { (_x, _) => { } }
-   |                 -- value moved here
+   |                 -- value partially moved here
 LL |     match mm { (_, _y) => { } }
    |           ^^ value used here after partial move
    |
-   = note: move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait
+   = note: partial move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `mm`
+error[E0382]: use of partially moved value: `mm`
   --> $DIR/issue-53114-borrow-checks.rs:29:11
    |
 LL |     match mm { (_, _y) => { } }
-   |                    -- value moved here
+   |                    -- value partially moved here
 LL |
 LL |     match mm { (_, _) => { } }
    |           ^^ value used here after partial move
    |
-   = note: move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait
+   = note: partial move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait
 
 error[E0382]: use of moved value: `m`
   --> $DIR/issue-53114-borrow-checks.rs:36:16
@@ -39,26 +39,26 @@ LL |     drop(m);
 LL |     if let _ = m { } // #53114: should eventually be accepted too
    |                ^ value used here after move
 
-error[E0382]: use of moved value: `mm`
+error[E0382]: use of partially moved value: `mm`
   --> $DIR/issue-53114-borrow-checks.rs:41:22
    |
 LL |     if let (_x, _) = mm { }
-   |             -- value moved here
+   |             -- value partially moved here
 LL |     if let (_, _y) = mm { }
    |                      ^^ value used here after partial move
    |
-   = note: move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait
+   = note: partial move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `mm`
+error[E0382]: use of partially moved value: `mm`
   --> $DIR/issue-53114-borrow-checks.rs:43:21
    |
 LL |     if let (_, _y) = mm { }
-   |                -- value moved here
+   |                -- value partially moved here
 LL |
 LL |     if let (_, _) = mm { }
    |                     ^^ value used here after partial move
    |
-   = note: move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait
+   = note: partial move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait
 
 error: aborting due to 6 previous errors
 
index c1513fcba8a66819626769f73a6d17796d043bef..ced4d002b384e7b5c276406eb01a5c5bb2c062fe 100644 (file)
@@ -20,7 +20,7 @@ fn move_out_from_begin_field_and_end() {
         [_, _, (_x, _)] => {}
     }
     match a {
-        [.., _y] => {} //~ ERROR use of moved value
+        [.., _y] => {} //~ ERROR use of partially moved value
     }
 }
 
@@ -42,7 +42,7 @@ fn move_out_by_const_index_and_subslice() {
         [_x, _, _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_y @ .., _, _] => {}
     }
 }
@@ -53,7 +53,7 @@ fn move_out_by_const_index_end_and_subslice() {
         [.., _x] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, _, _y @ ..] => {}
     }
 }
@@ -64,7 +64,7 @@ fn move_out_by_const_index_field_and_subslice() {
         [(_x, _), _, _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_y @ .., _, _] => {}
     }
 }
@@ -75,7 +75,7 @@ fn move_out_by_const_index_end_field_and_subslice() {
         [.., (_x, _)] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, _, _y @ ..] => {}
     }
 }
@@ -108,7 +108,7 @@ fn move_out_by_subslice_and_subslice() {
         [x @ .., _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, _y @ ..] => {}
     }
 }
index 84930b000ccb3a5a122c1fa25b438d2a39b727d4..d63f03a71dbfeb8bec2e90bd3f1c6c6decc5ae2e 100644 (file)
@@ -9,16 +9,16 @@ LL |         [.., _y] => {}
    |
    = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a[..]`
+error[E0382]: use of partially moved value: `a[..]`
   --> $DIR/borrowck-move-out-from-array-match.rs:23:14
    |
 LL |         [_, _, (_x, _)] => {}
-   |                 -- value moved here
+   |                 -- value partially moved here
 ...
 LL |         [.., _y] => {}
    |              ^^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
 error[E0382]: use of moved value: `a[..].0`
   --> $DIR/borrowck-move-out-from-array-match.rs:33:15
@@ -31,49 +31,49 @@ LL |         [.., (_y, _)] => {}
    |
    = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-match.rs:44:11
    |
 LL |         [_x, _, _] => {}
-   |          -- value moved here
+   |          -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-match.rs:55:11
    |
 LL |         [.., _x] => {}
-   |              -- value moved here
+   |              -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-match.rs:66:11
    |
 LL |         [(_x, _), _, _] => {}
-   |           -- value moved here
+   |           -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-match.rs:77:11
    |
 LL |         [.., (_x, _)] => {}
-   |               -- value moved here
+   |               -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
 error[E0382]: use of moved value: `a[..].0`
   --> $DIR/borrowck-move-out-from-array-match.rs:89:11
@@ -97,16 +97,16 @@ LL |         [.., (_x, _)] => {}
    |
    = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-match.rs:110:11
    |
 LL |         [x @ .., _] => {}
-   |          ------ value moved here
+   |          ------ value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
 error: aborting due to 10 previous errors
 
index 056b8e672bd93a5cab6658e05ce651added16f80..97db70f34cc7405eb52f6b32e15cbbddfc9a1cee 100644 (file)
@@ -15,7 +15,7 @@ fn move_out_from_begin_and_one_from_end() {
         [_, _, _x] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [.., _y, _] => {}
     }
 }
@@ -26,7 +26,7 @@ fn move_out_from_begin_field_and_end_field() {
         [_, _, (_x, _)] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [.., (_, _y)] => {}
     }
 }
@@ -39,7 +39,7 @@ fn move_out_by_const_index_and_subslice() {
         [_x, _, _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, _y @ ..] => {}
     }
 }
@@ -50,7 +50,7 @@ fn move_out_by_const_index_end_and_subslice() {
         [.., _x] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_y @ .., _] => {}
     }
 }
@@ -61,7 +61,7 @@ fn move_out_by_const_index_field_and_subslice() {
         [(_x, _), _, _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, _y @ ..] => {}
     }
 }
@@ -72,7 +72,7 @@ fn move_out_by_const_index_end_field_and_subslice() {
         [.., (_x, _)] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_y @ .., _] => {}
     }
 }
@@ -83,7 +83,7 @@ fn move_out_by_const_subslice_and_index_field() {
         [_, _y @ ..] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [(_x, _), _, _] => {}
     }
 }
@@ -94,7 +94,7 @@ fn move_out_by_const_subslice_and_end_index_field() {
         [_y @ .., _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [.., (_x, _)] => {}
     }
 }
@@ -107,7 +107,7 @@ fn move_out_by_subslice_and_subslice() {
         [x @ .., _, _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, _y @ ..] => {}
     }
 }
index ff5eab2442c83312dd43695f0b9e89cf68191efe..7c675149894a11fc6e15beaf04cc35c83f9b2d15 100644 (file)
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:17:11
    |
 LL |         [_, _, _x] => {}
-   |                -- value moved here
+   |                -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:28:11
    |
 LL |         [_, _, (_x, _)] => {}
-   |                 -- value moved here
+   |                 -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:41:11
    |
 LL |         [_x, _, _] => {}
-   |          -- value moved here
+   |          -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:52:11
    |
 LL |         [.., _x] => {}
-   |              -- value moved here
+   |              -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:63:11
    |
 LL |         [(_x, _), _, _] => {}
-   |           -- value moved here
+   |           -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:74:11
    |
 LL |         [.., (_x, _)] => {}
-   |               -- value moved here
+   |               -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:85:11
    |
 LL |         [_, _y @ ..] => {}
-   |             ------- value moved here
+   |             ------- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:96:11
    |
 LL |         [_y @ .., _] => {}
-   |          ------- value moved here
+   |          ------- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:109:11
    |
 LL |         [x @ .., _, _] => {}
-   |          ------ value moved here
+   |          ------ value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
 error: aborting due to 9 previous errors
 
index 0ef63105cfbd3212953a94c654a134be6c6c1906..806354b0116ce58084dcf38f46a67f2e7fdfa867 100644 (file)
@@ -9,16 +9,16 @@ LL |         [.., ref _y] => {}
    |
    = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: borrow of moved value: `a[..]`
+error[E0382]: borrow of partially moved value: `a[..]`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:23:14
    |
 LL |         [_, _, (_x, _)] => {}
-   |                 -- value moved here
+   |                 -- value partially moved here
 ...
 LL |         [.., ref _y] => {}
    |              ^^^^^^ value borrowed here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
 error[E0382]: borrow of moved value: `a[..].0`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:33:15
@@ -31,49 +31,49 @@ LL |         [.., (ref _y, _)] => {}
    |
    = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:44:11
    |
 LL |         [_x, _, _] => {}
-   |          -- value moved here
+   |          -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:55:11
    |
 LL |         [.., _x] => {}
-   |              -- value moved here
+   |              -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:66:11
    |
 LL |         [(_x, _), _, _] => {}
-   |           -- value moved here
+   |           -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:77:11
    |
 LL |         [.., (_x, _)] => {}
-   |               -- value moved here
+   |               -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
 error[E0382]: borrow of moved value: `a[..]`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:89:11
@@ -97,60 +97,60 @@ LL |         [.., (ref _x, _)] => {}
    |
    = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:110:11
    |
 LL |         [x @ .., _] => {}
-   |          ------ value moved here
+   |          ------ value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:123:5
    |
 LL |         [_, _, _x] => {}
-   |                -- value moved here
+   |                -- value partially moved here
 LL |     }
 LL |     a[2] = Default::default();
    |     ^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:131:5
    |
 LL |         [_, _, (_x, _)] => {}
-   |                 -- value moved here
+   |                 -- value partially moved here
 LL |     }
 LL |     a[2].1 = Default::default();
    |     ^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:139:5
    |
 LL |         [_, _, _x @ ..] => {}
-   |                ------- value moved here
+   |                ------- value partially moved here
 LL |     }
 LL |     a[0] = Default::default();
    |     ^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-match.rs:147:5
    |
 LL |         [_, _, _x @ ..] => {}
-   |                ------- value moved here
+   |                ------- value partially moved here
 LL |     }
 LL |     a[0].1 = Default::default();
    |     ^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
 error: aborting due to 14 previous errors
 
index 5afd6835dcfb60b0c98bd8b4bccf4887eb631174..017ca90b81a3f75e218d82fabcc6808b2d36be29 100644 (file)
@@ -15,7 +15,7 @@ fn move_out_from_begin_and_one_from_end() {
         [_, _, _x] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [.., ref _y, _] => {}
     }
 }
@@ -26,7 +26,7 @@ fn move_out_from_begin_field_and_end_field() {
         [_, _, (_x, _)] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [.., (_, ref _y)] => {}
     }
 }
@@ -39,7 +39,7 @@ fn move_out_by_const_index_and_subslice() {
         [_x, _, _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, ref _y @ ..] => {}
     }
 }
@@ -50,7 +50,7 @@ fn move_out_by_const_index_end_and_subslice() {
         [.., _x] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [ref _y @ .., _] => {}
     }
 }
@@ -61,7 +61,7 @@ fn move_out_by_const_index_field_and_subslice() {
         [(_x, _), _, _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, ref _y @ ..] => {}
     }
 }
@@ -72,7 +72,7 @@ fn move_out_by_const_index_end_field_and_subslice() {
         [.., (_x, _)] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [ref _y @ .., _] => {}
     }
 }
@@ -83,7 +83,7 @@ fn move_out_by_const_subslice_and_index_field() {
         [_, _y @ ..] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [(ref _x, _), _, _] => {}
     }
 }
@@ -94,7 +94,7 @@ fn move_out_by_const_subslice_and_end_index_field() {
         [_y @ .., _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [.., (ref _x, _)] => {}
     }
 }
@@ -107,7 +107,7 @@ fn move_out_by_subslice_and_subslice() {
         [x @ .., _, _] => {}
     }
     match a {
-        //~^ ERROR use of moved value
+        //~^ ERROR use of partially moved value
         [_, ref _y @ ..] => {}
     }
 }
index a4042ce7db336192c64086777a9a6105cc62be34..53f815db140d2c220dc2dd1d70cd0f12ddff0f73 100644 (file)
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:17:11
    |
 LL |         [_, _, _x] => {}
-   |                -- value moved here
+   |                -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:28:11
    |
 LL |         [_, _, (_x, _)] => {}
-   |                 -- value moved here
+   |                 -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:41:11
    |
 LL |         [_x, _, _] => {}
-   |          -- value moved here
+   |          -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:52:11
    |
 LL |         [.., _x] => {}
-   |              -- value moved here
+   |              -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:63:11
    |
 LL |         [(_x, _), _, _] => {}
-   |           -- value moved here
+   |           -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:74:11
    |
 LL |         [.., (_x, _)] => {}
-   |               -- value moved here
+   |               -- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:85:11
    |
 LL |         [_, _y @ ..] => {}
-   |             ------- value moved here
+   |             ------- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:96:11
    |
 LL |         [_y @ .., _] => {}
-   |          ------- value moved here
+   |          ------- value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:109:11
    |
 LL |         [x @ .., _, _] => {}
-   |          ------ value moved here
+   |          ------ value partially moved here
 LL |     }
 LL |     match a {
    |           ^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
 error: aborting due to 9 previous errors
 
index 7ad4116645e9351d87f1eef6d814f3cd85c9ca15..004cc433b3459b24f583824905ab390084a525c7 100644 (file)
@@ -8,15 +8,15 @@ LL |     let [.., ref _y] = a;
    |
    = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: borrow of moved value: `a[..]`
+error[E0382]: borrow of partially moved value: `a[..]`
   --> $DIR/borrowck-move-out-from-array-use.rs:16:14
    |
 LL |     let [_, _, (_x, _)] = a;
-   |                 -- value moved here
+   |                 -- value partially moved here
 LL |     let [.., ref _y] = a;
    |              ^^^^^^ value borrowed here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
 error[E0382]: borrow of moved value: `a[..].0`
   --> $DIR/borrowck-move-out-from-array-use.rs:22:15
@@ -28,45 +28,45 @@ LL |     let [.., (ref _y, _)] = a;
    |
    = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: borrow of moved value: `a`
+error[E0382]: borrow of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:30:10
    |
 LL |     let [_x, _, _] = a;
-   |          -- value moved here
+   |          -- value partially moved here
 LL |     let [ref _y @ .., _, _] = a;
    |          ^^^^^^^^^^^ value borrowed here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: borrow of moved value: `a`
+error[E0382]: borrow of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:36:16
    |
 LL |     let [.., _x] = a;
-   |              -- value moved here
+   |              -- value partially moved here
 LL |     let [_, _, ref _y @ ..] = a;
    |                ^^^^^^^^^^^ value borrowed here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: borrow of moved value: `a`
+error[E0382]: borrow of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:42:10
    |
 LL |     let [(_x, _), _, _] = a;
-   |           -- value moved here
+   |           -- value partially moved here
 LL |     let [ref _y @ .., _, _] = a;
    |          ^^^^^^^^^^^ value borrowed here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: borrow of moved value: `a`
+error[E0382]: borrow of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:48:16
    |
 LL |     let [.., (_x, _)] = a;
-   |               -- value moved here
+   |               -- value partially moved here
 LL |     let [_, _, ref _y @ ..] = a;
    |                ^^^^^^^^^^^ value borrowed here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
 error[E0382]: borrow of moved value: `a[..]`
   --> $DIR/borrowck-move-out-from-array-use.rs:54:11
@@ -88,55 +88,55 @@ LL |     let [.., (ref _x, _)] = a;
    |
    = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: borrow of moved value: `a`
+error[E0382]: borrow of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:68:13
    |
 LL |     let [x @ .., _] = a;
-   |          ------ value moved here
+   |          ------ value partially moved here
 LL |     let [_, ref _y @ ..] = a;
    |             ^^^^^^^^^^^ value borrowed here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:76:5
    |
 LL |     let [_, _, _x] = a;
-   |                -- value moved here
+   |                -- value partially moved here
 LL |     a[2] = Default::default();
    |     ^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:82:5
    |
 LL |     let [_, _, (_x, _)] = a;
-   |                 -- value moved here
+   |                 -- value partially moved here
 LL |     a[2].1 = Default::default();
    |     ^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:88:5
    |
 LL |     let [_, _, _x @ ..] = a;
-   |                ------- value moved here
+   |                ------- value partially moved here
 LL |     a[0] = Default::default();
    |     ^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array-use.rs:94:5
    |
 LL |     let [_, _, _x @ ..] = a;
-   |                ------- value moved here
+   |                ------- value partially moved here
 LL |     a[0].1 = Default::default();
    |     ^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
 error: aborting due to 14 previous errors
 
index b7babd93ed7a638244a594ff060fc62fad8a86cb..d3eb3e9f76144c8a7f93b16da853e8c1c3f969ce 100644 (file)
@@ -8,15 +8,15 @@ LL |     let [.., _y] = a;
    |
    = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a[..]`
+error[E0382]: use of partially moved value: `a[..]`
   --> $DIR/borrowck-move-out-from-array.rs:16:14
    |
 LL |     let [_, _, (_x, _)] = a;
-   |                 -- value moved here
+   |                 -- value partially moved here
 LL |     let [.., _y] = a;
    |              ^^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
 error[E0382]: use of moved value: `a[..].0`
   --> $DIR/borrowck-move-out-from-array.rs:22:15
@@ -28,45 +28,45 @@ LL |     let [.., (_y, _)] = a;
    |
    = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array.rs:30:10
    |
 LL |     let [_x, _, _] = a;
-   |          -- value moved here
+   |          -- value partially moved here
 LL |     let [_y @ .., _, _] = a;
    |          ^^^^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array.rs:36:16
    |
 LL |     let [.., _x] = a;
-   |              -- value moved here
+   |              -- value partially moved here
 LL |     let [_, _, _y @ ..] = a;
    |                ^^^^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array.rs:42:10
    |
 LL |     let [(_x, _), _, _] = a;
-   |           -- value moved here
+   |           -- value partially moved here
 LL |     let [_y @ .., _, _] = a;
    |          ^^^^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array.rs:48:16
    |
 LL |     let [.., (_x, _)] = a;
-   |               -- value moved here
+   |               -- value partially moved here
 LL |     let [_, _, _y @ ..] = a;
    |                ^^^^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait
 
 error[E0382]: use of moved value: `a[..].0`
   --> $DIR/borrowck-move-out-from-array.rs:54:11
@@ -88,15 +88,15 @@ LL |     let [.., (_x, _)] = a;
    |
    = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `a`
+error[E0382]: use of partially moved value: `a`
   --> $DIR/borrowck-move-out-from-array.rs:68:13
    |
 LL |     let [x @ .., _] = a;
-   |          ------ value moved here
+   |          ------ value partially moved here
 LL |     let [_, _y @ ..] = a;
    |             ^^^^^^^ value used here after partial move
    |
-   = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
+   = note: partial move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
 
 error: aborting due to 10 previous errors
 
index 9f35a4a8d83bd82e3d52c41088e6c13c8e7074bb..7951a5b1b5d70e6dfd2d236f4cadc78299608b7d 100644 (file)
@@ -14,15 +14,15 @@ LL |     let _ = line1.origin.x + 1;
    |
    = note: move occurs because `line1.origin` has type `Point`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `line2`
+error[E0382]: use of partially moved value: `line2`
   --> $DIR/borrowck-uninit-field-access.rs:29:5
    |
 LL |     let _moved = (line2.origin, line2.middle);
-   |                                 ------------ value moved here
+   |                                 ------------ value partially moved here
 LL |     line2.consume();
    |     ^^^^^ value used here after partial move
    |
-   = note: move occurs because `line2.middle` has type `Point`, which does not implement the `Copy` trait
+   = note: partial move occurs because `line2.middle` has type `Point`, which does not implement the `Copy` trait
 
 error: aborting due to 3 previous errors
 
index 175eb3b7a04d1168237ec991cbbd658f9abfb48b..b5c275bf28caf444b9fdd144cda811cd67b4a681 100644 (file)
@@ -15,9 +15,9 @@ fn main() {
     if let Some(mut x) = s {
         x = S;
     }
-    foo(s); //~ ERROR use of moved value: `s`
+    foo(s); //~ ERROR use of partially moved value: `s`
     let mut e = E::V { s: S };
     let E::V { s: mut x } = e;
     x = S;
-    bar(e); //~ ERROR use of moved value: `e`
+    bar(e); //~ ERROR use of partially moved value: `e`
 }
index 391638444c3bdcf06791fddc991dcb2960801a5b..17bc5492756e6fd47d9ec486e2fc979268b81b4b 100644 (file)
@@ -1,28 +1,28 @@
-error[E0382]: use of moved value: `s`
+error[E0382]: use of partially moved value: `s`
   --> $DIR/move-in-pattern-mut.rs:18:9
    |
 LL |     if let Some(mut x) = s {
-   |                 ----- value moved here
+   |                 ----- value partially moved here
 ...
 LL |     foo(s);
    |         ^ value used here after partial move
    |
-   = note: move occurs because value has type `S`, which does not implement the `Copy` trait
+   = note: partial move occurs because value has type `S`, which does not implement the `Copy` trait
 help: borrow this field in the pattern to avoid moving `s.0`
    |
 LL |     if let Some(ref mut x) = s {
    |                 ^^^
 
-error[E0382]: use of moved value: `e`
+error[E0382]: use of partially moved value: `e`
   --> $DIR/move-in-pattern-mut.rs:22:9
    |
 LL |     let E::V { s: mut x } = e;
-   |                   ----- value moved here
+   |                   ----- value partially moved here
 LL |     x = S;
 LL |     bar(e);
    |         ^ value used here after partial move
    |
-   = note: move occurs because value has type `S`, which does not implement the `Copy` trait
+   = note: partial move occurs because value has type `S`, which does not implement the `Copy` trait
 help: borrow this field in the pattern to avoid moving `e.s`
    |
 LL |     let E::V { s: ref mut x } = e;
index f55fdcc5f90e80f1dcca0613b180131d1af9b194..145893d3343bcf8feacded7ab4ac59a2bf1b905e 100644 (file)
@@ -16,9 +16,9 @@ fn main() {
     if let Some(ref x) = s {
         let _ = x;
     }
-    foo(s); //~ ERROR use of moved value: `s`
+    foo(s); //~ ERROR use of partially moved value: `s`
     let e = E::V { s: S };
     let E::V { s: ref x } = e;
     let _ = x;
-    bar(e); //~ ERROR use of moved value: `e`
+    bar(e); //~ ERROR use of partially moved value: `e`
 }
index 7ad04b9490c25167f83569d050585933fa11b008..14851d0f6fcffeaee16abf16277a86f431bef061 100644 (file)
@@ -16,9 +16,9 @@ fn main() {
     if let Some(x) = s {
         let _ = x;
     }
-    foo(s); //~ ERROR use of moved value: `s`
+    foo(s); //~ ERROR use of partially moved value: `s`
     let e = E::V { s: S };
     let E::V { s: x } = e;
     let _ = x;
-    bar(e); //~ ERROR use of moved value: `e`
+    bar(e); //~ ERROR use of partially moved value: `e`
 }
index c5cb24455eb61ee5c83fd7b05b6c904a21073eaa..21ba92f1fc41cbac70c52b26758e437bee2a232a 100644 (file)
@@ -1,28 +1,28 @@
-error[E0382]: use of moved value: `s`
+error[E0382]: use of partially moved value: `s`
   --> $DIR/move-in-pattern.rs:19:9
    |
 LL |     if let Some(x) = s {
-   |                 - value moved here
+   |                 - value partially moved here
 ...
 LL |     foo(s);
    |         ^ value used here after partial move
    |
-   = note: move occurs because value has type `S`, which does not implement the `Copy` trait
+   = note: partial move occurs because value has type `S`, which does not implement the `Copy` trait
 help: borrow this field in the pattern to avoid moving `s.0`
    |
 LL |     if let Some(ref x) = s {
    |                 ^^^
 
-error[E0382]: use of moved value: `e`
+error[E0382]: use of partially moved value: `e`
   --> $DIR/move-in-pattern.rs:23:9
    |
 LL |     let E::V { s: x } = e;
-   |                   - value moved here
+   |                   - value partially moved here
 LL |     let _ = x;
 LL |     bar(e);
    |         ^ value used here after partial move
    |
-   = note: move occurs because value has type `S`, which does not implement the `Copy` trait
+   = note: partial move occurs because value has type `S`, which does not implement the `Copy` trait
 help: borrow this field in the pattern to avoid moving `e.s`
    |
 LL |     let E::V { s: ref x } = e;
index 6b86feb5921a1d04a64972ad763f07654d8c53aa..6b03e847def140dbf98864f75a05aaef99254ad9 100644 (file)
@@ -17,6 +17,8 @@
 
 #[lang = "eh_personality"]
 fn eh() {}
+#[lang = "eh_catch_typeinfo"]
+static EH_CATCH_TYPEINFO: u8 = 0;
 
 #[panic_handler]
 fn panic(_info: &PanicInfo) -> ! {
diff --git a/src/test/ui/issues/issue-70381.rs b/src/test/ui/issues/issue-70381.rs
new file mode 100644 (file)
index 0000000..3df8277
--- /dev/null
@@ -0,0 +1,6 @@
+// Test that multi-byte unicode characters with missing parameters do not ICE.
+
+fn main() {
+  println!("\r¡{}")
+  //~^ ERROR 1 positional argument in format string
+}
diff --git a/src/test/ui/issues/issue-70381.stderr b/src/test/ui/issues/issue-70381.stderr
new file mode 100644 (file)
index 0000000..96b8e65
--- /dev/null
@@ -0,0 +1,8 @@
+error: 1 positional argument in format string, but no arguments were given
+  --> $DIR/issue-70381.rs:4:16
+   |
+LL |   println!("\r¡{}")
+   |                ^^
+
+error: aborting due to previous error
+
index d6ac7ccccc77b1c6b111ac80855efa81d0d5eb16..41f0baecf24a892704d45d95beaed02c808b5fea 100644 (file)
@@ -182,7 +182,9 @@ struct Point3 {
             y: f32,
             z: f32,
         }
-        extern "C" { fn origin() -> Point3; }
+        extern "C" {
+            fn origin() -> Point3;
+        }
     }
     mod b {
         #[repr(C)]
@@ -191,8 +193,9 @@ struct Point3 {
             y: i32,
             z: i32, // NOTE: Incorrectly redeclared as i32
         }
-        extern "C" { fn origin() -> Point3; }
-        //~^ WARN `origin` redeclared with a different signature
+        extern "C" {
+            fn origin() -> Point3; //~ WARN `origin` redeclared with a different signature
+        }
     }
 }
 
@@ -258,6 +261,78 @@ mod b {
     }
 }
 
+// See #75739
+mod non_zero_transparent {
+    mod a1 {
+        use std::num::NonZeroUsize;
+        extern "C" {
+            fn f1() -> NonZeroUsize;
+        }
+    }
+
+    mod b1 {
+        #[repr(transparent)]
+        struct X(NonZeroUsize);
+        use std::num::NonZeroUsize;
+        extern "C" {
+            fn f1() -> X;
+        }
+    }
+
+    mod a2 {
+        use std::num::NonZeroUsize;
+        extern "C" {
+            fn f2() -> NonZeroUsize;
+        }
+    }
+
+    mod b2 {
+        #[repr(transparent)]
+        struct X1(NonZeroUsize);
+
+        #[repr(transparent)]
+        struct X(X1);
+
+        use std::num::NonZeroUsize;
+        extern "C" {
+            // Same case as above, but with two layers of newtyping.
+            fn f2() -> X;
+        }
+    }
+
+    mod a3 {
+        #[repr(transparent)]
+        struct X(core::ptr::NonNull<i32>);
+
+        use std::num::NonZeroUsize;
+        extern "C" {
+            fn f3() -> X;
+        }
+    }
+
+    mod b3 {
+        extern "C" {
+            fn f3() -> core::ptr::NonNull<i32>;
+        }
+    }
+
+    mod a4 {
+        #[repr(transparent)]
+        enum E {
+            X(std::num::NonZeroUsize),
+        }
+        extern "C" {
+            fn f4() -> E;
+        }
+    }
+
+    mod b4 {
+        extern "C" {
+            fn f4() -> std::num::NonZeroUsize;
+        }
+    }
+}
+
 mod null_optimised_enums {
     mod a {
         extern "C" {
@@ -285,3 +360,26 @@ mod b {
         }
     }
 }
+
+#[allow(improper_ctypes)]
+mod unknown_layout {
+    mod a {
+        extern "C" {
+            pub fn generic(l: Link<u32>);
+        }
+        pub struct Link<T> {
+            pub item: T,
+            pub next: *const Link<T>,
+        }
+    }
+
+    mod b {
+        extern "C" {
+            pub fn generic(l: Link<u32>);
+        }
+        pub struct Link<T> {
+            pub item: T,
+            pub next: *const Link<T>,
+        }
+    }
+}
index cca0c4c59eb197166f08f901e65b006c604281da..0a18f05ba2903ca915219a80db4fd94fbd35ede1 100644 (file)
@@ -106,19 +106,19 @@ LL |             fn draw_point(p: Point);
               found `unsafe extern "C" fn(sameish_members::b::Point)`
 
 warning: `origin` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:194:22
+  --> $DIR/clashing-extern-fn.rs:197:13
    |
-LL |         extern "C" { fn origin() -> Point3; }
-   |                      ---------------------- `origin` previously declared here
+LL |             fn origin() -> Point3;
+   |             ---------------------- `origin` previously declared here
 ...
-LL |         extern "C" { fn origin() -> Point3; }
-   |                      ^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+LL |             fn origin() -> Point3;
+   |             ^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> same_sized_members_clash::a::Point3`
               found `unsafe extern "C" fn() -> same_sized_members_clash::b::Point3`
 
 warning: `transparent_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:217:13
+  --> $DIR/clashing-extern-fn.rs:220:13
    |
 LL |             fn transparent_incorrect() -> T;
    |             -------------------------------- `transparent_incorrect` previously declared here
@@ -130,7 +130,7 @@ LL |             fn transparent_incorrect() -> isize;
               found `unsafe extern "C" fn() -> isize`
 
 warning: `missing_return_type` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:235:13
+  --> $DIR/clashing-extern-fn.rs:238:13
    |
 LL |             fn missing_return_type() -> usize;
    |             ---------------------------------- `missing_return_type` previously declared here
@@ -142,7 +142,7 @@ LL |             fn missing_return_type();
               found `unsafe extern "C" fn()`
 
 warning: `non_zero_usize` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:253:13
+  --> $DIR/clashing-extern-fn.rs:256:13
    |
 LL |             fn non_zero_usize() -> core::num::NonZeroUsize;
    |             ----------------------------------------------- `non_zero_usize` previously declared here
@@ -154,7 +154,7 @@ LL |             fn non_zero_usize() -> usize;
               found `unsafe extern "C" fn() -> usize`
 
 warning: `non_null_ptr` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:255:13
+  --> $DIR/clashing-extern-fn.rs:258:13
    |
 LL |             fn non_null_ptr() -> core::ptr::NonNull<usize>;
    |             ----------------------------------------------- `non_null_ptr` previously declared here
@@ -166,7 +166,7 @@ LL |             fn non_null_ptr() -> *const usize;
               found `unsafe extern "C" fn() -> *const usize`
 
 warning: `option_non_zero_usize_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:281:13
+  --> $DIR/clashing-extern-fn.rs:356:13
    |
 LL |             fn option_non_zero_usize_incorrect() -> usize;
    |             ---------------------------------------------- `option_non_zero_usize_incorrect` previously declared here
@@ -178,7 +178,7 @@ LL |             fn option_non_zero_usize_incorrect() -> isize;
               found `unsafe extern "C" fn() -> isize`
 
 warning: `option_non_null_ptr_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:283:13
+  --> $DIR/clashing-extern-fn.rs:358:13
    |
 LL |             fn option_non_null_ptr_incorrect() -> *const usize;
    |             --------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here
index 83a88ab3bd97c4b0dbf6763e7f9130d38abf2d3e..dd0cac659fd312a1e4ccc8a96b3ceee10d58ab2a 100644 (file)
@@ -1,41 +1,41 @@
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:20:23
+  --> $DIR/macro-comma-behavior.rs:21:23
    |
 LL |     assert_eq!(1, 1, "{}",);
    |                       ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:23:23
+  --> $DIR/macro-comma-behavior.rs:24:23
    |
 LL |     assert_ne!(1, 2, "{}",);
    |                       ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:29:29
+  --> $DIR/macro-comma-behavior.rs:30:29
    |
 LL |     debug_assert_eq!(1, 1, "{}",);
    |                             ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:32:29
+  --> $DIR/macro-comma-behavior.rs:33:29
    |
 LL |     debug_assert_ne!(1, 2, "{}",);
    |                             ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:53:19
+  --> $DIR/macro-comma-behavior.rs:54:19
    |
 LL |     format_args!("{}",);
    |                   ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:71:21
+  --> $DIR/macro-comma-behavior.rs:72:21
    |
 LL |     unimplemented!("{}",);
    |                     ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:80:24
+  --> $DIR/macro-comma-behavior.rs:81:24
    |
 LL |             write!(f, "{}",)?;
    |                        ^^
index 04714c65b5cb71daf78e86397487ce16a11767cd..0bfe06830783278e7a57ce63c7fcf2a1520ca031 100644 (file)
@@ -9,6 +9,7 @@
 #[cfg(std)] use std::fmt;
 #[cfg(core)] use core::fmt;
 #[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {}
+#[cfg(core)] #[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0;
 #[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} }
 
 // (see documentation of the similarly-named test in run-pass)
index 26445f2c5c542c318f756ad4cf4e02c92d4cf02e..4372d89fbf5220170906f086313b8fcdf45ab7c6 100644 (file)
@@ -1,59 +1,59 @@
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:20:23
+  --> $DIR/macro-comma-behavior.rs:21:23
    |
 LL |     assert_eq!(1, 1, "{}",);
    |                       ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:23:23
+  --> $DIR/macro-comma-behavior.rs:24:23
    |
 LL |     assert_ne!(1, 2, "{}",);
    |                       ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:29:29
+  --> $DIR/macro-comma-behavior.rs:30:29
    |
 LL |     debug_assert_eq!(1, 1, "{}",);
    |                             ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:32:29
+  --> $DIR/macro-comma-behavior.rs:33:29
    |
 LL |     debug_assert_ne!(1, 2, "{}",);
    |                             ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:37:18
+  --> $DIR/macro-comma-behavior.rs:38:18
    |
 LL |         eprint!("{}",);
    |                  ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:49:18
+  --> $DIR/macro-comma-behavior.rs:50:18
    |
 LL |         format!("{}",);
    |                  ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:53:19
+  --> $DIR/macro-comma-behavior.rs:54:19
    |
 LL |     format_args!("{}",);
    |                   ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:60:17
+  --> $DIR/macro-comma-behavior.rs:61:17
    |
 LL |         print!("{}",);
    |                 ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:71:21
+  --> $DIR/macro-comma-behavior.rs:72:21
    |
 LL |     unimplemented!("{}",);
    |                     ^^
 
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-comma-behavior.rs:80:24
+  --> $DIR/macro-comma-behavior.rs:81:24
    |
 LL |             write!(f, "{}",)?;
    |                        ^^
diff --git a/src/test/ui/moves/issue-75904-move-closure-loop.rs b/src/test/ui/moves/issue-75904-move-closure-loop.rs
new file mode 100644 (file)
index 0000000..6641a03
--- /dev/null
@@ -0,0 +1,15 @@
+// Regression test for issue #75904
+// Tests that we point at an expression
+// that required the upvar to be moved, rather than just borrowed.
+
+struct NotCopy;
+
+fn main() {
+    let mut a = NotCopy;
+    loop {
+        || { //~ ERROR use of moved value
+            &mut a;
+            a;
+        };
+    }
+}
diff --git a/src/test/ui/moves/issue-75904-move-closure-loop.stderr b/src/test/ui/moves/issue-75904-move-closure-loop.stderr
new file mode 100644 (file)
index 0000000..5e427a1
--- /dev/null
@@ -0,0 +1,15 @@
+error[E0382]: use of moved value: `a`
+  --> $DIR/issue-75904-move-closure-loop.rs:10:9
+   |
+LL |     let mut a = NotCopy;
+   |         ----- move occurs because `a` has type `NotCopy`, which does not implement the `Copy` trait
+LL |     loop {
+LL |         || {
+   |         ^^ value moved into closure here, in previous iteration of loop
+LL |             &mut a;
+LL |             a;
+   |             - use occurs due to use in closure
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
index b070671cb250e4d1ee07f8d4fb2b7819ef40607b..4417fb926d96dff7cdb93903b019e86fa0560369 100644 (file)
@@ -10,7 +10,7 @@ fn foo(node: Box<List>) -> isize {
         Some(right) => consume(right),
         None => 0
     };
-    consume(node) + r //~ ERROR use of moved value: `node`
+    consume(node) + r //~ ERROR use of partially moved value: `node`
 }
 
 fn consume(v: Box<List>) -> isize {
index 952985fcddee6c18268718248bf22ad9e355ff07..49964e2a947c813069671b8d6790e2f7ea530b20 100644 (file)
@@ -1,13 +1,13 @@
-error[E0382]: use of moved value: `node`
+error[E0382]: use of partially moved value: `node`
   --> $DIR/moves-based-on-type-cyclic-types-issue-4821.rs:13:13
    |
 LL |         Some(right) => consume(right),
-   |              ----- value moved here
+   |              ----- value partially moved here
 ...
 LL |     consume(node) + r
    |             ^^^^ value used here after partial move
    |
-   = note: move occurs because value has type `std::boxed::Box<List>`, which does not implement the `Copy` trait
+   = note: partial move occurs because value has type `std::boxed::Box<List>`, which does not implement the `Copy` trait
 help: borrow this field in the pattern to avoid moving `node.next.0`
    |
 LL |         Some(ref right) => consume(right),
index 1290d4a25abc4d8e52f6a4846fb8e197d2d47109..75fc6085f0aaaf3e168a7decdf1f99f7cbc604bf 100644 (file)
@@ -13,9 +13,9 @@ fn f10() {
         Foo {f} => {}
     };
 
-    touch(&x); //~ ERROR borrow of moved value: `x`
+    touch(&x); //~ ERROR borrow of partially moved value: `x`
     //~^ value borrowed here after partial move
-    //~| move occurs because `x.f` has type `std::string::String`
+    //~| partial move occurs because `x.f` has type `std::string::String`
 }
 
 fn main() {}
index 322999a1f0ff9bd20114d695f580fa1aee700bbf..2ee8d8d0b755cebdee8c5dcf25c90039ed7e1844 100644 (file)
@@ -1,13 +1,13 @@
-error[E0382]: borrow of moved value: `x`
+error[E0382]: borrow of partially moved value: `x`
   --> $DIR/moves-based-on-type-match-bindings.rs:16:11
    |
 LL |         Foo {f} => {}
-   |              - value moved here
+   |              - value partially moved here
 ...
 LL |     touch(&x);
    |           ^^ value borrowed here after partial move
    |
-   = note: move occurs because `x.f` has type `std::string::String`, which does not implement the `Copy` trait
+   = note: partial move occurs because `x.f` has type `std::string::String`, which does not implement the `Copy` trait
 
 error: aborting due to previous error
 
index e7caf89e78391d2b50eec6e068edd5afbe67764e..d266c6bb6584ffc019980b83dc180f802e12bea1 100644 (file)
@@ -1,5 +1,5 @@
 fn main() {
     let x = (vec![1, 2, 3], );
     drop(x.0);
-    drop(x); //~ ERROR use of moved value
+    drop(x); //~ ERROR use of partially moved value
 }
index 7030d5b3305f1181990d13e7b20844d6deabcf1d..d86801cf296b599b2cb82cfdc28ec577d7cdc0fa 100644 (file)
@@ -1,12 +1,12 @@
-error[E0382]: use of moved value: `x`
+error[E0382]: use of partially moved value: `x`
   --> $DIR/move-subpaths-moves-root.rs:4:10
    |
 LL |     drop(x.0);
-   |          --- value moved here
+   |          --- value partially moved here
 LL |     drop(x);
    |          ^ value used here after partial move
    |
-   = note: move occurs because `x.0` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
+   = note: partial move occurs because `x.0` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
 
 error: aborting due to previous error
 
index 58e45ff73a5e645e389443a9f490df2f979f9f80..bef630d826c0f8aff05af84b0ecb2dd5caeaf3c1 100644 (file)
@@ -12,4 +12,5 @@ fn main() {
 }
 
 #[lang = "eh_personality"] extern fn eh_personality() {}
+#[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0;
 #[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} }
index 3e5cdad7ab936b865fc98b782a5b5d6ff9b22e30..b9ef2f329414e8db0ca087e0bb5fac2c41bd87d9 100644 (file)
@@ -11,3 +11,5 @@
 fn panic_impl(info: &PanicInfo) -> ! { loop {} }
 #[lang = "eh_personality"]
 fn eh_personality() {}
+#[lang = "eh_catch_typeinfo"]
+static EH_CATCH_TYPEINFO: u8 = 0;
index c6a3cc346fc808cd4d2c75e89c27a6586d0f733e..f5d5823e468b0046736739469c926bf4b65e0956 100644 (file)
@@ -14,6 +14,9 @@
 #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
 #[lang = "eh_personality"]
 extern fn eh_personality() {}
+#[cfg(target_os = "emscripten")]
+#[lang = "eh_catch_typeinfo"]
+static EH_CATCH_TYPEINFO: u8 = 0;
 
 
 // take a reference to any built-in range
index 909340611328a47e1364c8560790998dc5d1d762..5537ed4576763ebacc98800fbfe726d94241ba92 100644 (file)
@@ -1,7 +1,7 @@
 error: `#[panic_handler]` function required, but not found
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:24:16
+  --> $DIR/issue-54505-no-std.rs:27:16
    |
 LL |     take_range(0..1);
    |                ^^^^
@@ -13,7 +13,7 @@ LL |     take_range(0..1);
                  found struct `core::ops::Range<{integer}>`
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:29:16
+  --> $DIR/issue-54505-no-std.rs:32:16
    |
 LL |     take_range(1..);
    |                ^^^
@@ -25,7 +25,7 @@ LL |     take_range(1..);
                  found struct `core::ops::RangeFrom<{integer}>`
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:34:16
+  --> $DIR/issue-54505-no-std.rs:37:16
    |
 LL |     take_range(..);
    |                ^^
@@ -37,7 +37,7 @@ LL |     take_range(..);
                  found struct `core::ops::RangeFull`
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:39:16
+  --> $DIR/issue-54505-no-std.rs:42:16
    |
 LL |     take_range(0..=1);
    |                ^^^^^
@@ -49,7 +49,7 @@ LL |     take_range(0..=1);
                  found struct `core::ops::RangeInclusive<{integer}>`
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:44:16
+  --> $DIR/issue-54505-no-std.rs:47:16
    |
 LL |     take_range(..5);
    |                ^^^
@@ -61,7 +61,7 @@ LL |     take_range(..5);
                  found struct `core::ops::RangeTo<{integer}>`
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:49:16
+  --> $DIR/issue-54505-no-std.rs:52:16
    |
 LL |     take_range(..=42);
    |                ^^^^^
index 540b35e0392de1ef5aad30813fc508b5805e6291..7e7a15924b7653128c9c5a2db10d6d7e28fd88f9 100644 (file)
@@ -1,6 +1,5 @@
 // run-pass
 // Test inclusive range syntax.
-#![feature(range_is_empty)]
 #![allow(unused_braces)]
 #![allow(unused_comparisons)]
 
index bdcd47a7260d30f22664b0ba720117a9bea2110b..2e07fdcbe830cd8074492028bd8ed67da90f1549 100644 (file)
@@ -48,7 +48,7 @@ unsafe fn allocate(layout: Layout) -> *mut u8 {
             println!("allocate({:?}) = {:?}", layout, ptr);
         }
 
-        ptr.as_non_null_ptr().as_ptr()
+        ptr.as_mut_ptr()
     }
 
     unsafe fn deallocate(ptr: *mut u8, layout: Layout) {
@@ -65,23 +65,17 @@ unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 {
         }
 
         let memory = if new.size() > old.size() {
-            Global.grow(
-                NonNull::new_unchecked(ptr),
-                old,
-                new.size(),
-            )
+            Global.grow(NonNull::new_unchecked(ptr), old, new)
         } else {
-            Global.shrink(NonNull::new_unchecked(ptr), old, new.size())
+            Global.shrink(NonNull::new_unchecked(ptr), old, new)
         };
 
-        let ptr = memory.unwrap_or_else(|_| {
-            handle_alloc_error(Layout::from_size_align_unchecked(new.size(), old.align()))
-        });
+        let ptr = memory.unwrap_or_else(|_| handle_alloc_error(new));
 
         if PRINT {
             println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, ptr);
         }
-        ptr.as_non_null_ptr().as_ptr()
+        ptr.as_mut_ptr()
     }
 
     fn idx_to_size(i: usize) -> usize {
index 49d199cd9e75422731e1334cab1c933eb593da10..346d118f0f9a9dd91140c4442cefcaf195ba2c4b 100644 (file)
@@ -13,5 +13,5 @@ fn main() {
         (Some(y), ()) => {},
         _ => {},
     }
-    x; //~ ERROR use of moved value
+    x; //~ ERROR use of partially moved value
 }
index 97d2c174d9adbf082682c9b7e5d476eb967815ea..313ad087c348ddb83353054702e3f2cd9dbf2529 100644 (file)
@@ -18,16 +18,16 @@ LL |     let mut y = x;
 LL |     x;
    |     ^ value used here after move
 
-error[E0382]: use of moved value: `x`
+error[E0382]: use of partially moved value: `x`
   --> $DIR/ref-suggestion.rs:16:5
    |
 LL |         (Some(y), ()) => {},
-   |               - value moved here
+   |               - value partially moved here
 ...
 LL |     x;
    |     ^ value used here after partial move
    |
-   = note: move occurs because value has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
+   = note: partial move occurs because value has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
 help: borrow this field in the pattern to avoid moving `x.0.0`
    |
 LL |         (Some(ref y), ()) => {},
index 906b543e421229e20a3f67af6201b2dd4118193f..13f9507d8db91ad7f1695480e17b9f5d9332e6dd 100644 (file)
@@ -5,7 +5,7 @@ LL |         let y = *x;
    |                 -- value moved here
 LL |         drop_unsized(y);
 LL |         println!("{}", &x);
-   |                        ^^ value borrowed here after partial move
+   |                        ^^ value borrowed here after move
    |
    = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
 
@@ -27,7 +27,7 @@ LL |         let y = *x;
    |                 -- value moved here
 LL |         y.foo();
 LL |         println!("{}", &x);
-   |                        ^^ value borrowed here after partial move
+   |                        ^^ value borrowed here after move
    |
    = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
 
index 49b2031c6b9d969a12b82b603d77b3629920697c..5b29314ad55a4ab5490c9ae900b57d5456bee7a7 100644 (file)
@@ -14,7 +14,7 @@ error[E0382]: use of moved value: `x`
 LL |         let _y = *x;
    |                  -- value moved here
 LL |         drop_unsized(x);
-   |                      ^ value used here after partial move
+   |                      ^ value used here after move
    |
    = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
 
@@ -50,7 +50,7 @@ error[E0382]: use of moved value: `x`
 LL |         let _y = *x;
    |                  -- value moved here
 LL |         x.foo();
-   |         ^ value used here after partial move
+   |         ^ value used here after move
    |
    = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait
 
index 2bad3b9c57f0c95f05d5071bd0c45560f55c88a5..e70da43ab47abe45cb278e82216729723d7d37b0 100644 (file)
@@ -1,6 +1,6 @@
 [alias]
 uitest = "test --test compile-test"
-dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
+dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
 
 [build]
 rustflags = ["-Zunstable-options"]
index 5fa8009a8b42c8fad795d61114ad70deacdcd5fc..99e371631b149bbef1b7f537fbfd19fcc20eb982 100644 (file)
@@ -92,6 +92,13 @@ jobs:
       env:
         OS: ${{ runner.os }}
 
+    - name: Test cargo dev new lint
+      run: |
+        cargo dev new_lint --name new_early_pass --pass early
+        cargo dev new_lint --name new_late_pass --pass late
+        cargo check
+        git reset --hard HEAD
+
     # Cleanup
     - name: Run cargo-cache --autoclean
       run: |
index 5e9ed54c848205e499dd85cb43146d9b1d3f6f1e..137b561028a658fae51e18d0a5a16601a1a66e9c 100644 (file)
@@ -1498,6 +1498,7 @@ Released 2018-09-13
 [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
+[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
@@ -1690,6 +1691,7 @@ Released 2018-09-13
 [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
+[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
@@ -1699,6 +1701,7 @@ Released 2018-09-13
 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
+[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
 [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
@@ -1723,6 +1726,7 @@ Released 2018-09-13
 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
 [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
+[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
@@ -1752,6 +1756,7 @@ Released 2018-09-13
 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
+[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
@@ -1773,6 +1778,7 @@ Released 2018-09-13
 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
 [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
+[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
 [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
 [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
index 5f7b1e85ee9a1769f6de383ca49bd5c433e47293..54777810abbdfac70346b952f2510388aa1e789c 100644 (file)
@@ -189,6 +189,35 @@ Clippy in the `rust-lang/rust` repository.
 For general information about `subtree`s in the Rust repository see [Rust's
 `CONTRIBUTING.md`][subtree].
 
+### Patching git-subtree to work with big repos
+
+Currently there's a bug in `git-subtree` that prevents it from working properly
+with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale.
+Before continuing with the following steps, we need to manually apply that fix to
+our local copy of `git-subtree`.
+
+You can get the patched version of `git-subtree` from [here][gitgitgadget-pr].
+Put this file under `/usr/lib/git-core` (taking a backup of the previous file)
+and make sure it has the proper permissions:
+
+```bash
+sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree
+sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
+sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
+```
+
+_Note:_ The first time running `git subtree push` a cache has to be built. This
+involves going through the complete Clippy history once. For this you have to
+increase the stack limit though, which you can do with `ulimit -s 60000`.
+Make sure to run the `ulimit` command from the same session you call git subtree.
+
+_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`.
+This shell has a hardcoded recursion limit set to 1000. In order to make this process work,
+you need to force the script to run `bash` instead. You can do this by editing the first
+line of the `git-subtree` script and changing `sh` to `bash`.
+
+### Performing the sync
+
 Here is a TL;DR version of the sync process (all of the following commands have
 to be run inside the `rust` directory):
 
@@ -198,6 +227,7 @@ to be run inside the `rust` directory):
     # Make sure to change `your-github-name` to your github name in the following command
     git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
     ```
+
     _Note:_ This will directly push to the remote repository. You can also push
     to your local copy by replacing the remote address with `/path/to/rust-clippy`
     directory.
@@ -213,14 +243,30 @@ to be run inside the `rust` directory):
 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
    accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
    ~~annoy~~ ask them in the [Discord] channel.)
-4. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
+   
+### Syncing back changes in Clippy to [`rust-lang/rust`]
+
+To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back
+in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
+the Rust stable release and then every other week. That way we guarantee that
+every feature in Clippy is available for 2 weeks in nightly, before it can get to beta.
+For reference, the first sync following this cadence was performed the 2020-08-27.
+
+All of the following commands have to be run inside the `rust` directory.
+
+1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous
+section if necessary.
+
+2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
     ```bash
     git checkout -b sync-from-clippy
     git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master
     ```
-5. Open a PR to [`rust-lang/rust`]
+3. Open a PR to [`rust-lang/rust`]
+
+### Defining remotes
 
-Also, you may want to define remotes, so you don't have to type out the remote
+You may want to define remotes, so you don't have to type out the remote
 addresses on every sync. You can do this with the following commands (these
 commands still have to be run inside the `rust` directory):
 
@@ -241,12 +287,6 @@ You can then sync with the remote names from above, e.g.:
 $ git subtree push -P src/tools/clippy clippy-local sync-from-rust
 ```
 
-_Note:_ The first time running `git subtree push` a cache has to be built. This
-involves going through the complete Clippy history once. For this you have to
-increase the stack limit though, which you can do with `ulimit -s 60000`. For
-this to work, you will need the fix of `git subtree` available
-[here][gitgitgadget-pr].
-
 [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
 [subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
 [`rust-lang/rust`]: https://github.com/rust-lang/rust
index 836897927b01530d8227ce135a433c9168829d50..c7a3099b8ab0a4f0857cce4b5d079b359a27e85e 100644 (file)
@@ -31,13 +31,13 @@ path = "src/driver.rs"
 # begin automatic update
 clippy_lints = { version = "0.0.212", path = "clippy_lints" }
 # end automatic update
-semver = "0.9"
+semver = "0.10"
 rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
 tempfile = { version = "3.1.0", optional = true }
 lazy_static = "1.0"
 
 [dev-dependencies]
-cargo_metadata = "0.9.1"
+cargo_metadata = "0.11.1"
 compiletest_rs = { version = "0.5.0", features = ["tmp"] }
 tester = "0.7"
 lazy_static = "1.0"
index 1e032a7bc20cde319cd7442e4d5360d4285f2232..d951ca0e6308d154d8d121b8bf1880067656015d 100644 (file)
@@ -47,7 +47,7 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str
 fn create_lint(lint: &LintData) -> io::Result<()> {
     let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
         "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
-        "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
+        "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
         _ => {
             unreachable!("`pass_type` should only ever be `early` or `late`!");
         },
index e959c1a65112286fc14688e20c3906be95a69d23..cc7d3a04f003ee0728feb89aa83ef147fe12e7ac 100644 (file)
@@ -17,7 +17,7 @@ keywords = ["clippy", "lint", "plugin"]
 edition = "2018"
 
 [dependencies]
-cargo_metadata = "0.9.1"
+cargo_metadata = "0.11.1"
 if_chain = "1.0.0"
 itertools = "0.9"
 lazy_static = "1.0.2"
@@ -28,7 +28,7 @@ serde = { version = "1.0", features = ["derive"] }
 smallvec = { version = "1", features = ["union"] }
 toml = "0.5.3"
 unicode-normalization = "0.1"
-semver = "0.9.0"
+semver = "0.10.0"
 # NOTE: cargo requires serde feat in its url dep
 # see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
 url = { version =  "2.1.0", features = ["serde"] }
index dab1e96e282f051c75f8184b718f745fb711cace..b3185b88840142253648eb1eb033dca657d20aa6 100644 (file)
@@ -1,5 +1,5 @@
 use crate::utils::{
-    get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
+    eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method,
 };
 use crate::utils::{higher, sugg};
 use if_chain::if_chain;
@@ -70,11 +70,11 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                         return;
                     }
                     // lhs op= l op r
-                    if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
+                    if eq_expr_value(cx, lhs, l) {
                         lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
                     }
                     // lhs op= l commutative_op r
-                    if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
+                    if is_commutative(op.node) && eq_expr_value(cx, lhs, r) {
                         lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
                     }
                 }
@@ -161,14 +161,12 @@ macro_rules! ops {
 
                     if visitor.counter == 1 {
                         // a = a op b
-                        if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) {
+                        if eq_expr_value(cx, assignee, l) {
                             lint(assignee, r);
                         }
                         // a = b commutative_op a
                         // Limited to primitive type as these ops are know to be commutative
-                        if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r)
-                            && cx.typeck_results().expr_ty(assignee).is_primitive_ty()
-                        {
+                        if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
                             match op.node {
                                 hir::BinOpKind::Add
                                 | hir::BinOpKind::Mul
@@ -253,7 +251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
     type Map = Map<'tcx>;
 
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
-        if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) {
+        if eq_expr_value(self.cx, self.assignee, expr) {
             self.counter += 1;
         }
 
index 76ccf2e4bb98b0f37febdd73a133dcae08c2c952..cfcc1b3c5f356948df9bb333ccc292dd7a12e06e 100644 (file)
@@ -5,8 +5,8 @@
     span_lint_and_sugg, span_lint_and_then, without_block_comments,
 };
 use if_chain::if_chain;
-use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_ast::util::lev_distance::find_best_match_for_name;
+use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_errors::Applicability;
 use rustc_hir::{
     Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
index 18529f2113e77bc16a36b798f3a71ac1539c2917..280a2c7fe6770c68987354a8c7bfc7f8c527ef02 100644 (file)
@@ -1,6 +1,6 @@
 use crate::utils::{
-    get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg,
-    span_lint_and_then, SpanlessEq,
+    eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt,
+    span_lint_and_sugg, span_lint_and_then,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
@@ -128,7 +128,7 @@ fn negate(bin_op_kind: BinOpKind) -> Option<BinOpKind> {
             }
         }
         for (n, expr) in self.terminals.iter().enumerate() {
-            if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
+            if eq_expr_value(self.cx, e, expr) {
                 #[allow(clippy::cast_possible_truncation)]
                 return Ok(Bool::Term(n as u8));
             }
@@ -138,8 +138,8 @@ fn negate(bin_op_kind: BinOpKind) -> Option<BinOpKind> {
                 if implements_ord(self.cx, e_lhs);
                 if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind;
                 if negate(e_binop.node) == Some(expr_binop.node);
-                if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs);
-                if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs);
+                if eq_expr_value(self.cx, e_lhs, expr_lhs);
+                if eq_expr_value(self.cx, e_rhs, expr_rhs);
                 then {
                     #[allow(clippy::cast_possible_truncation)]
                     return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
index 1f8bff8d71e0f33d77c8e1c3b06237196d98baf6..10a64769585e543bdbfd46ae8cd59287080bfd5f 100644 (file)
@@ -1,5 +1,5 @@
+use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash};
 use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then};
-use crate::utils::{SpanlessEq, SpanlessHash};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -197,8 +197,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
         h.finish()
     };
 
-    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool =
-        &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) };
+    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) };
 
     for (i, j) in search_same(conds, hash, eq) {
         span_lint_and_note(
@@ -222,7 +221,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 
     let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
         // Do not spawn warning if `IFS_SAME_COND` already produced it.
-        if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) {
+        if eq_expr_value(cx, lhs, rhs) {
             return false;
         }
         SpanlessEq::new(cx).eq_expr(lhs, rhs)
index 6ce36fd2360e1e9569cb748e566a3b38ce57ef4a..9555459e240e9f874ba29132cb32eb47fe531161 100644 (file)
@@ -1,16 +1,22 @@
 use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint};
 use if_chain::if_chain;
 use itertools::Itertools;
-use rustc_ast::ast::{AttrKind, Attribute};
+use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind};
 use rustc_ast::token::CommentKind;
 use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::emitter::EmitterWriter;
+use rustc_errors::Handler;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
+use rustc_parse::maybe_new_parser_from_source_str;
+use rustc_session::parse::ParseSess;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::{BytePos, MultiSpan, Span};
-use rustc_span::Pos;
+use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
+use rustc_span::{FileName, Pos};
+use std::io;
 use std::ops::Range;
 use url::Url;
 
@@ -431,10 +437,67 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     headers
 }
 
-static LEAVE_MAIN_PATTERNS: &[&str] = &["static", "fn main() {}", "extern crate", "async fn main() {"];
-
 fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
-    if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) {
+    fn has_needless_main(code: &str) -> bool {
+        let filename = FileName::anon_source_code(code);
+
+        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+        let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
+        let handler = Handler::with_emitter(false, None, box emitter);
+        let sess = ParseSess::with_span_handler(handler, sm);
+
+        let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
+            Ok(p) => p,
+            Err(errs) => {
+                for mut err in errs {
+                    err.cancel();
+                }
+                return false;
+            },
+        };
+
+        let mut relevant_main_found = false;
+        loop {
+            match parser.parse_item() {
+                Ok(Some(item)) => match &item.kind {
+                    // Tests with one of these items are ignored
+                    ItemKind::Static(..)
+                    | ItemKind::Const(..)
+                    | ItemKind::ExternCrate(..)
+                    | ItemKind::ForeignMod(..) => return false,
+                    // We found a main function ...
+                    ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => {
+                        let is_async = matches!(sig.header.asyncness, Async::Yes{..});
+                        let returns_nothing = match &sig.decl.output {
+                            FnRetTy::Default(..) => true,
+                            FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
+                            _ => false,
+                        };
+
+                        if returns_nothing && !is_async && !block.stmts.is_empty() {
+                            // This main function should be linted, but only if there are no other functions
+                            relevant_main_found = true;
+                        } else {
+                            // This main function should not be linted, we're done
+                            return false;
+                        }
+                    },
+                    // Another function was found; this case is ignored too
+                    ItemKind::Fn(..) => return false,
+                    _ => {},
+                },
+                Ok(None) => break,
+                Err(mut e) => {
+                    e.cancel();
+                    return false;
+                },
+            }
+        }
+
+        relevant_main_found
+    }
+
+    if has_needless_main(text) {
         span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
     }
 }
index bae7c4647d487a898c944adbaa0d62aa2c124dd3..19f56195ec1b483d693c8e20d6464ca4864c0fa9 100644 (file)
@@ -6,7 +6,7 @@
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
+use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for double comparisons that could be simplified to a single expression.
@@ -46,8 +46,7 @@ fn check_binop(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs:
             },
             _ => return,
         };
-        let mut spanless_eq = SpanlessEq::new(cx).ignore_fn();
-        if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) {
+        if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) {
             return;
         }
         macro_rules! lint_double_comparison {
index 1dfb2eaa579728d98bf628f9e3b467f9ca6725c5..8ece44878fe32c6c7bcd1777e717cd6e185137ec 100644 (file)
@@ -56,7 +56,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     cx,
                     DURATION_SUBSEC,
                     expr.span,
-                    &format!("Calling `{}()` is more concise than this calculation", suggested_fn),
+                    &format!("calling `{}()` is more concise than this calculation", suggested_fn),
                     "try",
                     format!(
                         "{}.{}()",
index 91214f277be695cd6fbf83eb65d18b704509069b..48caf48dbdb2cab0e80f0e2f4ee70e631c225a62 100644 (file)
@@ -72,7 +72,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
                             cx,
                             ENUM_CLIKE_UNPORTABLE_VARIANT,
                             var.span,
-                            "Clike enum variant discriminant is not portable to 32-bit targets",
+                            "C-like enum variant discriminant is not portable to 32-bit targets",
                         );
                     };
                 }
index cb0fd59a2d4079acd2835fb752cd3e045cb43b1f..a9294a87f15d060d23878f0ff1a15705713d8395 100644 (file)
@@ -183,10 +183,10 @@ fn check_variant(
             && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
             && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
         {
-            span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
+            span_lint(cx, lint, var.span, "variant name starts with the enum's name");
         }
         if partial_rmatch(item_name, &name) == item_name_chars {
-            span_lint(cx, lint, var.span, "Variant name ends with the enum's name");
+            span_lint(cx, lint, var.span, "variant name ends with the enum's name");
         }
     }
     let first = &def.variants[0].ident.name.as_str();
@@ -227,7 +227,7 @@ fn check_variant(
         cx,
         lint,
         span,
-        &format!("All variants have the same {}fix: `{}`", what, value),
+        &format!("all variants have the same {}fix: `{}`", what, value),
         None,
         &format!(
             "remove the {}fixes and use full paths to \
index 140cd21c34e67916b9922565cbaf11ec46a9adef..e16ec783fab79c6c6744bcfaa958786ca7e912e5 100644 (file)
@@ -1,5 +1,5 @@
 use crate::utils::{
-    implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq,
+    eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then,
 };
 use rustc_errors::Applicability;
 use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind};
@@ -69,7 +69,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
             if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
                 return;
             }
-            if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
+            if is_valid_operator(op) && eq_expr_value(cx, left, right) {
                 span_lint(
                     cx,
                     EQ_OP,
diff --git a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
new file mode 100644 (file)
index 0000000..9ac5a45
--- /dev/null
@@ -0,0 +1,110 @@
+use crate::utils::{match_qpath, paths, span_lint_and_then, sugg};
+use if_chain::if_chain;
+use rustc_ast::util::parser::AssocOp;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or
+     /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
+     ///
+     /// **Why is this bad?** The code without `.abs()` is more likely to have a bug.
+     ///
+     /// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is
+     /// technically unneccessary. However, it will make the code more robust and doesn't have any
+     /// large performance implications. If the abs call was deliberately left out for performance
+     /// reasons, it is probably better to state this explicitly in the code, which then can be done
+     /// with an allow.
+     ///
+     /// **Example:**
+     ///
+     /// ```rust
+     /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
+     ///     (a - b) < f32::EPSILON
+     /// }
+     /// ```
+     /// Use instead:
+     /// ```rust
+     /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
+     ///     (a - b).abs() < f32::EPSILON
+     /// }
+     /// ```
+    pub FLOAT_EQUALITY_WITHOUT_ABS,
+    correctness,
+    "float equality check without `.abs()`"
+}
+
+declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]);
+
+impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let lhs;
+        let rhs;
+
+        // check if expr is a binary expression with a lt or gt operator
+        if let ExprKind::Binary(op, ref left, ref right) = expr.kind {
+            match op.node {
+                BinOpKind::Lt => {
+                    lhs = left;
+                    rhs = right;
+                },
+                BinOpKind::Gt => {
+                    lhs = right;
+                    rhs = left;
+                },
+                _ => return,
+            };
+        } else {
+            return;
+        }
+
+        if_chain! {
+
+            // left hand side is a substraction
+            if let ExprKind::Binary(
+                Spanned {
+                    node: BinOpKind::Sub,
+                    ..
+                },
+                val_l,
+                val_r,
+            ) = lhs.kind;
+
+            // right hand side matches either f32::EPSILON or f64::EPSILON
+            if let ExprKind::Path(ref epsilon_path) = rhs.kind;
+            if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON);
+
+            // values of the substractions on the left hand side are of the type float
+            let t_val_l = cx.typeck_results().expr_ty(val_l);
+            let t_val_r = cx.typeck_results().expr_ty(val_r);
+            if let ty::Float(_) = t_val_l.kind;
+            if let ty::Float(_) = t_val_r.kind;
+
+            then {
+                let sug_l = sugg::Sugg::hir(cx, &val_l, "..");
+                let sug_r = sugg::Sugg::hir(cx, &val_r, "..");
+                // format the suggestion
+                let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
+                // spans the lint
+                span_lint_and_then(
+                    cx,
+                    FLOAT_EQUALITY_WITHOUT_ABS,
+                    expr.span,
+                    "float equality check without `.abs()`",
+                    | diag | {
+                        diag.span_suggestion(
+                            lhs.span,
+                            "add `.abs()`",
+                            suggestion,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                );
+            }
+        }
+    }
+}
index 93f6ec92ec71328512aeb92b4b6b5976139fc45b..1b02cee126d03b71af78ed69cba56e3cddbd9905 100644 (file)
@@ -2,7 +2,7 @@
     constant, constant_simple, Constant,
     Constant::{Int, F32, F64},
 };
-use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
+use crate::utils::{eq_expr_value, get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@@ -363,8 +363,8 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
         if_chain! {
             if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind;
             if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind;
-            if are_exprs_equal(cx, lmul_lhs, lmul_rhs);
-            if are_exprs_equal(cx, rmul_lhs, rmul_rhs);
+            if eq_expr_value(cx, lmul_lhs, lmul_rhs);
+            if eq_expr_value(cx, rmul_lhs, rmul_rhs);
             then {
                 return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, "..")));
             }
@@ -502,8 +502,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
 fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
     if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
         match op {
-            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test),
-            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test),
+            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
+            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
             _ => false,
         }
     } else {
@@ -515,8 +515,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
 fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
     if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
         match op {
-            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test),
-            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test),
+            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
+            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
             _ => false,
         }
     } else {
@@ -524,10 +524,6 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
     }
 }
 
-fn are_exprs_equal(cx: &LateContext<'_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool {
-    SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2)
-}
-
 /// Returns true iff expr is some zero literal
 fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match constant_simple(cx, cx.typeck_results(), expr) {
@@ -546,12 +542,12 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 /// returns None.
 fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
     if let ExprKind::Unary(UnOp::UnNeg, expr1_negated) = &expr1.kind {
-        if are_exprs_equal(cx, expr1_negated, expr2) {
+        if eq_expr_value(cx, expr1_negated, expr2) {
             return Some((false, expr2));
         }
     }
     if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind {
-        if are_exprs_equal(cx, expr1, expr2_negated) {
+        if eq_expr_value(cx, expr1, expr2_negated) {
             return Some((true, expr1));
         }
     }
@@ -614,7 +610,7 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>
                 args_a.len() == args_b.len() &&
                 (
                     ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
-                    method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1])
+                    method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
                 );
         }
     }
index 5b22df5fe491e70e3afcadb2933e176a6492ff47..28b20cdeac343ecdf30c3cf66be49aa909765b96 100644 (file)
@@ -61,8 +61,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     cx,
                     IF_LET_SOME_RESULT,
                     expr.span.with_hi(op.span.hi()),
-                    "Matching on `Some` with `ok()` is redundant",
-                    &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
+                    "matching on `Some` with `ok()` is redundant",
+                    &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
                     sugg,
                     applicability,
                 );
index c11e291f98e4b41766dbf22c8940d8f9fff3b74c..b86d2e766566bd16391dde7b738d288d873050ac 100644 (file)
@@ -60,7 +60,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
                             cx,
                             IF_NOT_ELSE,
                             item.span,
-                            "Unnecessary boolean `not` operation",
+                            "unnecessary boolean `not` operation",
                             None,
                             "remove the `!` and swap the blocks of the `if`/`else`",
                         );
@@ -70,7 +70,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
                             cx,
                             IF_NOT_ELSE,
                             item.span,
-                            "Unnecessary `!=` operation",
+                            "unnecessary `!=` operation",
                             None,
                             "change to `==` and swap the blocks of the `if`/`else`",
                         );
index 5f931a0addedf6b3781460b545d15a7ec46dc629..b57fe8dc4269e341b7f6299cae07331aff409562 100644 (file)
@@ -158,9 +158,9 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) {
         cx,
         IMPLICIT_SATURATING_SUB,
         expr.span,
-        "Implicitly performing saturating subtraction",
+        "implicitly performing saturating subtraction",
         "try",
-        format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()),
+        format!("{} = {}.saturating_sub({});", var_name, var_name, '1'),
         Applicability::MachineApplicable,
     );
 }
index 9fb10c7f62768583ec311d0748f68796cf0a5b32..4e6bb604785417977320c72d6de98ad623abd59e 100644 (file)
@@ -81,9 +81,9 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'_>) {
                             cx,
                             MULTIPLE_INHERENT_IMPL,
                             *additional_span,
-                            "Multiple implementations of this structure",
+                            "multiple implementations of this structure",
                             |diag| {
-                                diag.span_note(*initial_span, "First implementation here");
+                                diag.span_note(*initial_span, "first implementation here");
                             },
                         )
                     })
index e91fb0c2f27cd0340caf1ace6afb0fb8e70d7098..c629ee05ab97c1e2740441170c3ad142c857fbb7 100644 (file)
@@ -152,7 +152,7 @@ fn emit_warning(cx: &EarlyContext<'_>, block: &Expr, recommendation: String) {
             cx,
             INT_PLUS_ONE,
             block.span,
-            "Unnecessary `>= y + 1` or `x - 1 >=`",
+            "unnecessary `>= y + 1` or `x - 1 >=`",
             "change it to",
             recommendation,
             Applicability::MachineApplicable, // snippet
@@ -163,8 +163,8 @@ fn emit_warning(cx: &EarlyContext<'_>, block: &Expr, recommendation: String) {
 impl EarlyLintPass for IntPlusOne {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
         if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind {
-            if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
-                Self::emit_warning(cx, item, rec.clone());
+            if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
+                Self::emit_warning(cx, item, rec);
             }
         }
     }
index e5daa30f8ca15745759706c41534552cb355d8d8..b691d363d2f2165ca03be278f0706a82f018e0bb 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -260,17 +260,6 @@ fn check_len(
 
 /// Checks if this type has an `is_empty` method.
 fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    /// Special case ranges until `range_is_empty` is stabilized. See issue 3807.
-    fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-        higher::range(expr).map_or(false, |_| {
-            !cx.tcx
-                .features()
-                .declared_lib_features
-                .iter()
-                .any(|(name, _)| name.as_str() == "range_is_empty")
-        })
-    }
-
     /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
     fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
         if let ty::AssocKind::Fn = item.kind {
@@ -296,10 +285,6 @@ fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
         })
     }
 
-    if should_skip_range(cx, expr) {
-        return false;
-    }
-
     let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr));
     match ty.kind {
         ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
diff --git a/src/tools/clippy/clippy_lints/src/let_and_return.rs b/src/tools/clippy/clippy_lints/src/let_and_return.rs
deleted file mode 100644 (file)
index fa560ff..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::hir::map::Map;
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then};
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for `let`-bindings, which are subsequently
-    /// returned.
-    ///
-    /// **Why is this bad?** It is just extraneous code. Remove it to make your code
-    /// more rusty.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// fn foo() -> String {
-    ///     let x = String::new();
-    ///     x
-    /// }
-    /// ```
-    /// instead, use
-    /// ```
-    /// fn foo() -> String {
-    ///     String::new()
-    /// }
-    /// ```
-    pub LET_AND_RETURN,
-    style,
-    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
-}
-
-declare_lint_pass!(LetReturn => [LET_AND_RETURN]);
-
-impl<'tcx> LateLintPass<'tcx> for LetReturn {
-    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
-        // we need both a let-binding stmt and an expr
-        if_chain! {
-            if let Some(retexpr) = block.expr;
-            if let Some(stmt) = block.stmts.iter().last();
-            if let StmtKind::Local(local) = &stmt.kind;
-            if local.ty.is_none();
-            if local.attrs.is_empty();
-            if let Some(initexpr) = &local.init;
-            if let PatKind::Binding(.., ident, _) = local.pat.kind;
-            if let ExprKind::Path(qpath) = &retexpr.kind;
-            if match_qpath(qpath, &[&*ident.name.as_str()]);
-            if !last_statement_borrows(cx, initexpr);
-            if !in_external_macro(cx.sess(), initexpr.span);
-            if !in_external_macro(cx.sess(), retexpr.span);
-            if !in_external_macro(cx.sess(), local.span);
-            if !in_macro(local.span);
-            then {
-                span_lint_and_then(
-                    cx,
-                    LET_AND_RETURN,
-                    retexpr.span,
-                    "returning the result of a `let` binding from a block",
-                    |err| {
-                        err.span_label(local.span, "unnecessary `let` binding");
-
-                        if let Some(snippet) = snippet_opt(cx, initexpr.span) {
-                            err.multipart_suggestion(
-                                "return the expression directly",
-                                vec![
-                                    (local.span, String::new()),
-                                    (retexpr.span, snippet),
-                                ],
-                                Applicability::MachineApplicable,
-                            );
-                        } else {
-                            err.span_help(initexpr.span, "this expression can be directly returned");
-                        }
-                    },
-                );
-            }
-        }
-    }
-}
-
-fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    let mut visitor = BorrowVisitor { cx, borrows: false };
-    walk_expr(&mut visitor, expr);
-    visitor.borrows
-}
-
-struct BorrowVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    borrows: bool,
-}
-
-impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if self.borrows {
-            return;
-        }
-
-        if let Some(def_id) = fn_def_id(self.cx, expr) {
-            self.borrows = self
-                .cx
-                .tcx
-                .fn_sig(def_id)
-                .output()
-                .skip_binder()
-                .walk()
-                .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
-        }
-
-        walk_expr(self, expr);
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-}
index aa1002636406ad29310267f2067c045e396689c4..577ce6523b491f4342a4138f75c229fe61bcd9f5 100644 (file)
@@ -193,6 +193,7 @@ macro_rules! declare_clippy_lint {
 mod exit;
 mod explicit_write;
 mod fallible_impl_from;
+mod float_equality_without_abs;
 mod float_literal;
 mod floating_point_arithmetic;
 mod format;
@@ -218,7 +219,6 @@ macro_rules! declare_clippy_lint {
 mod large_enum_variant;
 mod large_stack_arrays;
 mod len_zero;
-mod let_and_return;
 mod let_if_seq;
 mod let_underscore;
 mod lifetimes;
@@ -285,6 +285,7 @@ macro_rules! declare_clippy_lint {
 mod regex;
 mod repeat_once;
 mod returns;
+mod self_assignment;
 mod serde_api;
 mod shadow;
 mod single_component_path_imports;
@@ -296,6 +297,7 @@ macro_rules! declare_clippy_lint {
 mod tabs_in_doc_comments;
 mod temporary_assignment;
 mod to_digit_is_some;
+mod to_string_in_display;
 mod trait_bounds;
 mod transmute;
 mod transmuting_null;
@@ -310,7 +312,9 @@ macro_rules! declare_clippy_lint {
 mod unsafe_removed_from_name;
 mod unused_io_amount;
 mod unused_self;
+mod unused_unit;
 mod unwrap;
+mod unwrap_in_result;
 mod use_self;
 mod useless_conversion;
 mod vec;
@@ -547,6 +551,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &exit::EXIT,
         &explicit_write::EXPLICIT_WRITE,
         &fallible_impl_from::FALLIBLE_IMPL_FROM,
+        &float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
         &float_literal::EXCESSIVE_PRECISION,
         &float_literal::LOSSY_FLOAT_LITERAL,
         &floating_point_arithmetic::IMPRECISE_FLOPS,
@@ -586,7 +591,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &large_stack_arrays::LARGE_STACK_ARRAYS,
         &len_zero::LEN_WITHOUT_IS_EMPTY,
         &len_zero::LEN_ZERO,
-        &let_and_return::LET_AND_RETURN,
         &let_if_seq::USELESS_LET_IF_SEQ,
         &let_underscore::LET_UNDERSCORE_LOCK,
         &let_underscore::LET_UNDERSCORE_MUST_USE,
@@ -677,6 +681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &methods::SEARCH_IS_SOME,
         &methods::SHOULD_IMPLEMENT_TRAIT,
         &methods::SINGLE_CHAR_PATTERN,
+        &methods::SINGLE_CHAR_PUSH_STR,
         &methods::SKIP_WHILE_NEXT,
         &methods::STRING_EXTEND_CHARS,
         &methods::SUSPICIOUS_MAP,
@@ -684,6 +689,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &methods::UNINIT_ASSUMED_INIT,
         &methods::UNNECESSARY_FILTER_MAP,
         &methods::UNNECESSARY_FOLD,
+        &methods::UNNECESSARY_LAZY_EVALUATIONS,
         &methods::UNWRAP_USED,
         &methods::USELESS_ASREF,
         &methods::WRONG_PUB_SELF_CONVENTION,
@@ -769,8 +775,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &regex::INVALID_REGEX,
         &regex::TRIVIAL_REGEX,
         &repeat_once::REPEAT_ONCE,
+        &returns::LET_AND_RETURN,
         &returns::NEEDLESS_RETURN,
-        &returns::UNUSED_UNIT,
+        &self_assignment::SELF_ASSIGNMENT,
         &serde_api::SERDE_API_MISUSE,
         &shadow::SHADOW_REUSE,
         &shadow::SHADOW_SAME,
@@ -788,6 +795,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
         &temporary_assignment::TEMPORARY_ASSIGNMENT,
         &to_digit_is_some::TO_DIGIT_IS_SOME,
+        &to_string_in_display::TO_STRING_IN_DISPLAY,
         &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
         &trait_bounds::TYPE_REPETITION_IN_BOUNDS,
         &transmute::CROSSPOINTER_TRANSMUTE,
@@ -840,8 +848,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
         &unused_io_amount::UNUSED_IO_AMOUNT,
         &unused_self::UNUSED_SELF,
+        &unused_unit::UNUSED_UNIT,
         &unwrap::PANICKING_UNWRAP,
         &unwrap::UNNECESSARY_UNWRAP,
+        &unwrap_in_result::UNWRAP_IN_RESULT,
         &use_self::USE_SELF,
         &useless_conversion::USELESS_CONVERSION,
         &utils::internal_lints::CLIPPY_LINTS_INTERNAL,
@@ -930,11 +940,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold));
     let too_large_for_stack = conf.too_large_for_stack;
     store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack});
+    store.register_late_pass(move || box vec::UselessVec{too_large_for_stack});
     store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented);
     store.register_late_pass(|| box strings::StringLitAsBytes);
     store.register_late_pass(|| box derive::Derive);
     store.register_late_pass(|| box types::CharLitAsU8);
-    store.register_late_pass(|| box vec::UselessVec);
     store.register_late_pass(|| box drop_bounds::DropBounds);
     store.register_late_pass(|| box get_last_with_len::GetLastWithLen);
     store.register_late_pass(|| box drop_forget_ref::DropForgetRef);
@@ -1017,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| box reference::DerefAddrOf);
     store.register_early_pass(|| box reference::RefInDeref);
     store.register_early_pass(|| box double_parens::DoubleParens);
+    store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new());
     store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval);
     store.register_early_pass(|| box if_not_else::IfNotElse);
     store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
@@ -1025,8 +1036,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| box misc_early::MiscEarlyLints);
     store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall);
     store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall);
-    store.register_early_pass(|| box returns::Return);
-    store.register_late_pass(|| box let_and_return::LetReturn);
+    store.register_early_pass(|| box unused_unit::UnusedUnit);
+    store.register_late_pass(|| box returns::Return);
     store.register_early_pass(|| box collapsible_if::CollapsibleIf);
     store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
     store.register_early_pass(|| box precedence::Precedence);
@@ -1085,6 +1096,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
     store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
     store.register_late_pass(|| box repeat_once::RepeatOnce);
+    store.register_late_pass(|| box unwrap_in_result::UnwrapInResult);
+    store.register_late_pass(|| box self_assignment::SelfAssignment);
+    store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1122,6 +1136,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&shadow::SHADOW_REUSE),
         LintId::of(&shadow::SHADOW_SAME),
         LintId::of(&strings::STRING_ADD),
+        LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT),
         LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
         LintId::of(&write::PRINT_STDOUT),
         LintId::of(&write::USE_DEBUG),
@@ -1260,6 +1275,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
         LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
         LintId::of(&explicit_write::EXPLICIT_WRITE),
+        LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
         LintId::of(&float_literal::EXCESSIVE_PRECISION),
         LintId::of(&format::USELESS_FORMAT),
         LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
@@ -1284,7 +1300,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
         LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
         LintId::of(&len_zero::LEN_ZERO),
-        LintId::of(&let_and_return::LET_AND_RETURN),
         LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
         LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES),
         LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
@@ -1349,6 +1364,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&methods::SEARCH_IS_SOME),
         LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
         LintId::of(&methods::SINGLE_CHAR_PATTERN),
+        LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
         LintId::of(&methods::SKIP_WHILE_NEXT),
         LintId::of(&methods::STRING_EXTEND_CHARS),
         LintId::of(&methods::SUSPICIOUS_MAP),
@@ -1356,6 +1372,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&methods::UNINIT_ASSUMED_INIT),
         LintId::of(&methods::UNNECESSARY_FILTER_MAP),
         LintId::of(&methods::UNNECESSARY_FOLD),
+        LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS),
         LintId::of(&methods::USELESS_ASREF),
         LintId::of(&methods::WRONG_SELF_CONVENTION),
         LintId::of(&methods::ZST_OFFSET),
@@ -1413,8 +1430,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&regex::INVALID_REGEX),
         LintId::of(&regex::TRIVIAL_REGEX),
         LintId::of(&repeat_once::REPEAT_ONCE),
+        LintId::of(&returns::LET_AND_RETURN),
         LintId::of(&returns::NEEDLESS_RETURN),
-        LintId::of(&returns::UNUSED_UNIT),
+        LintId::of(&self_assignment::SELF_ASSIGNMENT),
         LintId::of(&serde_api::SERDE_API_MISUSE),
         LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
         LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
@@ -1427,6 +1445,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
         LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
         LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
+        LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY),
         LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
         LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
         LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR),
@@ -1460,6 +1479,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         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_unit::UNUSED_UNIT),
         LintId::of(&unwrap::PANICKING_UNWRAP),
         LintId::of(&unwrap::UNNECESSARY_UNWRAP),
         LintId::of(&useless_conversion::USELESS_CONVERSION),
@@ -1500,7 +1520,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&inherent_to_string::INHERENT_TO_STRING),
         LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
         LintId::of(&len_zero::LEN_ZERO),
-        LintId::of(&let_and_return::LET_AND_RETURN),
         LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
         LintId::of(&loops::EMPTY_LOOP),
         LintId::of(&loops::FOR_KV_MAP),
@@ -1532,8 +1551,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&methods::OPTION_MAP_OR_NONE),
         LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
         LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
+        LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
         LintId::of(&methods::STRING_EXTEND_CHARS),
         LintId::of(&methods::UNNECESSARY_FOLD),
+        LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS),
         LintId::of(&methods::WRONG_SELF_CONVENTION),
         LintId::of(&misc::TOPLEVEL_REF_ARG),
         LintId::of(&misc::ZERO_PTR),
@@ -1554,8 +1575,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
         LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
         LintId::of(&regex::TRIVIAL_REGEX),
+        LintId::of(&returns::LET_AND_RETURN),
         LintId::of(&returns::NEEDLESS_RETURN),
-        LintId::of(&returns::UNUSED_UNIT),
         LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
         LintId::of(&strings::STRING_LIT_AS_BYTES),
         LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
@@ -1564,6 +1585,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&types::FN_TO_NUMERIC_CAST),
         LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
         LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
+        LintId::of(&unused_unit::UNUSED_UNIT),
         LintId::of(&write::PRINTLN_EMPTY_STRING),
         LintId::of(&write::PRINT_LITERAL),
         LintId::of(&write::PRINT_WITH_NEWLINE),
@@ -1672,6 +1694,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
         LintId::of(&eq_op::EQ_OP),
         LintId::of(&erasing_op::ERASING_OP),
+        LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
         LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
         LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
         LintId::of(&if_let_mutex::IF_LET_MUTEX),
@@ -1704,10 +1727,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&ptr::MUT_FROM_REF),
         LintId::of(&ranges::REVERSED_EMPTY_RANGES),
         LintId::of(&regex::INVALID_REGEX),
+        LintId::of(&self_assignment::SELF_ASSIGNMENT),
         LintId::of(&serde_api::SERDE_API_MISUSE),
         LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
         LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
         LintId::of(&swap::ALMOST_SWAPPED),
+        LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY),
         LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
         LintId::of(&transmute::WRONG_TRANSMUTE),
         LintId::of(&transmuting_null::TRANSMUTING_NULL),
index 8ffcd417d1df1848e59a035a3480a28b907e38b6..c95e43a9430446b2470aac5c782c10418b23ac57 100644 (file)
@@ -1141,11 +1141,31 @@ fn detect_same_item_push<'tcx>(
     if same_item_push_visitor.should_lint {
         if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
             // Make sure that the push does not involve possibly mutating values
-            if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
-                if let PatKind::Wild = pat.kind {
-                    let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
-                    let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
-
+            if let PatKind::Wild = pat.kind {
+                let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
+                let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
+                if let ExprKind::Path(ref qpath) = pushed_item.kind {
+                    if_chain! {
+                        if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id);
+                        let node = cx.tcx.hir().get(hir_id);
+                        if let Node::Binding(pat) = node;
+                        if let PatKind::Binding(bind_ann, ..) = pat.kind;
+                        if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
+                        then {
+                            span_lint_and_help(
+                                cx,
+                                SAME_ITEM_PUSH,
+                                vec.span,
+                                "it looks like the same item is being pushed into this Vec",
+                                None,
+                                &format!(
+                                    "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
+                                    item_str, vec_str, item_str
+                                ),
+                            )
+                        }
+                    }
+                } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
                     span_lint_and_help(
                         cx,
                         SAME_ITEM_PUSH,
index 641e6a17043246937384a2ae7c946d44a91fc634..1cd5b2012922f72ac65772a64de3af1f3d38c2e8 100644 (file)
@@ -111,8 +111,8 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
         cx,
         MAP_CLONE,
         root.trim_start(receiver).unwrap(),
-        "You are needlessly cloning iterator elements",
-        "Remove the `map` call",
+        "you are needlessly cloning iterator elements",
+        "remove the `map` call",
         String::new(),
         Applicability::MachineApplicable,
     )
@@ -125,8 +125,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) {
             cx,
             MAP_CLONE,
             replace,
-            "You are using an explicit closure for copying elements",
-            "Consider calling the dedicated `copied` method",
+            "you are using an explicit closure for copying elements",
+            "consider calling the dedicated `copied` method",
             format!(
                 "{}.copied()",
                 snippet_with_applicability(cx, root, "..", &mut applicability)
@@ -138,8 +138,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) {
             cx,
             MAP_CLONE,
             replace,
-            "You are using an explicit closure for cloning elements",
-            "Consider calling the dedicated `cloned` method",
+            "you are using an explicit closure for cloning elements",
+            "consider calling the dedicated `cloned` method",
             format!(
                 "{}.cloned()",
                 snippet_with_applicability(cx, root, "..", &mut applicability)
index faa20687ef61f3c938e7853f9783ced3c97552af..57966452253d52e75e55367ff540000448324377 100644 (file)
@@ -1,5 +1,5 @@
-use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
 use crate::utils::walk_ptrs_ty;
+use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};
index 2265a1888556af0ed9150af0ecb3e97c9a7f2f1b..9996df69470f0e9242548fb8f41a1e93f93067a4 100644 (file)
@@ -3,6 +3,7 @@
 mod manual_saturating_arithmetic;
 mod option_map_unwrap_or;
 mod unnecessary_filter_map;
+mod unnecessary_lazy_eval;
 
 use std::borrow::Cow;
 use std::fmt;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
 use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, Ty, TyS};
+use rustc_middle::ty::{self, TraitRef, Ty, TyS};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{sym, SymbolStr};
 use crate::consts::{constant, Constant};
 use crate::utils::usage::mutated_variables;
 use crate::utils::{
-    get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy,
-    is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
-    match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
-    remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite,
-    span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty,
-    walk_ptrs_ty_depth, SpanlessEq,
+    contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
+    is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats,
+    last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls,
+    method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
+    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
+    span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
 };
 
 declare_clippy_lint! {
     /// **Known problems:** None.
     ///
     /// **Example:**
+    /// In an impl block:
     /// ```rust
     /// # struct Foo;
     /// # struct NotAFoo;
     ///
     /// ```rust
     /// # struct Foo;
-    /// # struct FooError;
+    /// struct Bar(Foo);
     /// impl Foo {
-    ///     // Good. Return type contains `Self`
-    ///     fn new() -> Result<Foo, FooError> {
-    ///         # Ok(Foo)
+    ///     // Bad. The type name must contain `Self`
+    ///     fn new() -> Bar {
+    /// # Bar(Foo)
     ///     }
     /// }
     /// ```
     ///
     /// ```rust
     /// # struct Foo;
-    /// struct Bar(Foo);
+    /// # struct FooError;
     /// impl Foo {
-    ///     // Bad. The type name must contain `Self`.
-    ///     fn new() -> Bar {
-    ///         # Bar(Foo)
+    ///     // Good. Return type contains `Self`
+    ///     fn new() -> Result<Foo, FooError> {
+    /// # Ok(Foo)
     ///     }
     /// }
     /// ```
+    ///
+    /// Or in a trait definition:
+    /// ```rust
+    /// pub trait Trait {
+    ///     // Bad. The type name must contain `Self`
+    ///     fn new();
+    /// }
+    /// ```
+    ///
+    /// ```rust
+    /// pub trait Trait {
+    ///     // Good. Return type contains `Self`
+    ///     fn new() -> Self;
+    /// }
+    /// ```
     pub NEW_RET_NO_SELF,
     style,
     "not returning type containing `Self` in a `new` method"
     ///     call_some_ffi_func(c_str);
     /// }
     /// ```
-    /// Here `c_str` point to a freed address. The correct use would be:
+    /// Here `c_str` points to a freed address. The correct use would be:
     /// ```rust
     /// # use std::ffi::CString;
     /// # fn call_some_ffi_func(_: *const i8) {}
     "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Warns when using push_str with a single-character string literal,
+    /// and push with a char would work fine.
+    ///
+    /// **Why is this bad?** It's less clear that we are pushing a single character
+    ///
+    /// **Known problems:** None
+    ///
+    /// **Example:**
+    /// ```
+    /// let mut string = String::new();
+    /// string.push_str("R");
+    /// ```
+    /// Could be written as
+    /// ```
+    /// let mut string = String::new();
+    /// string.push('R');
+    /// ```
+    pub SINGLE_CHAR_PUSH_STR,
+    style,
+    "`push_str()` used with a single-character string literal as parameter"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary
+    /// lazily evaluated closures on `Option` and `Result`.
+    ///
+    /// This lint suggests changing the following functions, when eager evaluation results in
+    /// simpler code:
+    ///  - `unwrap_or_else` to `unwrap_or`
+    ///  - `and_then` to `and`
+    ///  - `or_else` to `or`
+    ///  - `get_or_insert_with` to `get_or_insert`
+    ///  - `ok_or_else` to `ok_or`
+    ///
+    /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.
+    ///
+    /// **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have
+    /// side effects. Eagerly evaluating them can change the semantics of the program.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// // example code where clippy issues a warning
+    /// let opt: Option<u32> = None;
+    ///
+    /// opt.unwrap_or_else(|| 42);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let opt: Option<u32> = None;
+    ///
+    /// opt.unwrap_or(42);
+    /// ```
+    pub UNNECESSARY_LAZY_EVALUATIONS,
+    style,
+    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
+}
+
 declare_lint_pass!(Methods => [
     UNWRAP_USED,
     EXPECT_USED,
     INEFFICIENT_TO_STRING,
     NEW_RET_NO_SELF,
     SINGLE_CHAR_PATTERN,
+    SINGLE_CHAR_PUSH_STR,
     SEARCH_IS_SOME,
     TEMPORARY_CSTRING_AS_PTR,
     FILTER_NEXT,
     ZST_OFFSET,
     FILETYPE_IS_FILE,
     OPTION_AS_REF_DEREF,
+    UNNECESSARY_LAZY_EVALUATIONS,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Methods {
@@ -1374,13 +1452,19 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
             ["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
             ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
-            ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
+            ["unwrap_or_else", "map"] => {
+                if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
+                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or");
+                }
+            },
             ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
             ["and_then", ..] => {
+                unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and");
                 bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
                 bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
             },
             ["or_else", ..] => {
+                unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or");
                 bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]);
             },
             ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
@@ -1424,6 +1508,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
             ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
             ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
+            ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"),
+            ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"),
+            ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"),
             _ => {},
         }
 
@@ -1441,6 +1528,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                     inefficient_to_string::lint(cx, expr, &args[0], self_ty);
                 }
 
+                if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+                    if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
+                        lint_single_char_push_string(cx, expr, args);
+                    }
+                }
+
                 match self_ty.kind {
                     ty::Ref(_, ty, _) if ty.kind == ty::Str => {
                         for &(method, pos) in &PATTERN_METHODS {
@@ -1470,6 +1563,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         }
     }
 
+    #[allow(clippy::too_many_lines)]
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
         if in_external_macro(cx.sess(), impl_item.span) {
             return;
@@ -1495,16 +1589,31 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
 
             then {
                 if cx.access_levels.is_exported(impl_item.hir_id) {
-                // check missing trait implementations
-                    for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS {
-                        if name == method_name &&
-                            sig.decl.inputs.len() == n_args &&
-                            out_type.matches(cx, &sig.decl.output) &&
-                            self_kind.matches(cx, self_ty, first_arg_ty) &&
-                            fn_header_equals(*fn_header, sig.header) {
-                            span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!(
-                                "defining a method called `{}` on this type; consider implementing \
-                                the `{}` trait or choosing a less ambiguous name", name, trait_name));
+                    // check missing trait implementations
+                    for method_config in &TRAIT_METHODS {
+                        if name == method_config.method_name &&
+                            sig.decl.inputs.len() == method_config.param_count &&
+                            method_config.output_type.matches(cx, &sig.decl.output) &&
+                            method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
+                            fn_header_equals(method_config.fn_header, sig.header) &&
+                            method_config.lifetime_param_cond(&impl_item)
+                        {
+                            span_lint_and_help(
+                                cx,
+                                SHOULD_IMPLEMENT_TRAIT,
+                                impl_item.span,
+                                &format!(
+                                    "method `{}` can be confused for the standard trait method `{}::{}`",
+                                    method_config.method_name,
+                                    method_config.trait_name,
+                                    method_config.method_name
+                                ),
+                                None,
+                                &format!(
+                                    "consider implementing the trait `{}` or choosing a less ambiguous method name",
+                                    method_config.trait_name
+                                )
+                            );
                         }
                     }
                 }
@@ -1538,19 +1647,16 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
             }
         }
 
+        // if this impl block implements a trait, lint in trait definition instead
+        if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
+            return;
+        }
+
         if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
             let ret_ty = return_ty(cx, impl_item.hir_id);
 
-            let contains_self_ty = |ty: Ty<'tcx>| {
-                ty.walk().any(|inner| match inner.unpack() {
-                    GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty),
-
-                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-                })
-            };
-
             // walk the return type and check for Self (this does not check associated types)
-            if contains_self_ty(ret_ty) {
+            if contains_ty(ret_ty, self_ty) {
                 return;
             }
 
@@ -1560,7 +1666,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
                 for &(predicate, _span) in cx.tcx.predicates_of(def_id).predicates {
                     if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
                         // walk the associated type and check for Self
-                        if contains_self_ty(projection_predicate.ty) {
+                        if contains_ty(projection_predicate.ty, self_ty) {
                             return;
                         }
                     }
@@ -1577,6 +1683,26 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
             }
         }
     }
+
+    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+        if_chain! {
+            if !in_external_macro(cx.tcx.sess, item.span);
+            if item.ident.name == sym!(new);
+            if let TraitItemKind::Fn(_, _) = item.kind;
+            let ret_ty = return_ty(cx, item.hir_id);
+            let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty();
+            if !contains_ty(ret_ty, self_ty);
+
+            then {
+                span_lint(
+                    cx,
+                    NEW_RET_NO_SELF,
+                    item.span,
+                    "methods called `new` usually return `Self`",
+                );
+            }
+        }
+    }
 }
 
 /// Checks for the `OR_FUN_CALL` lint.
@@ -2057,18 +2183,15 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::
             return;
         };
 
+        let snippet = snippet_with_macro_callsite(cx, arg.span, "_");
+
         span_lint_and_sugg(
             cx,
             CLONE_ON_REF_PTR,
             expr.span,
             "using `.clone()` on a ref-counted pointer",
             "try this",
-            format!(
-                "{}::<{}>::clone(&{})",
-                caller_type,
-                subst.type_at(0),
-                snippet(cx, arg.span, "_")
-            ),
+            format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet),
             Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak
         );
     }
@@ -2280,7 +2403,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
                     cx,
                     ITER_NEXT_SLICE,
                     expr.span,
-                    "Using `.iter().next()` on a Slice without end index.",
+                    "using `.iter().next()` on a Slice without end index",
                     "try calling",
                     format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx),
                     applicability,
@@ -2299,7 +2422,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
             cx,
             ITER_NEXT_SLICE,
             expr.span,
-            "Using `.iter().next()` on an array",
+            "using `.iter().next()` on an array",
             "try calling",
             format!(
                 "{}.get(0)",
@@ -2618,12 +2741,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
 }
 
 /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
+/// Return true if lint triggered
 fn lint_map_unwrap_or_else<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'_>,
     map_args: &'tcx [hir::Expr<'_>],
     unwrap_args: &'tcx [hir::Expr<'_>],
-) {
+) -> bool {
     // lint if the caller of `map()` is an `Option`
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type));
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type));
@@ -2635,10 +2759,10 @@ fn lint_map_unwrap_or_else<'tcx>(
         let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx);
         if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
             if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
-                return;
+                return false;
             }
         } else {
-            return;
+            return false;
         }
 
         // lint message
@@ -2668,10 +2792,14 @@ fn lint_map_unwrap_or_else<'tcx>(
                     map_snippet, unwrap_snippet,
                 ),
             );
+            return true;
         } else if same_span && multiline {
             span_lint(cx, MAP_UNWRAP_OR, expr.span, msg);
-        };
+            return true;
+        }
     }
+
+    false
 }
 
 /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
@@ -3124,15 +3252,18 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx
     }
 }
 
-/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
-fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
+fn get_hint_if_single_char_arg(
+    cx: &LateContext<'_>,
+    arg: &hir::Expr<'_>,
+    applicability: &mut Applicability,
+) -> Option<String> {
     if_chain! {
         if let hir::ExprKind::Lit(lit) = &arg.kind;
         if let ast::LitKind::Str(r, style) = lit.node;
-        if r.as_str().len() == 1;
+        let string = r.as_str();
+        if string.len() == 1;
         then {
-            let mut applicability = Applicability::MachineApplicable;
-            let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
+            let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
             let ch = if let ast::StrStyle::Raw(nhash) = style {
                 let nhash = nhash as usize;
                 // for raw string: r##"a"##
@@ -3142,19 +3273,47 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr
                 &snip[1..(snip.len() - 1)]
             };
             let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
-            span_lint_and_sugg(
-                cx,
-                SINGLE_CHAR_PATTERN,
-                arg.span,
-                "single-character string constant used as pattern",
-                "try using a `char` instead",
-                hint,
-                applicability,
-            );
+            Some(hint)
+        } else {
+            None
         }
     }
 }
 
+/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
+fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
+    let mut applicability = Applicability::MachineApplicable;
+    if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) {
+        span_lint_and_sugg(
+            cx,
+            SINGLE_CHAR_PATTERN,
+            arg.span,
+            "single-character string constant used as pattern",
+            "try using a `char` instead",
+            hint,
+            applicability,
+        );
+    }
+}
+
+/// lint for length-1 `str`s as argument for `push_str`
+fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+    let mut applicability = Applicability::MachineApplicable;
+    if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
+        let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+        let sugg = format!("{}.push({})", base_string_snippet, extension_string);
+        span_lint_and_sugg(
+            cx,
+            SINGLE_CHAR_PUSH_STR,
+            expr.span,
+            "calling `push_str()` using a single-character string literal",
+            "consider using `push` with a character literal",
+            sugg,
+            applicability,
+        );
+    }
+}
+
 /// Checks for the `USELESS_ASREF` lint.
 fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) {
     // when we get here, we've already checked that the call name is "as_ref" or "as_mut"
@@ -3292,7 +3451,12 @@ fn lint_option_as_ref_deref<'tcx>(
     ];
 
     let is_deref = match map_args[1].kind {
-        hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)),
+        hir::ExprKind::Path(ref expr_qpath) => cx
+            .qpath_res(expr_qpath, map_args[1].hir_id)
+            .opt_def_id()
+            .map_or(false, |fun_def_id| {
+                deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
+            }),
         hir::ExprKind::Closure(_, _, body_id, _, _) => {
             let closure_body = cx.tcx.hir().body(body_id);
             let closure_expr = remove_blocks(&closure_body.value);
@@ -3403,38 +3567,85 @@ enum Convention {
     abi: rustc_target::spec::abi::Abi::Rust,
 };
 
+struct ShouldImplTraitCase {
+    trait_name: &'static str,
+    method_name: &'static str,
+    param_count: usize,
+    fn_header: hir::FnHeader,
+    // implicit self kind expected (none, self, &self, ...)
+    self_kind: SelfKind,
+    // checks against the output type
+    output_type: OutType,
+    // certain methods with explicit lifetimes can't implement the equivalent trait method
+    lint_explicit_lifetime: bool,
+}
+impl ShouldImplTraitCase {
+    const fn new(
+        trait_name: &'static str,
+        method_name: &'static str,
+        param_count: usize,
+        fn_header: hir::FnHeader,
+        self_kind: SelfKind,
+        output_type: OutType,
+        lint_explicit_lifetime: bool,
+    ) -> ShouldImplTraitCase {
+        ShouldImplTraitCase {
+            trait_name,
+            method_name,
+            param_count,
+            fn_header,
+            self_kind,
+            output_type,
+            lint_explicit_lifetime,
+        }
+    }
+
+    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
+        self.lint_explicit_lifetime
+            || !impl_item.generics.params.iter().any(|p| {
+                matches!(
+                    p.kind,
+                    hir::GenericParamKind::Lifetime {
+                        kind: hir::LifetimeParamKind::Explicit
+                    }
+                )
+            })
+    }
+}
+
 #[rustfmt::skip]
-const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [
-    ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"),
-    ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"),
-    ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"),
-    ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"),
-    ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"),
-    ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"),
-    ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"),
-    ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"),
-    ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"),
-    ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"),
-    ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"),
-    ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"),
-    ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"),
-    ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"),
-    ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"),
-    ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"),
-    ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"),
-    ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"),
-    ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"),
-    ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"),
-    ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"),
-    ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"),
-    ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"),
-    ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"),
-    ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"),
-    ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"),
-    ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"),
-    ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"),
-    ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"),
-    ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"),
+const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
+    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
+    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
+    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
+    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
+    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
+    // FIXME: default doesn't work
+    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
+    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
+    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
+    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
+    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
+    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
+    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
+    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
+    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
+    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 ];
 
 #[rustfmt::skip]
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
new file mode 100644 (file)
index 0000000..3151765
--- /dev/null
@@ -0,0 +1,111 @@
+use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+use super::UNNECESSARY_LAZY_EVALUATIONS;
+
+// Return true if the expression is an accessor of any of the arguments
+fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool {
+    params.iter().any(|arg| {
+        if_chain! {
+            if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind;
+            if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind;
+            if let [p, ..] = path.segments;
+            then {
+                ident.name == p.ident.name
+            } else {
+                false
+            }
+        }
+    })
+}
+
+fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool {
+    paths.iter().any(|candidate| match_qpath(path, candidate))
+}
+
+fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool {
+    match expr.kind {
+        // Closures returning literals can be unconditionally simplified
+        hir::ExprKind::Lit(_) => true,
+
+        hir::ExprKind::Index(ref object, ref index) => {
+            // arguments are not being indexed into
+            if expr_uses_argument(object, params) {
+                false
+            } else {
+                // arguments are not used as index
+                !expr_uses_argument(index, params)
+            }
+        },
+
+        // Reading fields can be simplified if the object is not an argument of the closure
+        hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params),
+
+        // Paths can be simplified if the root is not the argument, this also covers None
+        hir::ExprKind::Path(_) => !expr_uses_argument(expr, params),
+
+        // Calls to Some, Ok, Err can be considered literals if they don't derive an argument
+        hir::ExprKind::Call(ref func, ref args) => if_chain! {
+            if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
+            if let hir::ExprKind::Path(ref path) = func.kind;
+            if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]);
+            then {
+                // Recursively check all arguments
+                args.iter().all(|arg| can_simplify(arg, params, variant_calls))
+            } else {
+                false
+            }
+        },
+
+        // For anything more complex than the above, a closure is probably the right solution,
+        // or the case is handled by an other lint
+        _ => false,
+    }
+}
+
+/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
+/// replaced with `<fn>(return value of simple closure)`
+pub(super) fn lint<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    args: &'tcx [hir::Expr<'_>],
+    allow_variant_calls: bool,
+    simplify_using: &str,
+) {
+    let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
+    let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));
+
+    if is_option || is_result {
+        if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
+            let body = cx.tcx.hir().body(eid);
+            let ex = &body.value;
+            let params = &body.params;
+
+            if can_simplify(ex, params, allow_variant_calls) {
+                let msg = if is_option {
+                    "unnecessary closure used to substitute value for `Option::None`"
+                } else {
+                    "unnecessary closure used to substitute value for `Result::Err`"
+                };
+
+                span_lint_and_sugg(
+                    cx,
+                    UNNECESSARY_LAZY_EVALUATIONS,
+                    expr.span,
+                    msg,
+                    &format!("Use `{}` instead", simplify_using),
+                    format!(
+                        "{0}.{1}({2})",
+                        snippet(cx, args[0].span, ".."),
+                        simplify_using,
+                        snippet(cx, ex.span, ".."),
+                    ),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
index 482a563572db270fa63a436879cb4348a3a3f79f..06f367a8b775f99ee7ae9fc610cc315c1170c4e5 100644 (file)
@@ -433,8 +433,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             return;
         }
         let binding = match expr.kind {
-            ExprKind::Path(hir::QPath::LangItem(..)) => None,
-            ExprKind::Path(ref qpath) => {
+            ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => {
                 let binding = last_path_segment(qpath).ident.as_str();
                 if binding.starts_with('_') &&
                     !binding.starts_with("__") &&
index b8dc5081632972827f9fc9203246dabc8649d096..c506440ed7987e48c7733262abbc12ac78805826 100644 (file)
@@ -9,8 +9,8 @@
     /// **What it does:** Detects passing a mutable reference to a function that only
     /// requires an immutable reference.
     ///
-    /// **Why is this bad?** The immutable reference rules out all other references
-    /// to the value. Also the code misleads about the intent of the call site.
+    /// **Why is this bad?** The mutable reference rules out all other references to
+    /// the value. Also the code misleads about the intent of the call site.
     ///
     /// **Known problems:** None.
     ///
@@ -39,6 +39,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                         arguments,
                         cx.typeck_results().expr_ty(fn_expr),
                         &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
+                        "function",
                     );
                 }
             },
@@ -46,14 +47,20 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                 let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
                 let substs = cx.typeck_results().node_substs(e.hir_id);
                 let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
-                check_arguments(cx, arguments, method_type, &path.ident.as_str())
+                check_arguments(cx, arguments, method_type, &path.ident.as_str(), "method")
             },
             _ => (),
         }
     }
 }
 
-fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_definition: Ty<'tcx>, name: &str) {
+fn check_arguments<'tcx>(
+    cx: &LateContext<'tcx>,
+    arguments: &[Expr<'_>],
+    type_definition: Ty<'tcx>,
+    name: &str,
+    fn_kind: &str,
+) {
     match type_definition.kind {
         ty::FnDef(..) | ty::FnPtr(_) => {
             let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
@@ -68,7 +75,7 @@ fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_de
                                 cx,
                                 UNNECESSARY_MUT_PASSED,
                                 argument.span,
-                                &format!("The function/method `{}` doesn't need a mutable reference", name),
+                                &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name),
                             );
                         }
                     },
index 568898aa5c9b7773f4e0fb53679def984114065b..21efee71269862484f60ca618249bb067dd363e5 100644 (file)
@@ -72,8 +72,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 let mutex_param = subst.type_at(0);
                 if let Some(atomic_name) = get_atomic_name(mutex_param) {
                     let msg = format!(
-                        "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \
-                         behavior and not the internal type, consider using `Mutex<()>`.",
+                        "consider using an `{}` instead of a `Mutex` here; if you just want the locking \
+                         behavior and not the internal type, consider using `Mutex<()>`",
                         atomic_name
                     );
                     match mutex_param.kind {
index 031d69e86a13edb2c7c5406127dcf67f6d6bd65d..f1df634701dd25d5c7a8e5dfcaa31abaad210426 100644 (file)
@@ -211,8 +211,21 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                             needs_check_adjustment = false;
                         },
                         ExprKind::Field(..) => {
-                            dereferenced_expr = parent_expr;
                             needs_check_adjustment = true;
+
+                            // Check whether implicit dereferences happened;
+                            // if so, no need to go further up
+                            // because of the same reason as the `ExprKind::Unary` case.
+                            if cx
+                                .typeck_results()
+                                .expr_adjustments(dereferenced_expr)
+                                .iter()
+                                .any(|adj| matches!(adj.kind, Adjust::Deref(_)))
+                            {
+                                break;
+                            }
+
+                            dereferenced_expr = parent_expr;
                         },
                         ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => {
                             // `e[i]` => desugared to `*Index::index(&e, i)`,
index 4797771e7bdbb4aa9e95dba4f3a00c9f587cda42..c9d18c3cb7287551e439bb15d3b57d05d29806e4 100644 (file)
@@ -1,4 +1,5 @@
 use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use if_chain::if_chain;
 use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -102,36 +103,36 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
             }
         }
 
-        if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind {
-            if let ExprKind::MethodCall(ref path_segment, ref args, _) = rhs.kind {
+        if let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind {
+            let mut arg = operand;
+
+            let mut all_odd = true;
+            while let ExprKind::MethodCall(path_segment, args, _) = &arg.kind {
                 let path_segment_str = path_segment.ident.name.as_str();
-                if let Some(slf) = args.first() {
-                    if let ExprKind::Lit(ref lit) = slf.kind {
-                        match lit.kind {
-                            LitKind::Int(..) | LitKind::Float(..) => {
-                                if ALLOWED_ODD_FUNCTIONS
-                                    .iter()
-                                    .any(|odd_function| **odd_function == *path_segment_str)
-                                {
-                                    return;
-                                }
-                                let mut applicability = Applicability::MachineApplicable;
-                                span_lint_and_sugg(
-                                    cx,
-                                    PRECEDENCE,
-                                    expr.span,
-                                    "unary minus has lower precedence than method call",
-                                    "consider adding parentheses to clarify your intent",
-                                    format!(
-                                        "-({})",
-                                        snippet_with_applicability(cx, rhs.span, "..", &mut applicability)
-                                    ),
-                                    applicability,
-                                );
-                            },
-                            _ => (),
-                        }
-                    }
+                all_odd &= ALLOWED_ODD_FUNCTIONS
+                    .iter()
+                    .any(|odd_function| **odd_function == *path_segment_str);
+                arg = args.first().expect("A method always has a receiver.");
+            }
+
+            if_chain! {
+                if !all_odd;
+                if let ExprKind::Lit(lit) = &arg.kind;
+                if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind;
+                then {
+                    let mut applicability = Applicability::MachineApplicable;
+                    span_lint_and_sugg(
+                        cx,
+                        PRECEDENCE,
+                        expr.span,
+                        "unary minus has lower precedence than method call",
+                        "consider adding parentheses to clarify your intent",
+                        format!(
+                            "-({})",
+                            snippet_with_applicability(cx, operand.span, "..", &mut applicability)
+                        ),
+                        applicability,
+                    );
                 }
             }
         }
index 460d631fab0fdc62ac9207e3112c9bfb65383937..7dafb1555dc6e7e187f62e44c1977de7dd2fd81d 100644 (file)
     /// argument may also fail to compile if you change the argument. Applying
     /// this lint on them will fix the problem, but they may be in other crates.
     ///
+    /// One notable example of a function that may cause issues, and which cannot
+    /// easily be changed due to being in the standard library is `Vec::contains`.
+    /// when called on a `Vec<Vec<T>>`. If a `&Vec` is passed to that method then
+    /// it will compile, but if a `&[T]` is passed then it will not compile.
+    ///
+    /// ```ignore
+    /// fn cannot_take_a_slice(v: &Vec<u8>) -> bool {
+    ///     let vec_of_vecs: Vec<Vec<u8>> = some_other_fn();
+    ///
+    ///     vec_of_vecs.contains(v)
+    /// }
+    /// ```
+    ///
     /// Also there may be `fn(&Vec)`-typed references pointing to your function.
     /// If you have them, you will get a compiler error after applying this lint's
     /// suggestions. You then have the choice to undo your changes or change the
     /// type of the reference.
     ///
     /// Note that if the function is part of your public interface, there may be
-    /// other crates referencing it you may not be aware. Carefully deprecate the
-    /// function before applying the lint suggestions in this case.
+    /// other crates referencing it, of which you may not be aware. Carefully
+    /// deprecate the function before applying the lint suggestions in this case.
     ///
     /// **Example:**
     /// ```ignore
index fb12c565afd863048bbcbc895f1abb4fbf36b082..dbc676ae22408b139172a742361ca9ac7e511003 100644 (file)
@@ -7,8 +7,8 @@
 
 use crate::utils::sugg::Sugg;
 use crate::utils::{
-    higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
-    span_lint_and_sugg, SpanlessEq,
+    eq_expr_value, higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
+    span_lint_and_sugg,
 };
 
 declare_clippy_lint! {
@@ -65,7 +65,7 @@ fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
                         if let ExprKind::Block(block, None) = &else_.kind;
                         if block.stmts.is_empty();
                         if let Some(block_expr) = &block.expr;
-                        if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr);
+                        if eq_expr_value(cx, subject, block_expr);
                         then {
                             replacement = Some(format!("Some({}?)", receiver_str));
                         }
index 8aa478ea2d69f41aa77253cc4696827820fa2a7e..49cb2ffc4e372a3f765e89e1f9fe4bf0f7740a23 100644 (file)
@@ -77,7 +77,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
                         cx,
                         REDUNDANT_CLOSURE_CALL,
                         expr.span,
-                        "try not to call a closure in the expression where it is declared.",
+                        "try not to call a closure in the expression where it is declared",
                         |diag| {
                             if decl.inputs.is_empty() {
                                 let mut app = Applicability::MachineApplicable;
@@ -95,12 +95,17 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 
 impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
-        fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize {
-            struct ClosureUsageCount<'tcx> {
+        fn count_closure_usage<'a, 'tcx>(
+            cx: &'a LateContext<'tcx>,
+            block: &'tcx hir::Block<'_>,
+            path: &'tcx hir::Path<'tcx>,
+        ) -> usize {
+            struct ClosureUsageCount<'a, 'tcx> {
+                cx: &'a LateContext<'tcx>,
                 path: &'tcx hir::Path<'tcx>,
                 count: usize,
             };
-            impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> {
+            impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> {
                 type Map = Map<'tcx>;
 
                 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
@@ -117,10 +122,10 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
                 }
 
                 fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
-                    hir_visit::NestedVisitorMap::None
+                    hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
                 }
             };
-            let mut closure_usage_count = ClosureUsageCount { path, count: 0 };
+            let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 };
             closure_usage_count.visit_block(block);
             closure_usage_count.count
         }
@@ -136,7 +141,7 @@ fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
                 if let hir::ExprKind::Call(ref closure, _) = call.kind;
                 if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind;
                 if ident == path.segments[0].ident;
-                if count_closure_usage(block, path) == 1;
+                if count_closure_usage(cx, block, path) == 1;
                 then {
                     span_lint(
                         cx,
index 77c206002ea790684217a3cc6404b69d7a14b7b3..c0890018d46aba3abe8c9843d8a98cdffadf63dc 100644 (file)
 impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
         if_chain! {
-            if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
+            if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind;
             if path.ident.name == sym!(repeat);
-            if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&args[1]);
-            if !in_macro(args[0].span);
+            if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count);
+            if !in_macro(receiver.span);
             then {
-                let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
+                let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver));
                 if ty.is_str() {
                     span_lint_and_sugg(
                         cx,
@@ -52,7 +52,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
                         expr.span,
                         "calling `repeat(1)` on str",
                         "consider using `.to_string()` instead",
-                        format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)),
+                        format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
                         Applicability::MachineApplicable,
                     );
                 } else if ty.builtin_index().is_some() {
@@ -62,7 +62,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
                         expr.span,
                         "calling `repeat(1)` on slice",
                         "consider using `.to_vec()` instead",
-                        format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)),
+                        format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
                         Applicability::MachineApplicable,
                     );
                 } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
@@ -72,7 +72,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
                         expr.span,
                         "calling `repeat(1)` on a string literal",
                         "consider using `.clone()` instead",
-                        format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)),
+                        format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
                         Applicability::MachineApplicable,
                     );
                 }
index 8ed20995a70af1257708ba3b8b278dc08adbec8d..a6e4252a0c82579b2c332cf8a31c2279ed9fcfcc 100644 (file)
@@ -1,60 +1,67 @@
 use if_chain::if_chain;
-use rustc_ast::ast;
-use rustc_ast::visit::FnKind;
+use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
-use rustc_span::BytePos;
 
-use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then};
+use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then};
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for return statements at the end of a block.
+    /// **What it does:** Checks for `let`-bindings, which are subsequently
+    /// returned.
     ///
-    /// **Why is this bad?** Removing the `return` and semicolon will make the code
+    /// **Why is this bad?** It is just extraneous code. Remove it to make your code
     /// more rusty.
     ///
-    /// **Known problems:** If the computation returning the value borrows a local
-    /// variable, removing the `return` may run afoul of the borrow checker.
+    /// **Known problems:** None.
     ///
     /// **Example:**
     /// ```rust
-    /// fn foo(x: usize) -> usize {
-    ///     return x;
+    /// fn foo() -> String {
+    ///     let x = String::new();
+    ///     x
     /// }
     /// ```
-    /// simplify to
-    /// ```rust
-    /// fn foo(x: usize) -> usize {
-    ///     x
+    /// instead, use
+    /// ```
+    /// fn foo() -> String {
+    ///     String::new()
     /// }
     /// ```
-    pub NEEDLESS_RETURN,
+    pub LET_AND_RETURN,
     style,
-    "using a return statement like `return expr;` where an expression would suffice"
+    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
 }
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for unit (`()`) expressions that can be removed.
+    /// **What it does:** Checks for return statements at the end of a block.
     ///
-    /// **Why is this bad?** Such expressions add no value, but can make the code
-    /// less readable. Depending on formatting they can make a `break` or `return`
-    /// statement look like a function call.
+    /// **Why is this bad?** Removing the `return` and semicolon will make the code
+    /// more rusty.
     ///
-    /// **Known problems:** The lint currently misses unit return types in types,
-    /// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
+    /// **Known problems:** None.
     ///
     /// **Example:**
     /// ```rust
-    /// fn return_unit() -> () {
-    ///     ()
+    /// fn foo(x: usize) -> usize {
+    ///     return x;
     /// }
     /// ```
-    pub UNUSED_UNIT,
+    /// simplify to
+    /// ```rust
+    /// fn foo(x: usize) -> usize {
+    ///     x
+    /// }
+    /// ```
+    pub NEEDLESS_RETURN,
     style,
-    "needless unit expression"
+    "using a return statement like `return expr;` where an expression would suffice"
 }
 
 #[derive(PartialEq, Eq, Copy, Clone)]
@@ -63,221 +70,220 @@ enum RetReplacement {
     Block,
 }
 
-declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]);
+declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
 
-impl Return {
-    // Check the final stmt or expr in a block for unnecessary return.
-    fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
-        if let Some(stmt) = block.stmts.last() {
-            match stmt.kind {
-                ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
-                    self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
-                },
-                _ => (),
+impl<'tcx> LateLintPass<'tcx> for Return {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
+        // we need both a let-binding stmt and an expr
+        if_chain! {
+            if let Some(retexpr) = block.expr;
+            if let Some(stmt) = block.stmts.iter().last();
+            if let StmtKind::Local(local) = &stmt.kind;
+            if local.ty.is_none();
+            if local.attrs.is_empty();
+            if let Some(initexpr) = &local.init;
+            if let PatKind::Binding(.., ident, _) = local.pat.kind;
+            if let ExprKind::Path(qpath) = &retexpr.kind;
+            if match_qpath(qpath, &[&*ident.name.as_str()]);
+            if !last_statement_borrows(cx, initexpr);
+            if !in_external_macro(cx.sess(), initexpr.span);
+            if !in_external_macro(cx.sess(), retexpr.span);
+            if !in_external_macro(cx.sess(), local.span);
+            if !in_macro(local.span);
+            then {
+                span_lint_and_then(
+                    cx,
+                    LET_AND_RETURN,
+                    retexpr.span,
+                    "returning the result of a `let` binding from a block",
+                    |err| {
+                        err.span_label(local.span, "unnecessary `let` binding");
+
+                        if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
+                            if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() {
+                                snippet.push_str(" as _");
+                            }
+                            err.multipart_suggestion(
+                                "return the expression directly",
+                                vec![
+                                    (local.span, String::new()),
+                                    (retexpr.span, snippet),
+                                ],
+                                Applicability::MachineApplicable,
+                            );
+                        } else {
+                            err.span_help(initexpr.span, "this expression can be directly returned");
+                        }
+                    },
+                );
             }
         }
     }
 
-    // Check the final expression in a block if it's a return.
-    fn check_final_expr(
+    fn check_fn(
         &mut self,
-        cx: &EarlyContext<'_>,
-        expr: &ast::Expr,
-        span: Option<Span>,
-        replacement: RetReplacement,
+        cx: &LateContext<'tcx>,
+        kind: FnKind<'tcx>,
+        _: &'tcx FnDecl<'tcx>,
+        body: &'tcx Body<'tcx>,
+        _: Span,
+        _: HirId,
     ) {
-        match expr.kind {
-            // simple return is always "bad"
-            ast::ExprKind::Ret(ref inner) => {
-                // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
-                if !expr.attrs.iter().any(attr_is_cfg) {
-                    Self::emit_return_lint(
-                        cx,
-                        span.expect("`else return` is not possible"),
-                        inner.as_ref().map(|i| i.span),
-                        replacement,
-                    );
-                }
-            },
-            // a whole block? check it!
-            ast::ExprKind::Block(ref block, _) => {
-                self.check_block_return(cx, block);
-            },
-            // an if/if let expr, check both exprs
-            // note, if without else is going to be a type checking error anyways
-            // (except for unit type functions) so we don't match it
-            ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
-                self.check_block_return(cx, ifblock);
-                self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty);
-            },
-            // a match expr, check all arms
-            ast::ExprKind::Match(_, ref arms) => {
-                for arm in arms {
-                    self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
+        match kind {
+            FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty),
+            FnKind::ItemFn(..) | FnKind::Method(..) => {
+                if let ExprKind::Block(ref block, _) = body.value.kind {
+                    check_block_return(cx, block);
                 }
             },
-            _ => (),
         }
     }
+}
 
-    fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
-        match inner_span {
-            Some(inner_span) => {
-                if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() {
-                    return;
-                }
+fn attr_is_cfg(attr: &Attribute) -> bool {
+    attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
+}
 
-                span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
-                    if let Some(snippet) = snippet_opt(cx, inner_span) {
-                        diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
-                    }
-                })
+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);
+    } else if let Some(stmt) = block.stmts.iter().last() {
+        match stmt.kind {
+            StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
+                check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
             },
-            None => match replacement {
-                RetReplacement::Empty => {
-                    span_lint_and_sugg(
-                        cx,
-                        NEEDLESS_RETURN,
-                        ret_span,
-                        "unneeded `return` statement",
-                        "remove `return`",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    );
-                },
-                RetReplacement::Block => {
-                    span_lint_and_sugg(
+            _ => (),
+        }
+    }
+}
+
+fn check_final_expr<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    span: Option<Span>,
+    replacement: RetReplacement,
+) {
+    match expr.kind {
+        // simple return is always "bad"
+        ExprKind::Ret(ref inner) => {
+            // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
+            if !expr.attrs.iter().any(attr_is_cfg) {
+                let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
+                if !borrows {
+                    emit_return_lint(
                         cx,
-                        NEEDLESS_RETURN,
-                        ret_span,
-                        "unneeded `return` statement",
-                        "replace `return` with an empty block",
-                        "{}".to_string(),
-                        Applicability::MachineApplicable,
+                        span.expect("`else return` is not possible"),
+                        inner.as_ref().map(|i| i.span),
+                        replacement,
                     );
-                },
+                }
+            }
+        },
+        // a whole block? check it!
+        ExprKind::Block(ref block, _) => {
+            check_block_return(cx, block);
+        },
+        // a match expr, check all arms
+        // an if/if let expr, check both exprs
+        // note, if without else is going to be a type checking error anyways
+        // (except for unit type functions) so we don't match it
+        ExprKind::Match(_, ref arms, source) => match source {
+            MatchSource::Normal => {
+                for arm in arms.iter() {
+                    check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
+                }
             },
-        }
+            MatchSource::IfDesugar {
+                contains_else_clause: true,
+            }
+            | MatchSource::IfLetDesugar {
+                contains_else_clause: true,
+            } => {
+                if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind {
+                    check_block_return(cx, ifblock);
+                }
+                check_final_expr(cx, arms[1].body, None, RetReplacement::Empty);
+            },
+            _ => (),
+        },
+        _ => (),
     }
 }
 
-impl EarlyLintPass for Return {
-    fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
-        match kind {
-            FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block),
-            FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty),
-            FnKind::Fn(.., None) => {},
-        }
-        if_chain! {
-            if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
-            if let ast::TyKind::Tup(ref vals) = ty.kind;
-            if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
-            then {
-                lint_unneeded_unit_return(cx, ty, span);
+fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
+    match inner_span {
+        Some(inner_span) => {
+            if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() {
+                return;
             }
-        }
-    }
 
-    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
-        if_chain! {
-            if let Some(ref stmt) = block.stmts.last();
-            if let ast::StmtKind::Expr(ref expr) = stmt.kind;
-            if is_unit_expr(expr) && !stmt.span.from_expansion();
-            then {
-                let sp = expr.span;
+            span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
+                if let Some(snippet) = snippet_opt(cx, inner_span) {
+                    diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
+                }
+            })
+        },
+        None => match replacement {
+            RetReplacement::Empty => {
                 span_lint_and_sugg(
                     cx,
-                    UNUSED_UNIT,
-                    sp,
-                    "unneeded unit expression",
-                    "remove the final `()`",
+                    NEEDLESS_RETURN,
+                    ret_span,
+                    "unneeded `return` statement",
+                    "remove `return`",
                     String::new(),
                     Applicability::MachineApplicable,
                 );
-            }
-        }
-    }
-
-    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
-        match e.kind {
-            ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
-                if is_unit_expr(expr) && !expr.span.from_expansion() {
-                    span_lint_and_sugg(
-                        cx,
-                        UNUSED_UNIT,
-                        expr.span,
-                        "unneeded `()`",
-                        "remove the `()`",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    );
-                }
             },
-            _ => (),
-        }
-    }
-
-    fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) {
-        let segments = &poly.trait_ref.path.segments;
-
-        if_chain! {
-            if segments.len() == 1;
-            if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str());
-            if let Some(args) = &segments[0].args;
-            if let ast::GenericArgs::Parenthesized(generic_args) = &**args;
-            if let ast::FnRetTy::Ty(ty) = &generic_args.output;
-            if ty.kind.is_unit();
-            then {
-                lint_unneeded_unit_return(cx, ty, generic_args.span);
-            }
-        }
+            RetReplacement::Block => {
+                span_lint_and_sugg(
+                    cx,
+                    NEEDLESS_RETURN,
+                    ret_span,
+                    "unneeded `return` statement",
+                    "replace `return` with an empty block",
+                    "{}".to_string(),
+                    Applicability::MachineApplicable,
+                );
+            },
+        },
     }
 }
 
-fn attr_is_cfg(attr: &ast::Attribute) -> bool {
-    attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
+fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    let mut visitor = BorrowVisitor { cx, borrows: false };
+    walk_expr(&mut visitor, expr);
+    visitor.borrows
 }
 
-// get the def site
-#[must_use]
-fn get_def(span: Span) -> Option<Span> {
-    if span.from_expansion() {
-        Some(span.ctxt().outer_expn_data().def_site)
-    } else {
-        None
-    }
+struct BorrowVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    borrows: bool,
 }
 
-// is this expr a `()` unit?
-fn is_unit_expr(expr: &ast::Expr) -> bool {
-    if let ast::ExprKind::Tup(ref vals) = expr.kind {
-        vals.is_empty()
-    } else {
-        false
+impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+        if self.borrows {
+            return;
+        }
+
+        if let Some(def_id) = fn_def_id(self.cx, expr) {
+            self.borrows = self
+                .cx
+                .tcx
+                .fn_sig(def_id)
+                .output()
+                .skip_binder()
+                .walk()
+                .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
+        }
+
+        walk_expr(self, expr);
     }
-}
 
-fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
-    let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
-        fn_source
-            .rfind("->")
-            .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
-                (
-                    #[allow(clippy::cast_possible_truncation)]
-                    ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
-                    Applicability::MachineApplicable,
-                )
-            })
-    } else {
-        (ty.span, Applicability::MaybeIncorrect)
-    };
-    span_lint_and_sugg(
-        cx,
-        UNUSED_UNIT,
-        ret_span,
-        "unneeded unit return type",
-        "remove the `-> ()`",
-        String::new(),
-        appl,
-    );
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/self_assignment.rs b/src/tools/clippy/clippy_lints/src/self_assignment.rs
new file mode 100644 (file)
index 0000000..e096c9a
--- /dev/null
@@ -0,0 +1,51 @@
+use crate::utils::{eq_expr_value, snippet, span_lint};
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for explicit self-assignments.
+    ///
+    /// **Why is this bad?** Self-assignments are redundant and unlikely to be
+    /// intentional.
+    ///
+    /// **Known problems:** If expression contains any deref coercions or
+    /// indexing operations they are assumed not to have any side effects.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// struct Event {
+    ///     id: usize,
+    ///     x: i32,
+    ///     y: i32,
+    /// }
+    ///
+    /// fn copy_position(a: &mut Event, b: &Event) {
+    ///     a.x = b.x;
+    ///     a.y = a.y;
+    /// }
+    /// ```
+    pub SELF_ASSIGNMENT,
+    correctness,
+    "explicit self-assignment"
+}
+
+declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]);
+
+impl<'tcx> LateLintPass<'tcx> for SelfAssignment {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Assign(lhs, rhs, _) = &expr.kind {
+            if eq_expr_value(cx, lhs, rhs) {
+                let lhs = snippet(cx, lhs.span, "<lhs>");
+                let rhs = snippet(cx, rhs.span, "<rhs>");
+                span_lint(
+                    cx,
+                    SELF_ASSIGNMENT,
+                    expr.span,
+                    &format!("self-assignment of `{}` to `{}`", rhs, lhs),
+                );
+            }
+        }
+    }
+}
index cd7056620a2e02c19744a17b42e0b129bb9bbc8a..99e4b293ac6809b40e74b2a33b62db8fa41e72ca 100644 (file)
@@ -86,6 +86,7 @@ struct LintDetection {
     slice_name: String,
     method: SortingKind,
     method_args: String,
+    slice_type: String,
 }
 
 fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
@@ -93,10 +94,10 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option
         if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
         if let Some(slice) = &args.get(0);
         if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str());
-        if is_slice_of_primitives(cx, slice);
+        if let Some(slice_type) = is_slice_of_primitives(cx, slice);
         then {
             let args_str = args.iter().skip(1).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 })
+            Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
         } else {
             None
         }
@@ -111,9 +112,10 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                 STABLE_SORT_PRIMITIVE,
                 expr.span,
                 format!(
-                    "Use {} instead of {}",
+                    "used {} instead of {} to sort primitive type `{}`",
+                    detection.method.stable_name(),
                     detection.method.unstable_name(),
-                    detection.method.stable_name()
+                    detection.slice_type,
                 )
                 .as_str(),
                 "try",
index 4e335a0222f2067c3fd33e05f9bed56c5fa91bff..3a688a7bbef324cd12a61086ec78fa9d059230ae 100644 (file)
@@ -86,12 +86,20 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                 cx,
                 expr,
                 binop.node,
-                &["Add", "Sub", "Mul", "Div"],
+                &[
+                    "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr",
+                ],
                 &[
                     hir::BinOpKind::Add,
                     hir::BinOpKind::Sub,
                     hir::BinOpKind::Mul,
                     hir::BinOpKind::Div,
+                    hir::BinOpKind::Rem,
+                    hir::BinOpKind::BitAnd,
+                    hir::BinOpKind::BitOr,
+                    hir::BinOpKind::BitXor,
+                    hir::BinOpKind::Shl,
+                    hir::BinOpKind::Shr,
                 ],
             ) {
                 span_lint(
index 754f87e6b55e2585f9b79b4c7993e78b6049df21..cc39f060fc7f3929a69ed1860ca789a6dfbc6e54 100644 (file)
@@ -1,7 +1,7 @@
 use crate::utils::sugg::Sugg;
 use crate::utils::{
-    differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty,
-    SpanlessEq,
+    differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then,
+    walk_ptrs_ty,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -92,8 +92,8 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
             if rhs2.segments.len() == 1;
 
             if ident.as_str() == rhs2.segments[0].ident.as_str();
-            if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1);
-            if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2);
+            if eq_expr_value(cx, tmp_init, lhs1);
+            if eq_expr_value(cx, rhs1, lhs2);
             then {
                 if let ExprKind::Field(ref lhs1, _) = lhs1.kind {
                     if let ExprKind::Field(ref lhs2, _) = lhs2.kind {
@@ -193,7 +193,7 @@ enum Slice<'a> {
 fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> {
     if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind {
         if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind {
-            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
+            if eq_expr_value(cx, lhs1, lhs2) {
                 let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1));
 
                 if matches!(ty.kind, ty::Slice(_))
@@ -221,8 +221,8 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
             if !differing_macro_contexts(first.span, second.span);
             if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind;
             if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind;
-            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1);
-            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0);
+            if eq_expr_value(cx, lhs0, rhs1);
+            if eq_expr_value(cx, lhs1, rhs0);
             then {
                 let lhs0 = Sugg::hir_opt(cx, lhs0);
                 let rhs0 = Sugg::hir_opt(cx, rhs0);
diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
new file mode 100644 (file)
index 0000000..006d7a3
--- /dev/null
@@ -0,0 +1,122 @@
+use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint};
+use if_chain::if_chain;
+use rustc_hir::def::Res;
+use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for uses of `to_string()` in `Display` traits.
+    ///
+    /// **Why is this bad?** Usually `to_string` is implemented indirectly
+    /// via `Display`. Hence using it while implementing `Display` would
+    /// lead to infinite recursion.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// use std::fmt;
+    ///
+    /// struct Structure(i32);
+    /// impl fmt::Display for Structure {
+    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    ///         write!(f, "{}", self.to_string())
+    ///     }
+    /// }
+    ///
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::fmt;
+    ///
+    /// struct Structure(i32);
+    /// impl fmt::Display for Structure {
+    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    ///         write!(f, "{}", self.0)
+    ///     }
+    /// }
+    /// ```
+    pub TO_STRING_IN_DISPLAY,
+    correctness,
+    "`to_string` method used while implementing `Display` trait"
+}
+
+#[derive(Default)]
+pub struct ToStringInDisplay {
+    in_display_impl: bool,
+    self_hir_id: Option<HirId>,
+}
+
+impl ToStringInDisplay {
+    pub fn new() -> Self {
+        Self {
+            in_display_impl: false,
+            self_hir_id: None,
+        }
+    }
+}
+
+impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]);
+
+impl LateLintPass<'_> for ToStringInDisplay {
+    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        if is_display_impl(cx, item) {
+            self.in_display_impl = true;
+        }
+    }
+
+    fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        if is_display_impl(cx, item) {
+            self.in_display_impl = false;
+            self.self_hir_id = None;
+        }
+    }
+
+    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
+        if_chain! {
+            if self.in_display_impl;
+            if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
+            let body = cx.tcx.hir().body(*body_id);
+            if !body.params.is_empty();
+            then {
+                let self_param = &body.params[0];
+                self.self_hir_id = Some(self_param.pat.hir_id);
+            }
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if_chain! {
+            if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind;
+            if path.ident.name == sym!(to_string);
+            if match_trait_method(cx, expr, &paths::TO_STRING);
+            if self.in_display_impl;
+            if let ExprKind::Path(ref qpath) = args[0].kind;
+            if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id);
+            if let Some(self_hir_id) = self.self_hir_id;
+            if hir_id == self_hir_id;
+            then {
+                span_lint(
+                    cx,
+                    TO_STRING_IN_DISPLAY,
+                    expr.span,
+                    "using `to_string` in `fmt::Display` implementation might lead to infinite recursion",
+                );
+            }
+        }
+    }
+}
+
+fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
+    if_chain! {
+        if let ItemKind::Impl { of_trait: Some(trait_ref), .. } = &item.kind;
+        if let Some(did) = trait_ref.trait_def_id();
+        then {
+            match_def_path(cx, did, &paths::DISPLAY_TRAIT)
+        } else {
+            false
+        }
+    }
+}
index 28fd55f6ff0ad0ea2fbe4e84a913f9ee39b78ec1..50d9c93f9d405f16e956de1c6446fada39ea7b75 100644 (file)
@@ -1,5 +1,5 @@
 use crate::utils::{
-    is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg,
+    in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg,
     span_lint_and_then, sugg,
 };
 use if_chain::if_chain;
@@ -331,6 +331,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
             if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
             if match_def_path(cx, def_id, &paths::TRANSMUTE);
             then {
+                // Avoid suggesting from/to bits in const contexts.
+                // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
+                let const_context = in_constant(cx, e.hir_id);
+
                 let from_ty = cx.typeck_results().expr_ty(&args[0]);
                 let to_ty = cx.typeck_results().expr_ty(e);
 
@@ -544,7 +548,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                             },
                         )
                     },
-                    (ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then(
+                    (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => span_lint_and_then(
                         cx,
                         TRANSMUTE_INT_TO_FLOAT,
                         e.span,
@@ -567,7 +571,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                             );
                         },
                     ),
-                    (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then(
+                    (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => span_lint_and_then(
                         cx,
                         TRANSMUTE_FLOAT_TO_INT,
                         e.span,
index 7948d99162b8116b91b6236a8f0a1d1945406b34..92f42168a1eabc56dd5df476d1f5b138211dff57 100644 (file)
@@ -2,6 +2,7 @@
 
 use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
 use if_chain::if_chain;
+use rustc_ast::attr;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
@@ -155,8 +156,12 @@ fn check_fn(
                     return;
                 }
                 for a in attrs {
-                    if a.meta_item_list().is_some() && a.has_name(sym!(proc_macro_derive)) {
-                        return;
+                    if let Some(meta_items) = a.meta_item_list() {
+                        if a.has_name(sym!(proc_macro_derive))
+                            || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always)))
+                        {
+                            return;
+                        }
                     }
                 }
             },
index a74104e92820a3ca7288b008edebbd46848ab3ad..a4676e505b6f38e946d0e83b5fdf9a6dee9a7030 100644 (file)
@@ -1,10 +1,10 @@
 use crate::utils::{
-    is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet,
-    snippet_with_macro_callsite, span_lint_and_sugg,
+    is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite,
+    span_lint_and_sugg,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, QPath, LangItem, MatchSource};
+use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
index 0fd70550fa0c292dfbe001a2ad3df146565d1a09..7e9190bef5e78425ac5478cb78e6e2ec24879f12 100644 (file)
@@ -353,14 +353,25 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, is_local: boo
                             );
                             return; // don't recurse into the type
                         }
-                        if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) {
+                        if match_type_parameter(cx, qpath, &paths::BOX).is_some() {
+                            let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => match &ty.kind {
+                                    TyKind::Path(qpath) => qpath,
+                                    _ => return,
+                                },
+                                _ => return,
+                            };
+                            let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => ty.span,
+                                _ => return,
+                            };
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Rc<Box<T>>`",
                                 "try",
-                                snippet(cx, span, "..").to_string(),
+                                format!("Rc<{}>", snippet(cx, inner_span, "..")),
                                 Applicability::MachineApplicable,
                             );
                             return; // don't recurse into the type
index 9fe771cef45bfbb80f691517f847512dda07bb08..7f4f16f8faf96225c201528e2d1f9a3804490da8 100644 (file)
@@ -2,9 +2,9 @@
 
 use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
 use crate::utils::{over, span_lint_and_then};
-use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
+use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
 use rustc_ast_pretty::pprust;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -340,7 +340,7 @@ fn take_pat(from: &mut Pat) -> Pat {
         id: DUMMY_NODE_ID,
         kind: Wild,
         span: DUMMY_SP,
-        tokens: None
+        tokens: None,
     };
     mem::replace(from, dummy)
 }
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
new file mode 100644 (file)
index 0000000..7548c6a
--- /dev/null
@@ -0,0 +1,144 @@
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_ast::visit::FnKind;
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use rustc_span::BytePos;
+
+use crate::utils::span_lint_and_sugg;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for unit (`()`) expressions that can be removed.
+    ///
+    /// **Why is this bad?** Such expressions add no value, but can make the code
+    /// less readable. Depending on formatting they can make a `break` or `return`
+    /// statement look like a function call.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// fn return_unit() -> () {
+    ///     ()
+    /// }
+    /// ```
+    pub UNUSED_UNIT,
+    style,
+    "needless unit expression"
+}
+
+declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]);
+
+impl EarlyLintPass for UnusedUnit {
+    fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
+        if_chain! {
+            if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
+            if let ast::TyKind::Tup(ref vals) = ty.kind;
+            if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
+            then {
+                lint_unneeded_unit_return(cx, ty, span);
+            }
+        }
+    }
+
+    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
+        if_chain! {
+            if let Some(ref stmt) = block.stmts.last();
+            if let ast::StmtKind::Expr(ref expr) = stmt.kind;
+            if is_unit_expr(expr) && !stmt.span.from_expansion();
+            then {
+                let sp = expr.span;
+                span_lint_and_sugg(
+                    cx,
+                    UNUSED_UNIT,
+                    sp,
+                    "unneeded unit expression",
+                    "remove the final `()`",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
+        match e.kind {
+            ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
+                if is_unit_expr(expr) && !expr.span.from_expansion() {
+                    span_lint_and_sugg(
+                        cx,
+                        UNUSED_UNIT,
+                        expr.span,
+                        "unneeded `()`",
+                        "remove the `()`",
+                        String::new(),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            },
+            _ => (),
+        }
+    }
+
+    fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) {
+        let segments = &poly.trait_ref.path.segments;
+
+        if_chain! {
+            if segments.len() == 1;
+            if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str());
+            if let Some(args) = &segments[0].args;
+            if let ast::GenericArgs::Parenthesized(generic_args) = &**args;
+            if let ast::FnRetTy::Ty(ty) = &generic_args.output;
+            if ty.kind.is_unit();
+            then {
+                lint_unneeded_unit_return(cx, ty, generic_args.span);
+            }
+        }
+    }
+}
+
+// get the def site
+#[must_use]
+fn get_def(span: Span) -> Option<Span> {
+    if span.from_expansion() {
+        Some(span.ctxt().outer_expn_data().def_site)
+    } else {
+        None
+    }
+}
+
+// is this expr a `()` unit?
+fn is_unit_expr(expr: &ast::Expr) -> bool {
+    if let ast::ExprKind::Tup(ref vals) = expr.kind {
+        vals.is_empty()
+    } else {
+        false
+    }
+}
+
+fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
+    let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
+        fn_source
+            .rfind("->")
+            .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
+                (
+                    #[allow(clippy::cast_possible_truncation)]
+                    ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
+                    Applicability::MachineApplicable,
+                )
+            })
+    } else {
+        (ty.span, Applicability::MaybeIncorrect)
+    };
+    span_lint_and_sugg(
+        cx,
+        UNUSED_UNIT,
+        ret_span,
+        "unneeded unit return type",
+        "remove the `-> ()`",
+        String::new(),
+        appl,
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
new file mode 100644 (file)
index 0000000..1c7e62e
--- /dev/null
@@ -0,0 +1,140 @@
+use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty};
+use if_chain::if_chain;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`
+    ///
+    /// **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
+    ///
+    /// **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors.
+    ///
+    /// **Example:**
+    /// Before:
+    /// ```rust
+    /// fn divisible_by_3(i_str: String) -> Result<(), String> {
+    ///     let i = i_str
+    ///         .parse::<i32>()
+    ///         .expect("cannot divide the input by three");
+    ///
+    ///     if i % 3 != 0 {
+    ///         Err("Number is not divisible by 3")?
+    ///     }
+    ///
+    ///     Ok(())
+    /// }
+    /// ```
+    ///
+    /// After:
+    /// ```rust
+    /// fn divisible_by_3(i_str: String) -> Result<(), String> {
+    ///     let i = i_str
+    ///         .parse::<i32>()
+    ///         .map_err(|e| format!("cannot divide the input by three: {}", e))?;
+    ///
+    ///     if i % 3 != 0 {
+    ///         Err("Number is not divisible by 3")?
+    ///     }
+    ///
+    ///     Ok(())
+    /// }
+    /// ```
+    pub UNWRAP_IN_RESULT,
+    restriction,
+    "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
+}
+
+declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]);
+
+impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
+    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
+        if_chain! {
+            // first check if it's a method or function
+            if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
+            // checking if its return type is `result` or `option`
+            if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type))
+                || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type));
+            then {
+                lint_impl_body(cx, impl_item.span, impl_item);
+            }
+        }
+    }
+}
+
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{Expr, ImplItemKind};
+
+struct FindExpectUnwrap<'a, 'tcx> {
+    lcx: &'a LateContext<'tcx>,
+    typeck_results: &'tcx ty::TypeckResults<'tcx>,
+    result: Vec<Span>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+        // check for `expect`
+        if let Some(arglists) = method_chain_args(expr, &["expect"]) {
+            let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
+            if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
+                || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
+            {
+                self.result.push(expr.span);
+            }
+        }
+
+        // check for `unwrap`
+        if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
+            let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
+            if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
+                || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
+            {
+                self.result.push(expr.span);
+            }
+        }
+
+        // and check sub-expressions
+        intravisit::walk_expr(self, expr);
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
+    if_chain! {
+
+        if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
+        then {
+            let body = cx.tcx.hir().body(body_id);
+            let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
+            let mut fpu = FindExpectUnwrap {
+                lcx: cx,
+                typeck_results: cx.tcx.typeck(impl_item_def_id),
+                result: Vec::new(),
+            };
+            fpu.visit_expr(&body.value);
+
+            // if we've found one, lint
+            if  !fpu.result.is_empty()  {
+                span_lint_and_then(
+                    cx,
+                    UNWRAP_IN_RESULT,
+                    impl_span,
+                    "used unwrap or expect in a function that returns result or option",
+                    move |diag| {
+                        diag.help(
+                            "unwrap and expect should not be used in a function that returns result or option" );
+                        diag.span_note(fpu.result, "potential non-recoverable error(s)");
+                    });
+            }
+        }
+    }
+}
index 776c6bc57ca6f857c4d86b699d99a4d5f2351f41..427a1b6577315c271611a0cd9c5c5d8e721a4346 100644 (file)
@@ -50,7 +50,7 @@
     /// ```
     pub USE_SELF,
     nursery,
-    "Unnecessary structure name repetition whereas `Self` is applicable"
+    "unnecessary structure name repetition whereas `Self` is applicable"
 }
 
 declare_lint_pass!(UseSelf => [USE_SELF]);
index 1bf37632e326cfa2d7a76be9361983a49fecd731..4ab2b5e796deb75f7361fc466c9d69306e7c7db4 100644 (file)
@@ -1,3 +1,4 @@
+use crate::utils::sugg::Sugg;
 use crate::utils::{
     get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet,
     snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg,
@@ -158,7 +159,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                             if TyS::same_type(a, b);
 
                             then {
-                                let sugg = snippet(cx, args[0].span.source_callsite(), "<expr>").into_owned();
+                                let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "<expr>").maybe_par();
                                 let sugg_msg =
                                     format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
                                 span_lint_and_sugg(
@@ -167,7 +168,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                                     e.span,
                                     "useless conversion to the same type",
                                     &sugg_msg,
-                                    sugg,
+                                    sugg.to_string(),
                                     Applicability::MachineApplicable, // snippet
                                 );
                             }
index c32c80dcd3ce6da1f31bdd87e6ddb7ca62948626..7b419431c0f51dafe566ad6a29dd46394213ac74 100644 (file)
@@ -5,8 +5,8 @@
 #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
 
 use crate::utils::{both, over};
-use rustc_ast::{self as ast, *};
 use rustc_ast::ptr::P;
+use rustc_ast::{self as ast, *};
 use rustc_span::symbol::Ident;
 use std::mem;
 
index 9b7a268c6287c1fffd8b2fab131bb2d53bbeb175..6eda6d1fa834074abca08450b2ece236c44ba175 100644 (file)
@@ -175,18 +175,15 @@ fn next(&mut self, s: &'static str) -> String {
     }
 
     fn print_qpath(&mut self, path: &QPath<'_>) {
-        match  *path {
-            QPath::LangItem(lang_item, _) => {
-                println!(
-                    "    if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
-                   self.current, lang_item,
-                );
-            },
-            _ => {
-                print!("    if match_qpath({}, &[", self.current);
-                print_path(path, &mut true);
-                println!("]);");
-            },
+        if let QPath::LangItem(lang_item, _) = *path {
+            println!(
+                "    if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
+                self.current, lang_item,
+            );
+        } else {
+            print!("    if match_qpath({}, &[", self.current);
+            print_path(path, &mut true);
+            println!("]);");
         }
     }
 }
index ba3492a6fff1136dc1d70fcce17ef362da35292d..292dbd7ad6b480babd426aae3dadc51060b419ac 100644 (file)
@@ -138,7 +138,7 @@ fn $config() -> $Ty {
     (type_complexity_threshold, "type_complexity_threshold": u64, 250),
     /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
     (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
-    /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
+    /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
     (too_large_for_stack, "too_large_for_stack": u64, 200),
     /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
     (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
index ba15456014d35e730a7876b0077600ec59d13867..8563b469a30dd73772ee71722bdf04520f67575d 100644 (file)
@@ -56,43 +56,45 @@ fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Ex
     }
 
     match expr.kind {
-        hir::ExprKind::Call(ref path, ref args) if matches!(
-            path.kind,
-            hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
-        ) => Some(Range {
-            start: Some(&args[0]),
-            end: Some(&args[1]),
-            limits: ast::RangeLimits::Closed,
-        }),
-        hir::ExprKind::Struct(ref path, ref fields, None) => {
-            match path {
-                hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
-                    start: None,
-                    end: None,
-                    limits: ast::RangeLimits::HalfOpen,
-                }),
-                hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
-                    start: Some(get_field("start", fields)?),
-                    end: None,
-                    limits: ast::RangeLimits::HalfOpen,
-                }),
-                hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
-                    start: Some(get_field("start", fields)?),
-                    end: Some(get_field("end", fields)?),
-                    limits: ast::RangeLimits::HalfOpen,
-                }),
-                hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
-                    start: None,
-                    end: Some(get_field("end", fields)?),
-                    limits: ast::RangeLimits::Closed,
-                }),
-                hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
-                    start: None,
-                    end: Some(get_field("end", fields)?),
-                    limits: ast::RangeLimits::HalfOpen,
-                }),
-                _ => None,
-            }
+        hir::ExprKind::Call(ref path, ref args)
+            if matches!(
+                path.kind,
+                hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
+            ) =>
+        {
+            Some(Range {
+                start: Some(&args[0]),
+                end: Some(&args[1]),
+                limits: ast::RangeLimits::Closed,
+            })
+        },
+        hir::ExprKind::Struct(ref path, ref fields, None) => match path {
+            hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
+                start: None,
+                end: None,
+                limits: ast::RangeLimits::HalfOpen,
+            }),
+            hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
+                start: Some(get_field("start", fields)?),
+                end: None,
+                limits: ast::RangeLimits::HalfOpen,
+            }),
+            hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
+                start: Some(get_field("start", fields)?),
+                end: Some(get_field("end", fields)?),
+                limits: ast::RangeLimits::HalfOpen,
+            }),
+            hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
+                start: None,
+                end: Some(get_field("end", fields)?),
+                limits: ast::RangeLimits::Closed,
+            }),
+            hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
+                start: None,
+                end: Some(get_field("end", fields)?),
+                limits: ast::RangeLimits::HalfOpen,
+            }),
+            _ => None,
         },
         _ => None,
     }
index 2eefd4a38a67a5056981c2622deaebedc78cc348..c7263f48965a5a505e0e1f879fda4eaf2d0c5522 100644 (file)
@@ -3,9 +3,9 @@
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_hir::{
-    BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat,
-    FnRetTy, GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName,
-    Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
+    BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy,
+    GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path,
+    PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
 };
 use rustc_lint::LateContext;
 use rustc_middle::ich::StableHashingContextProvider;
@@ -23,9 +23,7 @@ pub struct SpanlessEq<'a, 'tcx> {
     /// Context used to evaluate constant expressions.
     cx: &'a LateContext<'tcx>,
     maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
-    /// If is true, never consider as equal expressions containing function
-    /// calls.
-    ignore_fn: bool,
+    allow_side_effects: bool,
 }
 
 impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
@@ -33,13 +31,14 @@ pub fn new(cx: &'a LateContext<'tcx>) -> Self {
         Self {
             cx,
             maybe_typeck_results: cx.maybe_typeck_results(),
-            ignore_fn: false,
+            allow_side_effects: true,
         }
     }
 
-    pub fn ignore_fn(self) -> Self {
+    /// Consider expressions containing potential side effects as not equal.
+    pub fn deny_side_effects(self) -> Self {
         Self {
-            ignore_fn: true,
+            allow_side_effects: false,
             ..self
         }
     }
@@ -67,7 +66,7 @@ pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 
     #[allow(clippy::similar_names)]
     pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
-        if self.ignore_fn && differing_macro_contexts(left.span, right.span) {
+        if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) {
             return false;
         }
 
@@ -90,10 +89,10 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
                 both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
             },
             (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
-                self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+                self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
             },
             (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => {
-                lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+                self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
             },
             (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r),
             (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => {
@@ -108,7 +107,7 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
             },
             (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
             (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
-                !self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
+                self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
             },
             (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt))
             | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => {
@@ -134,7 +133,7 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
                     })
             },
             (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => {
-                !self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
+                self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
             },
             (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => {
                 let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body));
@@ -186,10 +185,8 @@ fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
     }
 
     pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool {
-        match (&left, &right) {
-            (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) =>
-                li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp),
-        }
+        let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right);
+        li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp)
     }
 
     /// Checks whether two patterns are the same.
@@ -233,8 +230,7 @@ fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
             (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => {
                 self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
             },
-            (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) =>
-                llang_item == rlang_item,
+            (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item,
             _ => false,
         }
     }
@@ -352,6 +348,11 @@ pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -
     left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
 }
 
+/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
+pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
+}
+
 /// Type used to hash an ast element. This is different from the `Hash` trait
 /// on ast types as this
 /// trait would consider IDs and spans.
@@ -615,7 +616,7 @@ pub fn hash_qpath(&mut self, p: &QPath<'_>) {
             },
             QPath::LangItem(lang_item, ..) => {
                 lang_item.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
-            }
+            },
         }
         // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
     }
@@ -727,7 +728,7 @@ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
                 },
                 QPath::LangItem(lang_item, ..) => {
                     lang_item.hash(&mut self.s);
-                }
+                },
             },
             TyKind::OpaqueDef(_, arg_list) => {
                 self.hash_generic_args(arg_list);
index 6c2356799142db6b609fb6c2a77be4c00f8266a2..8fa5d22210a3691f585b16256dc54e5464827d85 100644 (file)
@@ -1,7 +1,6 @@
-use crate::utils::SpanlessEq;
 use crate::utils::{
     is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint,
-    span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty,
+    span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId};
@@ -493,7 +492,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             if let StmtKind::Semi(only_expr) = &stmts[0].kind;
             if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind;
             let and_then_snippets = get_and_then_snippets(cx, and_then_args);
-            let mut sle = SpanlessEq::new(cx).ignore_fn();
+            let mut sle = SpanlessEq::new(cx).deny_side_effects();
             then {
                 match &*ps.ident.as_str() {
                     "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
index a56b8203513e6d2c8945a5b520cb9093d4483b40..82005257115642072aa4c26858eeda9532904075 100644 (file)
@@ -21,7 +21,7 @@
 pub mod usage;
 pub use self::attrs::*;
 pub use self::diagnostics::*;
-pub use self::hir_utils::{both, over, SpanlessEq, SpanlessHash};
+pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
 
 use std::borrow::Cow;
 use std::mem;
@@ -42,7 +42,8 @@
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::map::Map;
-use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
 use rustc_mir::const_eval;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::original_sp;
@@ -574,7 +575,7 @@ pub fn snippet_block<'a, T: LintContext>(
 }
 
 /// Same as `snippet_block`, but adapts the applicability level by the rules of
-/// `snippet_with_applicabiliy`.
+/// `snippet_with_applicability`.
 pub fn snippet_block_with_applicability<'a, T: LintContext>(
     cx: &T,
     span: Span,
@@ -866,6 +867,14 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx>
     cx.tcx.erase_late_bound_regions(&ret_ty)
 }
 
+/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
+pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
+    ty.walk().any(|inner| match inner.unpack() {
+        GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
+        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+    })
+}
+
 /// Returns `true` if the given type is an `unsafe` function.
 pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     match ty.kind {
@@ -1304,7 +1313,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     }
 }
 
-// check if expr is calling method or function with #[must_use] attribyte
+// check if expr is calling method or function with #[must_use] attribute
 pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let did = match expr.kind {
         ExprKind::Call(ref path, _) => if_chain! {
@@ -1409,11 +1418,13 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
     }
 }
 
-/// Returns true iff the given expression is a slice of primitives (as defined in the
-/// `is_recursively_primitive_type` function).
-pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+/// Returns Option<String> where String is a textual representation of the type encapsulated in the
+/// slice iff the given expression is a slice of primitives (as defined in the
+/// `is_recursively_primitive_type` function) and None otherwise.
+pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
     let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
-    match expr_type.kind {
+    let expr_kind = &expr_type.kind;
+    let is_primitive = match expr_kind {
         ty::Slice(ref element_type)
         | ty::Ref(
             _,
@@ -1424,7 +1435,24 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
             _,
         ) => is_recursively_primitive_type(element_type),
         _ => false,
+    };
+
+    if is_primitive {
+        // if we have wrappers like Array, Slice or Tuple, print these
+        // and get the type enclosed in the slice ref
+        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind {
+            ty::Slice(..) => return Some("slice".into()),
+            ty::Array(..) => return Some("array".into()),
+            ty::Tuple(..) => return Some("tuple".into()),
+            _ => {
+                // is_recursively_primitive_type() should have taken care
+                // of the rest and we can rely on the type that is found
+                let refs_peeled = expr_type.peel_refs();
+                return Some(refs_peeled.walk().last().unwrap().to_string());
+            },
+        }
     }
+    None
 }
 
 #[macro_export]
index 9c28d63d414c525b2ae5e3013be44ab07fef0255..d44854aefe97acfd2c3943657ac107e428ecde8d 100644 (file)
@@ -35,6 +35,8 @@
 pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
 pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
 pub const EXIT: [&str; 3] = ["std", "process", "exit"];
+pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"];
+pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"];
 pub const FILE: [&str; 3] = ["std", "fs", "File"];
 pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
 pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
@@ -84,6 +86,7 @@
 pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
 pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
+pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
 pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
index f2e76442a19bae5775d72f5343183bf10ac4066f..84e907d7125de8951f291dc201a4c453d417c74d 100644 (file)
@@ -1,13 +1,20 @@
-use crate::consts::constant;
+use crate::consts::{constant, Constant};
+use crate::rustc_target::abi::LayoutOf;
 use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 
+#[allow(clippy::module_name_repetitions)]
+#[derive(Copy, Clone)]
+pub struct UselessVec {
+    pub too_large_for_stack: u64,
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would
     /// be possible.
@@ -31,7 +38,7 @@
     "useless `vec!`"
 }
 
-declare_lint_pass!(UselessVec => [USELESS_VEC]);
+impl_lint_pass!(UselessVec => [USELESS_VEC]);
 
 impl<'tcx> LateLintPass<'tcx> for UselessVec {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -42,7 +49,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind;
             if let Some(vec_args) = higher::vec_macro(cx, addressee);
             then {
-                check_vec_macro(cx, &vec_args, expr.span);
+                self.check_vec_macro(cx, &vec_args, expr.span);
             }
         }
 
@@ -60,46 +67,62 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     .ctxt()
                     .outer_expn_data()
                     .call_site;
-                check_vec_macro(cx, &vec_args, span);
+                self.check_vec_macro(cx, &vec_args, span);
             }
         }
     }
 }
 
-fn check_vec_macro<'tcx>(cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
-    let mut applicability = Applicability::MachineApplicable;
-    let snippet = match *vec_args {
-        higher::VecArgs::Repeat(elem, len) => {
-            if constant(cx, cx.typeck_results(), len).is_some() {
-                format!(
-                    "&[{}; {}]",
-                    snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
-                    snippet_with_applicability(cx, len.span, "len", &mut applicability)
-                )
-            } else {
-                return;
-            }
-        },
-        higher::VecArgs::Vec(args) => {
-            if let Some(last) = args.iter().last() {
-                let span = args[0].span.to(last.span);
+impl UselessVec {
+    fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
+        let mut applicability = Applicability::MachineApplicable;
+        let snippet = match *vec_args {
+            higher::VecArgs::Repeat(elem, len) => {
+                if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) {
+                    #[allow(clippy::cast_possible_truncation)]
+                    if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
+                        return;
+                    }
 
-                format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
-            } else {
-                "&[]".into()
-            }
-        },
-    };
+                    format!(
+                        "&[{}; {}]",
+                        snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
+                        snippet_with_applicability(cx, len.span, "len", &mut applicability)
+                    )
+                } else {
+                    return;
+                }
+            },
+            higher::VecArgs::Vec(args) => {
+                if let Some(last) = args.iter().last() {
+                    #[allow(clippy::cast_possible_truncation)]
+                    if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
+                        return;
+                    }
+                    let span = args[0].span.to(last.span);
+
+                    format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
+                } else {
+                    "&[]".into()
+                }
+            },
+        };
+
+        span_lint_and_sugg(
+            cx,
+            USELESS_VEC,
+            span,
+            "useless use of `vec!`",
+            "you can use a slice directly",
+            snippet,
+            applicability,
+        );
+    }
+}
 
-    span_lint_and_sugg(
-        cx,
-        USELESS_VEC,
-        span,
-        "useless use of `vec!`",
-        "you can use a slice directly",
-        snippet,
-        applicability,
-    );
+fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
+    let ty = cx.typeck_results().expr_ty_adjusted(expr);
+    cx.layout_of(ty).map_or(0, |l| l.size.bytes())
 }
 
 /// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
index e7eb7c2e9802d05c249f9e9ebe8f4cbb633c7b40..5683a71efea4eff68208fd420aeda4c0d95b73d5 100644 (file)
@@ -195,13 +195,10 @@ fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> boo
     }
 }
 
-// Allow "...prelude::*" imports.
+// Allow "...prelude::..::*" imports.
 // Many crates have a prelude, and it is imported as a glob by design.
 fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
-    segments
-        .iter()
-        .last()
-        .map_or(false, |ps| ps.ident.as_str() == "prelude")
+    segments.iter().any(|ps| ps.ident.as_str() == "prelude")
 }
 
 // Allow "super::*" imports in tests.
index 063f94582b9d14cc38f138d8914e8ce178aaae66..e653240d049170b61d4755cd964185195e2edce7 100644 (file)
@@ -237,7 +237,7 @@ fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
     fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
         if mac.path == sym!(println) {
             span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
-            if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) {
+            if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
                 if fmt_str.symbol == Symbol::intern("") {
                     span_lint_and_sugg(
                         cx,
@@ -252,7 +252,7 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
             }
         } else if mac.path == sym!(print) {
             span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
-            if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) {
+            if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
                 if check_newlines(&fmt_str) {
                     span_lint_and_then(
                         cx,
@@ -273,7 +273,7 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
                 }
             }
         } else if mac.path == sym!(write) {
-            if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
+            if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) {
                 if check_newlines(&fmt_str) {
                     span_lint_and_then(
                         cx,
@@ -294,16 +294,17 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
                 }
             }
         } else if mac.path == sym!(writeln) {
-            if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
+            if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) {
                 if fmt_str.symbol == Symbol::intern("") {
                     let mut applicability = Applicability::MachineApplicable;
-                    let suggestion = expr.map_or_else(
-                        || {
-                            applicability = Applicability::HasPlaceholders;
-                            Cow::Borrowed("v")
-                        },
-                        |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable),
-                    );
+                    // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed
+                    #[allow(clippy::option_if_let_else)]
+                    let suggestion = if let Some(e) = expr {
+                        snippet_with_applicability(cx, e.span, "v", &mut applicability)
+                    } else {
+                        applicability = Applicability::HasPlaceholders;
+                        Cow::Borrowed("v")
+                    };
 
                     span_lint_and_sugg(
                         cx,
@@ -364,17 +365,11 @@ impl Write {
     /// (Some("string to write: {}"), Some(buf))
     /// ```
     #[allow(clippy::too_many_lines)]
-    fn check_tts<'a>(
-        &self,
-        cx: &EarlyContext<'a>,
-        tts: &TokenStream,
-        is_write: bool,
-    ) -> (Option<StrLit>, Option<Expr>) {
+    fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
         use rustc_parse_format::{
             AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, ParseMode, Parser,
             Piece,
         };
-        let tts = tts.clone();
 
         let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None);
         let mut expr: Option<Expr> = None;
index 168092f7329cc0caed65ea30f0edfe20ac91644c..3c782e9b17ff15c0db0947b54b766aa52f8326ef 100644 (file)
@@ -295,8 +295,14 @@ impl EarlyLintPass for FooFunctions {
 
 Running our UI test should now produce output that contains the lint message.
 
+According to [the rustc-dev-guide], the text should be matter of fact and avoid
+capitalization and periods, unless multiple sentences are needed.
+When code or an identifier must appear in a message or label, it should be
+surrounded with single acute accents \`.
+
 [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
 [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
+[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
 
 ## Adding the lint logic
 
index bbb300296be9720ab629d8426e03fef89e6c5379..687fac7baa848038e9c5a735728907dd177b37fb 100644 (file)
         deprecation: None,
         module: "misc",
     },
+    Lint {
+        name: "float_equality_without_abs",
+        group: "correctness",
+        desc: "float equality check without `.abs()`",
+        deprecation: None,
+        module: "float_equality_without_abs",
+    },
     Lint {
         name: "fn_address_comparisons",
         group: "correctness",
         group: "style",
         desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block",
         deprecation: None,
-        module: "let_and_return",
+        module: "returns",
     },
     Lint {
         name: "let_underscore_lock",
         deprecation: None,
         module: "methods",
     },
+    Lint {
+        name: "self_assignment",
+        group: "correctness",
+        desc: "explicit self-assignment",
+        deprecation: None,
+        module: "self_assignment",
+    },
     Lint {
         name: "serde_api_misuse",
         group: "correctness",
         deprecation: None,
         module: "methods",
     },
+    Lint {
+        name: "single_char_push_str",
+        group: "style",
+        desc: "`push_str()` used with a single-character string literal as parameter",
+        deprecation: None,
+        module: "methods",
+    },
     Lint {
         name: "single_component_path_imports",
         group: "style",
         deprecation: None,
         module: "to_digit_is_some",
     },
+    Lint {
+        name: "to_string_in_display",
+        group: "correctness",
+        desc: "`to_string` method used while implementing `Display` trait",
+        deprecation: None,
+        module: "to_string_in_display",
+    },
     Lint {
         name: "todo",
         group: "restriction",
         deprecation: None,
         module: "methods",
     },
+    Lint {
+        name: "unnecessary_lazy_evaluations",
+        group: "style",
+        desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation",
+        deprecation: None,
+        module: "methods",
+    },
     Lint {
         name: "unnecessary_mut_passed",
         group: "style",
         group: "style",
         desc: "needless unit expression",
         deprecation: None,
-        module: "returns",
+        module: "unused_unit",
+    },
+    Lint {
+        name: "unwrap_in_result",
+        group: "restriction",
+        desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`",
+        deprecation: None,
+        module: "unwrap_in_result",
     },
     Lint {
         name: "unwrap_used",
     Lint {
         name: "use_self",
         group: "nursery",
-        desc: "Unnecessary structure name repetition whereas `Self` is applicable",
+        desc: "unnecessary structure name repetition whereas `Self` is applicable",
         deprecation: None,
         module: "use_self",
     },
index 3aff8741f6051d7e6b95e525bc1a37812b3e2173..7616d8001e8853bb8e9de4e44659d01b3253bd6f 100644 (file)
@@ -7,7 +7,7 @@ fn fmt() {
         return;
     }
 
-    // Skip this test if rustup nightly is unavailable
+    // Skip this test if nightly rustfmt is unavailable
     let rustup_output = Command::new("rustup")
         .args(&["component", "list", "--toolchain", "nightly"])
         .output()
@@ -19,12 +19,9 @@ fn fmt() {
     }
 
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
-    let dev_dir = root_dir.join("clippy_dev");
-    let target_dir = root_dir.join("target");
-    let target_dir = target_dir.to_str().unwrap();
     let output = Command::new("cargo")
-        .current_dir(dev_dir)
-        .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"])
+        .current_dir(root_dir)
+        .args(&["dev", "fmt", "--check"])
         .output()
         .unwrap();
 
index 414477aedd78323ccb46ebe0eea1be00f3d7b841..d75cdd625f9ec3de170ff6adae0bad84563d47e3 100644 (file)
@@ -19,3 +19,9 @@ pub enum ExternExportedEnum {
         A,
     }
 }
+
+pub mod prelude {
+    pub mod v1 {
+        pub struct PreludeModAnywhere;
+    }
+}
index fef9f4f39f80941d56d58225a086c526bcfa9a43..a414832bcd36200765be6b70ae3fe59588d64586 100644 (file)
@@ -2,7 +2,7 @@
 #![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)]
 
 use std::borrow::Cow;
-use std::cell::Cell;
+use std::cell::{Cell, UnsafeCell};
 use std::fmt::Display;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Once;
@@ -30,6 +30,37 @@ impl Trait<u32> for u64 {
     const ATOMIC: AtomicUsize = AtomicUsize::new(9);
 }
 
+// This is just a pointer that can be safely dereferended,
+// it's semantically the same as `&'static T`;
+// but it isn't allowed to make a static reference from an arbitrary integer value at the moment.
+// For more information, please see the issue #5918.
+pub struct StaticRef<T> {
+    ptr: *const T,
+}
+
+impl<T> StaticRef<T> {
+    /// Create a new `StaticRef` from a raw pointer
+    ///
+    /// ## Safety
+    ///
+    /// Callers must pass in a reference to statically allocated memory which
+    /// does not overlap with other values.
+    pub const unsafe fn new(ptr: *const T) -> StaticRef<T> {
+        StaticRef { ptr }
+    }
+}
+
+impl<T> std::ops::Deref for StaticRef<T> {
+    type Target = T;
+
+    fn deref(&self) -> &'static T {
+        unsafe { &*self.ptr }
+    }
+}
+
+// use a tuple to make sure referencing a field behind a pointer isn't linted.
+const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
+
 fn main() {
     ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
@@ -82,4 +113,6 @@ fn main() {
     assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
 
     assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
+
+    let _ = &CELL_REF.0;
 }
index dc738064a17184ddf9c1177e36d18a9db60c31b8..1e0b3e4d20a5236c6b7c72162f0490bd8eb54345 100644 (file)
@@ -1,5 +1,5 @@
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:34:5
+  --> $DIR/borrow_interior_mutable_const.rs:65:5
    |
 LL |     ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
    |     ^^^^^^
@@ -8,7 +8,7 @@ LL |     ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:35:16
+  --> $DIR/borrow_interior_mutable_const.rs:66:16
    |
 LL |     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
    |                ^^^^^^
@@ -16,7 +16,7 @@ LL |     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:38:22
+  --> $DIR/borrow_interior_mutable_const.rs:69:22
    |
 LL |     let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
    |                      ^^^^^^^^^
@@ -24,7 +24,7 @@ LL |     let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:39:25
+  --> $DIR/borrow_interior_mutable_const.rs:70:25
    |
 LL |     let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
    |                         ^^^^^^^^^
@@ -32,7 +32,7 @@ LL |     let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:40:27
+  --> $DIR/borrow_interior_mutable_const.rs:71:27
    |
 LL |     let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
    |                           ^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:41:26
+  --> $DIR/borrow_interior_mutable_const.rs:72:26
    |
 LL |     let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
    |                          ^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:52:14
+  --> $DIR/borrow_interior_mutable_const.rs:83:14
    |
 LL |     let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
    |              ^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:53:14
+  --> $DIR/borrow_interior_mutable_const.rs:84:14
    |
 LL |     let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
    |              ^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:54:19
+  --> $DIR/borrow_interior_mutable_const.rs:85:19
    |
 LL |     let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
    |                   ^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:55:14
+  --> $DIR/borrow_interior_mutable_const.rs:86:14
    |
 LL |     let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
    |              ^^^^^^^^^^^^
@@ -80,7 +80,7 @@ LL |     let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:56:13
+  --> $DIR/borrow_interior_mutable_const.rs:87:13
    |
 LL |     let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
    |             ^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL |     let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:62:13
+  --> $DIR/borrow_interior_mutable_const.rs:93:13
    |
 LL |     let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
    |             ^^^^^^^^^^^^
@@ -96,7 +96,7 @@ LL |     let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:67:5
+  --> $DIR/borrow_interior_mutable_const.rs:98:5
    |
 LL |     CELL.set(2); //~ ERROR interior mutability
    |     ^^^^
@@ -104,7 +104,7 @@ LL |     CELL.set(2); //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:68:16
+  --> $DIR/borrow_interior_mutable_const.rs:99:16
    |
 LL |     assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
    |                ^^^^
@@ -112,7 +112,7 @@ LL |     assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:81:5
+  --> $DIR/borrow_interior_mutable_const.rs:112:5
    |
 LL |     u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
    |     ^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL |     u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:82:16
+  --> $DIR/borrow_interior_mutable_const.rs:113:16
    |
 LL |     assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
    |                ^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5944.rs b/src/tools/clippy/tests/ui/crashes/ice-5944.rs
new file mode 100644 (file)
index 0000000..5caf29c
--- /dev/null
@@ -0,0 +1,13 @@
+#![warn(clippy::repeat_once)]
+
+trait Repeat {
+    fn repeat(&self) {}
+}
+
+impl Repeat for usize {
+    fn repeat(&self) {}
+}
+
+fn main() {
+    let _ = 42.repeat();
+}
index bd8adc2c57055b20e0b515c9f24c33dbcaab902d..cdbeff6a037834f818e717ddc8ae24842dc8b412 100644 (file)
@@ -1,4 +1,4 @@
-error: Calling `subsec_millis()` is more concise than this calculation
+error: calling `subsec_millis()` is more concise than this calculation
   --> $DIR/duration_subsec.rs:10:24
    |
 LL |     let bad_millis_1 = dur.subsec_micros() / 1_000;
@@ -6,25 +6,25 @@ LL |     let bad_millis_1 = dur.subsec_micros() / 1_000;
    |
    = note: `-D clippy::duration-subsec` implied by `-D warnings`
 
-error: Calling `subsec_millis()` is more concise than this calculation
+error: calling `subsec_millis()` is more concise than this calculation
   --> $DIR/duration_subsec.rs:11:24
    |
 LL |     let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
 
-error: Calling `subsec_micros()` is more concise than this calculation
+error: calling `subsec_micros()` is more concise than this calculation
   --> $DIR/duration_subsec.rs:16:22
    |
 LL |     let bad_micros = dur.subsec_nanos() / 1_000;
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
 
-error: Calling `subsec_micros()` is more concise than this calculation
+error: calling `subsec_micros()` is more concise than this calculation
   --> $DIR/duration_subsec.rs:21:13
    |
 LL |     let _ = (&dur).subsec_nanos() / 1_000;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()`
 
-error: Calling `subsec_micros()` is more concise than this calculation
+error: calling `subsec_micros()` is more concise than this calculation
   --> $DIR/duration_subsec.rs:25:13
    |
 LL |     let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
index 71f3f5e083e0d1f67739aa5055b7b0d78a363c26..5935eea5e036e74845b71e7020526cd1d4426195 100644 (file)
@@ -1,4 +1,4 @@
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:8:5
    |
 LL |     X = 0x1_0000_0000,
@@ -6,49 +6,49 @@ LL |     X = 0x1_0000_0000,
    |
    = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings`
 
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:15:5
    |
 LL |     X = 0x1_0000_0000,
    |     ^^^^^^^^^^^^^^^^^
 
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:18:5
    |
 LL |     A = 0xFFFF_FFFF,
    |     ^^^^^^^^^^^^^^^
 
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:25:5
    |
 LL |     Z = 0xFFFF_FFFF,
    |     ^^^^^^^^^^^^^^^
 
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:26:5
    |
 LL |     A = 0x1_0000_0000,
    |     ^^^^^^^^^^^^^^^^^
 
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:28:5
    |
 LL |     C = (i32::MIN as isize) - 1,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:34:5
    |
 LL |     Z = 0xFFFF_FFFF,
    |     ^^^^^^^^^^^^^^^
 
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:35:5
    |
 LL |     A = 0x1_0000_0000,
    |     ^^^^^^^^^^^^^^^^^
 
-error: Clike enum variant discriminant is not portable to 32-bit targets
+error: C-like enum variant discriminant is not portable to 32-bit targets
   --> $DIR/enum_clike_unportable_variant.rs:40:5
    |
 LL |     X = <usize as Trait>::Number,
index 2835391de7f5838a7013ff98feb3357826c03df7..b1d481190ff53bdfa68e41934fa668c49edb05f6 100644 (file)
@@ -1,4 +1,4 @@
-error: Variant name ends with the enum's name
+error: variant name ends with the enum's name
   --> $DIR/enum_variants.rs:16:5
    |
 LL |     cFoo,
@@ -6,25 +6,25 @@ LL |     cFoo,
    |
    = note: `-D clippy::enum-variant-names` implied by `-D warnings`
 
-error: Variant name starts with the enum's name
+error: variant name starts with the enum's name
   --> $DIR/enum_variants.rs:27:5
    |
 LL |     FoodGood,
    |     ^^^^^^^^
 
-error: Variant name starts with the enum's name
+error: variant name starts with the enum's name
   --> $DIR/enum_variants.rs:28:5
    |
 LL |     FoodMiddle,
    |     ^^^^^^^^^^
 
-error: Variant name starts with the enum's name
+error: variant name starts with the enum's name
   --> $DIR/enum_variants.rs:29:5
    |
 LL |     FoodBad,
    |     ^^^^^^^
 
-error: All variants have the same prefix: `Food`
+error: all variants have the same prefix: `Food`
   --> $DIR/enum_variants.rs:26:1
    |
 LL | / enum Food {
@@ -36,7 +36,7 @@ LL | | }
    |
    = help: remove the prefixes and use full paths to the variants instead of glob imports
 
-error: All variants have the same prefix: `CallType`
+error: all variants have the same prefix: `CallType`
   --> $DIR/enum_variants.rs:36:1
    |
 LL | / enum BadCallType {
@@ -48,7 +48,7 @@ LL | | }
    |
    = help: remove the prefixes and use full paths to the variants instead of glob imports
 
-error: All variants have the same prefix: `Constant`
+error: all variants have the same prefix: `Constant`
   --> $DIR/enum_variants.rs:48:1
    |
 LL | / enum Consts {
@@ -60,7 +60,7 @@ LL | | }
    |
    = help: remove the prefixes and use full paths to the variants instead of glob imports
 
-error: All variants have the same prefix: `With`
+error: all variants have the same prefix: `With`
   --> $DIR/enum_variants.rs:82:1
    |
 LL | / enum Seallll {
@@ -72,7 +72,7 @@ LL | | }
    |
    = help: remove the prefixes and use full paths to the variants instead of glob imports
 
-error: All variants have the same prefix: `Prefix`
+error: all variants have the same prefix: `Prefix`
   --> $DIR/enum_variants.rs:88:1
    |
 LL | / enum NonCaps {
@@ -84,7 +84,7 @@ LL | | }
    |
    = help: remove the prefixes and use full paths to the variants instead of glob imports
 
-error: All variants have the same prefix: `With`
+error: all variants have the same prefix: `With`
   --> $DIR/enum_variants.rs:94:1
    |
 LL | / pub enum PubSeall {
diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.rs b/src/tools/clippy/tests/ui/float_equality_without_abs.rs
new file mode 100644 (file)
index 0000000..d40fa00
--- /dev/null
@@ -0,0 +1,31 @@
+#![warn(clippy::float_equality_without_abs)]
+
+pub fn is_roughly_equal(a: f32, b: f32) -> bool {
+    (a - b) < f32::EPSILON
+}
+
+pub fn main() {
+    // all errors
+    is_roughly_equal(1.0, 2.0);
+    let a = 0.05;
+    let b = 0.0500001;
+
+    let _ = (a - b) < f32::EPSILON;
+    let _ = a - b < f32::EPSILON;
+    let _ = a - b.abs() < f32::EPSILON;
+    let _ = (a as f64 - b as f64) < f64::EPSILON;
+    let _ = 1.0 - 2.0 < f32::EPSILON;
+
+    let _ = f32::EPSILON > (a - b);
+    let _ = f32::EPSILON > a - b;
+    let _ = f32::EPSILON > a - b.abs();
+    let _ = f64::EPSILON > (a as f64 - b as f64);
+    let _ = f32::EPSILON > 1.0 - 2.0;
+
+    // those are correct
+    let _ = (a - b).abs() < f32::EPSILON;
+    let _ = (a as f64 - b as f64).abs() < f64::EPSILON;
+
+    let _ = f32::EPSILON > (a - b).abs();
+    let _ = f64::EPSILON > (a as f64 - b as f64).abs();
+}
diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr
new file mode 100644 (file)
index 0000000..b34c815
--- /dev/null
@@ -0,0 +1,92 @@
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:4:5
+   |
+LL |     (a - b) < f32::EPSILON
+   |     -------^^^^^^^^^^^^^^^
+   |     |
+   |     help: add `.abs()`: `(a - b).abs()`
+   |
+   = note: `-D clippy::float-equality-without-abs` implied by `-D warnings`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:13:13
+   |
+LL |     let _ = (a - b) < f32::EPSILON;
+   |             -------^^^^^^^^^^^^^^^
+   |             |
+   |             help: add `.abs()`: `(a - b).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:14:13
+   |
+LL |     let _ = a - b < f32::EPSILON;
+   |             -----^^^^^^^^^^^^^^^
+   |             |
+   |             help: add `.abs()`: `(a - b).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:15:13
+   |
+LL |     let _ = a - b.abs() < f32::EPSILON;
+   |             -----------^^^^^^^^^^^^^^^
+   |             |
+   |             help: add `.abs()`: `(a - b.abs()).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:16:13
+   |
+LL |     let _ = (a as f64 - b as f64) < f64::EPSILON;
+   |             ---------------------^^^^^^^^^^^^^^^
+   |             |
+   |             help: add `.abs()`: `(a as f64 - b as f64).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:17:13
+   |
+LL |     let _ = 1.0 - 2.0 < f32::EPSILON;
+   |             ---------^^^^^^^^^^^^^^^
+   |             |
+   |             help: add `.abs()`: `(1.0 - 2.0).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:19:13
+   |
+LL |     let _ = f32::EPSILON > (a - b);
+   |             ^^^^^^^^^^^^^^^-------
+   |                            |
+   |                            help: add `.abs()`: `(a - b).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:20:13
+   |
+LL |     let _ = f32::EPSILON > a - b;
+   |             ^^^^^^^^^^^^^^^-----
+   |                            |
+   |                            help: add `.abs()`: `(a - b).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:21:13
+   |
+LL |     let _ = f32::EPSILON > a - b.abs();
+   |             ^^^^^^^^^^^^^^^-----------
+   |                            |
+   |                            help: add `.abs()`: `(a - b.abs()).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:22:13
+   |
+LL |     let _ = f64::EPSILON > (a as f64 - b as f64);
+   |             ^^^^^^^^^^^^^^^---------------------
+   |                            |
+   |                            help: add `.abs()`: `(a as f64 - b as f64).abs()`
+
+error: float equality check without `.abs()`
+  --> $DIR/float_equality_without_abs.rs:23:13
+   |
+LL |     let _ = f32::EPSILON > 1.0 - 2.0;
+   |             ^^^^^^^^^^^^^^^---------
+   |                            |
+   |                            help: add `.abs()`: `(1.0 - 2.0).abs()`
+
+error: aborting due to 11 previous errors
+
index 334ccb0101678c6c05cbb0c7418c75c8a17489b2..6afee0f36b9da21b592e78b947cdc07140b14015 100644 (file)
@@ -1,22 +1,22 @@
-error: Matching on `Some` with `ok()` is redundant
+error: matching on `Some` with `ok()` is redundant
   --> $DIR/if_let_some_result.rs:6:5
    |
 LL |     if let Some(y) = x.parse().ok() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::if-let-some-result` implied by `-D warnings`
-help: Consider matching on `Ok(y)` and removing the call to `ok` instead
+help: consider matching on `Ok(y)` and removing the call to `ok` instead
    |
 LL |     if let Ok(y) = x.parse() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: Matching on `Some` with `ok()` is redundant
+error: matching on `Some` with `ok()` is redundant
   --> $DIR/if_let_some_result.rs:24:9
    |
 LL |         if let Some(y) = x   .   parse()   .   ok   ()    {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: Consider matching on `Ok(y)` and removing the call to `ok` instead
+help: consider matching on `Ok(y)` and removing the call to `ok` instead
    |
 LL |         if let Ok(y) = x   .   parse()       {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 78bc4d4bd20a30359e686ac4e891dec16c2108bf..53d1b86d02a962a639631ea29c6415573cb8ee51 100644 (file)
@@ -1,4 +1,4 @@
-error: Unnecessary boolean `not` operation
+error: unnecessary boolean `not` operation
   --> $DIR/if_not_else.rs:9:5
    |
 LL | /     if !bla() {
@@ -11,7 +11,7 @@ LL | |     }
    = note: `-D clippy::if-not-else` implied by `-D warnings`
    = help: remove the `!` and swap the blocks of the `if`/`else`
 
-error: Unnecessary `!=` operation
+error: unnecessary `!=` operation
   --> $DIR/if_not_else.rs:14:5
    |
 LL | /     if 4 != 5 {
index 585d32845d290af4f8e66a3b1fd9a41b13332f5a..aab688cc2d8b2b87d77ca9eb864a699a191edc3c 100644 (file)
@@ -1,4 +1,4 @@
-error: Multiple implementations of this structure
+error: multiple implementations of this structure
   --> $DIR/impl.rs:10:1
    |
 LL | / impl MyStruct {
@@ -7,7 +7,7 @@ LL | | }
    | |_^
    |
    = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings`
-note: First implementation here
+note: first implementation here
   --> $DIR/impl.rs:6:1
    |
 LL | / impl MyStruct {
@@ -15,7 +15,7 @@ LL | |     fn first() {}
 LL | | }
    | |_^
 
-error: Multiple implementations of this structure
+error: multiple implementations of this structure
   --> $DIR/impl.rs:24:5
    |
 LL | /     impl super::MyStruct {
@@ -23,7 +23,7 @@ LL | |         fn third() {}
 LL | |     }
    | |_____^
    |
-note: First implementation here
+note: first implementation here
   --> $DIR/impl.rs:6:1
    |
 LL | / impl MyStruct {
index 2eb2023b3b9ef44bbdfa0c0c7f23169f6b0de117..5bb9a606422a13fdb740663f33850c3d3181f34b 100644 (file)
@@ -1,4 +1,4 @@
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:13:5
    |
 LL | /     if u_8 > 0 {
@@ -8,7 +8,7 @@ LL | |     }
    |
    = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:20:13
    |
 LL | /             if u_8 > 0 {
@@ -16,7 +16,7 @@ LL | |                 u_8 -= 1;
 LL | |             }
    | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:34:5
    |
 LL | /     if u_16 > 0 {
@@ -24,7 +24,7 @@ LL | |         u_16 -= 1;
 LL | |     }
    | |_____^ help: try: `u_16 = u_16.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:44:5
    |
 LL | /     if u_32 != 0 {
@@ -32,7 +32,7 @@ LL | |         u_32 -= 1;
 LL | |     }
    | |_____^ help: try: `u_32 = u_32.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:65:5
    |
 LL | /     if u_64 > 0 {
@@ -40,7 +40,7 @@ LL | |         u_64 -= 1;
 LL | |     }
    | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:70:5
    |
 LL | /     if 0 < u_64 {
@@ -48,7 +48,7 @@ LL | |         u_64 -= 1;
 LL | |     }
    | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:75:5
    |
 LL | /     if 0 != u_64 {
@@ -56,7 +56,7 @@ LL | |         u_64 -= 1;
 LL | |     }
    | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:96:5
    |
 LL | /     if u_usize > 0 {
@@ -64,7 +64,7 @@ LL | |         u_usize -= 1;
 LL | |     }
    | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:108:5
    |
 LL | /     if i_8 > i8::MIN {
@@ -72,7 +72,7 @@ LL | |         i_8 -= 1;
 LL | |     }
    | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:113:5
    |
 LL | /     if i_8 > i8::MIN {
@@ -80,7 +80,7 @@ LL | |         i_8 -= 1;
 LL | |     }
    | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:118:5
    |
 LL | /     if i_8 != i8::MIN {
@@ -88,7 +88,7 @@ LL | |         i_8 -= 1;
 LL | |     }
    | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:123:5
    |
 LL | /     if i_8 != i8::MIN {
@@ -96,7 +96,7 @@ LL | |         i_8 -= 1;
 LL | |     }
    | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:133:5
    |
 LL | /     if i_16 > i16::MIN {
@@ -104,7 +104,7 @@ LL | |         i_16 -= 1;
 LL | |     }
    | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:138:5
    |
 LL | /     if i_16 > i16::MIN {
@@ -112,7 +112,7 @@ LL | |         i_16 -= 1;
 LL | |     }
    | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:143:5
    |
 LL | /     if i_16 != i16::MIN {
@@ -120,7 +120,7 @@ LL | |         i_16 -= 1;
 LL | |     }
    | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:148:5
    |
 LL | /     if i_16 != i16::MIN {
@@ -128,7 +128,7 @@ LL | |         i_16 -= 1;
 LL | |     }
    | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:158:5
    |
 LL | /     if i_32 > i32::MIN {
@@ -136,7 +136,7 @@ LL | |         i_32 -= 1;
 LL | |     }
    | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:163:5
    |
 LL | /     if i_32 > i32::MIN {
@@ -144,7 +144,7 @@ LL | |         i_32 -= 1;
 LL | |     }
    | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:168:5
    |
 LL | /     if i_32 != i32::MIN {
@@ -152,7 +152,7 @@ LL | |         i_32 -= 1;
 LL | |     }
    | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:173:5
    |
 LL | /     if i_32 != i32::MIN {
@@ -160,7 +160,7 @@ LL | |         i_32 -= 1;
 LL | |     }
    | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:183:5
    |
 LL | /     if i64::MIN < i_64 {
@@ -168,7 +168,7 @@ LL | |         i_64 -= 1;
 LL | |     }
    | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:188:5
    |
 LL | /     if i64::MIN != i_64 {
@@ -176,7 +176,7 @@ LL | |         i_64 -= 1;
 LL | |     }
    | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
 
-error: Implicitly performing saturating subtraction
+error: implicitly performing saturating subtraction
   --> $DIR/implicit_saturating_sub.rs:193:5
    |
 LL | /     if i64::MIN < i_64 {
index 29a6914761c9f92c319918bbee76403312ad2724..c5b020ba8ced51bce578f1337fe5ac52c7801662 100644 (file)
@@ -1,4 +1,4 @@
-error: Unnecessary `>= y + 1` or `x - 1 >=`
+error: unnecessary `>= y + 1` or `x - 1 >=`
   --> $DIR/int_plus_one.rs:9:13
    |
 LL |     let _ = x >= y + 1;
@@ -6,19 +6,19 @@ LL |     let _ = x >= y + 1;
    |
    = note: `-D clippy::int-plus-one` implied by `-D warnings`
 
-error: Unnecessary `>= y + 1` or `x - 1 >=`
+error: unnecessary `>= y + 1` or `x - 1 >=`
   --> $DIR/int_plus_one.rs:10:13
    |
 LL |     let _ = y + 1 <= x;
    |             ^^^^^^^^^^ help: change it to: `y < x`
 
-error: Unnecessary `>= y + 1` or `x - 1 >=`
+error: unnecessary `>= y + 1` or `x - 1 >=`
   --> $DIR/int_plus_one.rs:12:13
    |
 LL |     let _ = x - 1 >= y;
    |             ^^^^^^^^^^ help: change it to: `x > y`
 
-error: Unnecessary `>= y + 1` or `x - 1 >=`
+error: unnecessary `>= y + 1` or `x - 1 >=`
   --> $DIR/int_plus_one.rs:13:13
    |
 LL |     let _ = y <= x - 1;
index bbf61df0cda68523debcdea9309072eeb61a73e6..8c10a252ee01b427ead6cc1b2c2432a505003e49 100644 (file)
@@ -1,4 +1,4 @@
-error: Using `.iter().next()` on an array
+error: using `.iter().next()` on an array
   --> $DIR/iter_next_slice.rs:9:5
    |
 LL |     s.iter().next();
@@ -6,19 +6,19 @@ LL |     s.iter().next();
    |
    = note: `-D clippy::iter-next-slice` implied by `-D warnings`
 
-error: Using `.iter().next()` on a Slice without end index.
+error: using `.iter().next()` on a Slice without end index
   --> $DIR/iter_next_slice.rs:12:5
    |
 LL |     s[2..].iter().next();
    |     ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)`
 
-error: Using `.iter().next()` on a Slice without end index.
+error: using `.iter().next()` on a Slice without end index
   --> $DIR/iter_next_slice.rs:15:5
    |
 LL |     v[5..].iter().next();
    |     ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)`
 
-error: Using `.iter().next()` on an array
+error: using `.iter().next()` on an array
   --> $DIR/iter_next_slice.rs:18:5
    |
 LL |     v.iter().next();
index d81676a3d9f4801d06f1884e1473c8730bd7597f..1f3b8ac99b19146f766e6758d0cd799d37dc92c0 100644 (file)
@@ -141,11 +141,3 @@ fn main() {
 fn test_slice(b: &[u8]) {
     if !b.is_empty() {}
 }
-
-mod issue_3807 {
-    // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`.
-    // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965
-    fn no_suggestion() {
-        let _ = (0..42).len() == 0;
-    }
-}
index ecdba921a5d0fde97fa67b5a3a455d953d3cdd38..dc21de0001b6c76eb2f57cdba4e7416319d5b756 100644 (file)
@@ -141,11 +141,3 @@ fn main() {
 fn test_slice(b: &[u8]) {
     if b.len() != 0 {}
 }
-
-mod issue_3807 {
-    // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`.
-    // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965
-    fn no_suggestion() {
-        let _ = (0..42).len() == 0;
-    }
-}
index 7da26f8ff4d477b9ab0cde400eb834b44ee24cde..7978176624274ab7626f9c45be5d383daf7cde36 100644 (file)
@@ -1,14 +1,17 @@
 // run-rustfix
 
-#![feature(range_is_empty)]
 #![warn(clippy::len_zero)]
 #![allow(unused)]
 
+// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this
 mod issue_3807 {
-    // With the feature enabled, `is_empty` should be suggested
-    fn suggestion_is_fine() {
+    fn suggestion_is_fine_range() {
         let _ = (0..42).is_empty();
     }
+
+    fn suggestion_is_fine_range_inclusive() {
+        let _ = (0_u8..=42).is_empty();
+    }
 }
 
 fn main() {}
index be7b4244bc06c4d27463f35de2181ca23ab2c20a..a0eb51cc9760c462372dddaaa31819db94541efe 100644 (file)
@@ -1,14 +1,17 @@
 // run-rustfix
 
-#![feature(range_is_empty)]
 #![warn(clippy::len_zero)]
 #![allow(unused)]
 
+// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this
 mod issue_3807 {
-    // With the feature enabled, `is_empty` should be suggested
-    fn suggestion_is_fine() {
+    fn suggestion_is_fine_range() {
         let _ = (0..42).len() == 0;
     }
+
+    fn suggestion_is_fine_range_inclusive() {
+        let _ = (0_u8..=42).len() == 0;
+    }
 }
 
 fn main() {}
index 6e5fa41fb08a55c127f9bb7a8e34624498e018d5..d0defb5a79edcb582ca4b5711ce40ee3a4af0e9f 100644 (file)
@@ -1,10 +1,16 @@
 error: length comparison to zero
-  --> $DIR/len_zero_ranges.rs:10:17
+  --> $DIR/len_zero_ranges.rs:9:17
    |
 LL |         let _ = (0..42).len() == 0;
    |                 ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()`
    |
    = note: `-D clippy::len-zero` implied by `-D warnings`
 
-error: aborting due to previous error
+error: length comparison to zero
+  --> $DIR/len_zero_ranges.rs:13:17
+   |
+LL |         let _ = (0_u8..=42).len() == 0;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0_u8..=42).is_empty()`
+
+error: aborting due to 2 previous errors
 
index 09614b8c1ad787d98975f9c800279a1817a7d3b3..73e550b3df891864fac7c2cd2a46920efbf890f9 100644 (file)
@@ -135,4 +135,25 @@ fn test() -> i32 {
     }
 }
 
+mod issue_5729 {
+    use std::sync::Arc;
+
+    trait Foo {}
+
+    trait FooStorage {
+        fn foo_cloned(&self) -> Arc<dyn Foo>;
+    }
+
+    struct FooStorageImpl<T: Foo> {
+        foo: Arc<T>,
+    }
+
+    impl<T: Foo + 'static> FooStorage for FooStorageImpl<T> {
+        fn foo_cloned(&self) -> Arc<dyn Foo> {
+            let clone = Arc::clone(&self.foo);
+            clone
+        }
+    }
+}
+
 fn main() {}
index eacf948b3927a5ee28cd67b3756941d811868764..fe878e5f20601ff68d80908aaf95711f52463d9e 100644 (file)
@@ -27,5 +27,19 @@ LL |
 LL |         5
    |
 
-error: aborting due to 2 previous errors
+error: returning the result of a `let` binding from a block
+  --> $DIR/let_and_return.rs:154:13
+   |
+LL |             let clone = Arc::clone(&self.foo);
+   |             ---------------------------------- unnecessary `let` binding
+LL |             clone
+   |             ^^^^^
+   |
+help: return the expression directly
+   |
+LL |             
+LL |             Arc::clone(&self.foo) as _
+   |
+
+error: aborting due to 3 previous errors
 
index 9eec6928e8cee331d0b71086069eb322007877e1..4f43cff50244449b93b23b76db601285132eab85 100644 (file)
@@ -1,40 +1,40 @@
-error: You are using an explicit closure for copying elements
+error: you are using an explicit closure for copying elements
   --> $DIR/map_clone.rs:10:22
    |
 LL |     let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()`
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()`
    |
    = note: `-D clippy::map-clone` implied by `-D warnings`
 
-error: You are using an explicit closure for cloning elements
+error: you are using an explicit closure for cloning elements
   --> $DIR/map_clone.rs:11:26
    |
 LL |     let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
 
-error: You are using an explicit closure for copying elements
+error: you are using an explicit closure for copying elements
   --> $DIR/map_clone.rs:12:23
    |
 LL |     let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
-   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()`
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()`
 
-error: You are using an explicit closure for copying elements
+error: you are using an explicit closure for copying elements
   --> $DIR/map_clone.rs:14:26
    |
 LL |     let _: Option<u64> = Some(&16).map(|b| *b);
-   |                          ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()`
+   |                          ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()`
 
-error: You are using an explicit closure for copying elements
+error: you are using an explicit closure for copying elements
   --> $DIR/map_clone.rs:15:25
    |
 LL |     let _: Option<u8> = Some(&1).map(|x| x.clone());
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()`
 
-error: You are needlessly cloning iterator elements
+error: you are needlessly cloning iterator elements
   --> $DIR/map_clone.rs:26:29
    |
 LL |     let _ = std::env::args().map(|v| v.clone());
-   |                             ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call
+   |                             ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call
 
 error: aborting due to 6 previous errors
 
index 7880cf36415ff87acc8ba620ceb7712ec8038e35..80dd2f744b3a1137c06f513a4329afee18f9de9f 100644 (file)
@@ -10,6 +10,7 @@
     clippy::non_ascii_literal,
     clippy::new_without_default,
     clippy::needless_pass_by_value,
+    clippy::needless_lifetimes,
     clippy::print_stdout,
     clippy::must_use_candidate,
     clippy::use_self,
 
 use option_helpers::IteratorFalsePositives;
 
-pub struct T;
-
-impl T {
-    pub fn add(self, other: T) -> T {
-        self
-    }
-
-    // no error, not public interface
-    pub(crate) fn drop(&mut self) {}
-
-    // no error, private function
-    fn neg(self) -> Self {
-        self
-    }
-
-    // no error, private function
-    fn eq(&self, other: T) -> bool {
-        true
-    }
-
-    // No error; self is a ref.
-    fn sub(&self, other: T) -> &T {
-        self
-    }
-
-    // No error; different number of arguments.
-    fn div(self) -> T {
-        self
-    }
-
-    // No error; wrong return type.
-    fn rem(self, other: T) {}
-
-    // Fine
-    fn into_u32(self) -> u32 {
-        0
-    }
-
-    fn into_u16(&self) -> u16 {
-        0
-    }
-
-    fn to_something(self) -> u32 {
-        0
-    }
-
-    fn new(self) -> Self {
-        unimplemented!();
-    }
-}
-
-pub struct T1;
-
-impl T1 {
-    // Shouldn't trigger lint as it is unsafe.
-    pub unsafe fn add(self, rhs: T1) -> T1 {
-        self
-    }
-
-    // Should not trigger lint since this is an async function.
-    pub async fn next(&mut self) -> Option<T1> {
-        None
-    }
-}
-
 struct Lt<'a> {
     foo: &'a u32,
 }
@@ -171,6 +107,8 @@ fn new() -> i32 {
     }
 }
 
+struct T;
+
 impl Mul<T> for T {
     type Output = T;
     // No error, obviously.
index 01cf487ac148e77f519563bac11d9a1da659e858..2a0a43e83a653089158ed99ac5e80d4d729ad8f7 100644 (file)
@@ -1,15 +1,5 @@
-error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name
-  --> $DIR/methods.rs:39:5
-   |
-LL | /     pub fn add(self, other: T) -> T {
-LL | |         self
-LL | |     }
-   | |_____^
-   |
-   = note: `-D clippy::should-implement-trait` implied by `-D warnings`
-
 error: methods called `new` usually return `Self`
-  --> $DIR/methods.rs:169:5
+  --> $DIR/methods.rs:105:5
    |
 LL | /     fn new() -> i32 {
 LL | |         0
@@ -19,7 +9,7 @@ LL | |     }
    = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
 
 error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
-  --> $DIR/methods.rs:188:13
+  --> $DIR/methods.rs:126:13
    |
 LL |     let _ = v.iter().filter(|&x| *x < 0).next();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -28,7 +18,7 @@ LL |     let _ = v.iter().filter(|&x| *x < 0).next();
    = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)`
 
 error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
-  --> $DIR/methods.rs:191:13
+  --> $DIR/methods.rs:129:13
    |
 LL |       let _ = v.iter().filter(|&x| {
    |  _____________^
@@ -38,7 +28,7 @@ LL | |                    ).next();
    | |___________________________^
 
 error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:208:22
+  --> $DIR/methods.rs:146:22
    |
 LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)`
@@ -46,25 +36,25 @@ LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
    = note: `-D clippy::search-is-some` implied by `-D warnings`
 
 error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:209:20
+  --> $DIR/methods.rs:147:20
    |
 LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)`
 
 error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:210:20
+  --> $DIR/methods.rs:148:20
    |
 LL |     let _ = (0..1).find(|x| *x == 0).is_some();
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)`
 
 error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:211:22
+  --> $DIR/methods.rs:149:22
    |
 LL |     let _ = v.iter().find(|x| **x == 0).is_some();
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)`
 
 error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:214:13
+  --> $DIR/methods.rs:152:13
    |
 LL |       let _ = v.iter().find(|&x| {
    |  _____________^
@@ -74,13 +64,13 @@ LL | |                    ).is_some();
    | |______________________________^
 
 error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:220:22
+  --> $DIR/methods.rs:158:22
    |
 LL |     let _ = v.iter().position(|&x| x < 0).is_some();
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
 
 error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:223:13
+  --> $DIR/methods.rs:161:13
    |
 LL |       let _ = v.iter().position(|&x| {
    |  _____________^
@@ -90,13 +80,13 @@ LL | |                    ).is_some();
    | |______________________________^
 
 error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:229:22
+  --> $DIR/methods.rs:167:22
    |
 LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
 
 error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
-  --> $DIR/methods.rs:232:13
+  --> $DIR/methods.rs:170:13
    |
 LL |       let _ = v.iter().rposition(|&x| {
    |  _____________^
@@ -105,5 +95,5 @@ LL | |                                }
 LL | |                    ).is_some();
    | |______________________________^
 
-error: aborting due to 13 previous errors
+error: aborting due to 12 previous errors
 
index fa8c82ae0f34022fbc212f7ed8e60622e7bf74d5..062d30b262c1be2dc62988f12e535e70972afa7b 100644 (file)
@@ -1,4 +1,4 @@
-error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference
+error: the function `takes_an_immutable_reference` doesn't need a mutable reference
   --> $DIR/mut_reference.rs:17:34
    |
 LL |     takes_an_immutable_reference(&mut 42);
@@ -6,13 +6,13 @@ LL |     takes_an_immutable_reference(&mut 42);
    |
    = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
 
-error: The function/method `as_ptr` doesn't need a mutable reference
+error: the function `as_ptr` doesn't need a mutable reference
   --> $DIR/mut_reference.rs:19:12
    |
 LL |     as_ptr(&mut 42);
    |            ^^^^^^^
 
-error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference
+error: the method `takes_an_immutable_reference` doesn't need a mutable reference
   --> $DIR/mut_reference.rs:23:44
    |
 LL |     my_struct.takes_an_immutable_reference(&mut 42);
index 7dac086585548431a9c8b0cbd66a6376fecd504c..a3511ba708a885f808e6f4afd7d257149951c891 100644 (file)
@@ -1,4 +1,4 @@
-error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
+error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
   --> $DIR/mutex_atomic.rs:6:5
    |
 LL |     Mutex::new(true);
@@ -6,31 +6,31 @@ LL |     Mutex::new(true);
    |
    = note: `-D clippy::mutex-atomic` implied by `-D warnings`
 
-error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
+error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
   --> $DIR/mutex_atomic.rs:7:5
    |
 LL |     Mutex::new(5usize);
    |     ^^^^^^^^^^^^^^^^^^
 
-error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
+error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
   --> $DIR/mutex_atomic.rs:8:5
    |
 LL |     Mutex::new(9isize);
    |     ^^^^^^^^^^^^^^^^^^
 
-error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
+error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
   --> $DIR/mutex_atomic.rs:10:5
    |
 LL |     Mutex::new(&x as *const u32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
+error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
   --> $DIR/mutex_atomic.rs:11:5
    |
 LL |     Mutex::new(&mut x as *mut u32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
+error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
   --> $DIR/mutex_atomic.rs:12:5
    |
 LL |     Mutex::new(0u32);
@@ -38,7 +38,7 @@ LL |     Mutex::new(0u32);
    |
    = note: `-D clippy::mutex-integer` implied by `-D warnings`
 
-error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
+error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
   --> $DIR/mutex_atomic.rs:13:5
    |
 LL |     Mutex::new(0i32);
index 682d7b3c4ceb4f64b0f559f37138193ce3034da6..883683e08a2aa83b4356d0a31bef4a7983cdf570 100644 (file)
@@ -9,8 +9,14 @@
 /// }
 /// ```
 ///
-/// This should, too.
+/// With an explicit return type it should lint too
+/// ```
+/// fn main() -> () {
+///     unimplemented!();
+/// }
+/// ```
 ///
+/// This should, too.
 /// ```rust
 /// fn main() {
 ///     unimplemented!();
@@ -18,7 +24,6 @@
 /// ```
 ///
 /// This one too.
-///
 /// ```no_run
 /// fn main() {
 ///     unimplemented!();
@@ -33,6 +38,20 @@ fn bad_doctests() {}
 /// fn main(){}
 /// ```
 ///
+/// This shouldn't lint either, because main is async:
+/// ```
+/// async fn main() {
+///     assert_eq!(42, ANSWER);
+/// }
+/// ```
+///
+/// Same here, because the return type is not the unit type:
+/// ```
+/// fn main() -> Result<()> {
+///     Ok(())
+/// }
+/// ```
+///
 /// This shouldn't lint either, because there's a `static`:
 /// ```
 /// static ANSWER: i32 = 42;
@@ -42,6 +61,15 @@ fn bad_doctests() {}
 /// }
 /// ```
 ///
+/// This shouldn't lint either, because there's a `const`:
+/// ```
+/// fn main() {
+///     assert_eq!(42, ANSWER);
+/// }
+///
+/// const ANSWER: i32 = 42;
+/// ```
+///
 /// Neither should this lint because of `extern crate`:
 /// ```
 /// #![feature(test)]
@@ -51,8 +79,41 @@ fn bad_doctests() {}
 /// }
 /// ```
 ///
-/// We should not lint ignored examples:
+/// Neither should this lint because it has an extern block:
+/// ```
+/// extern {}
+/// fn main() {
+///     unimplemented!();
+/// }
+/// ```
+///
+/// This should not lint because there is another function defined:
+/// ```
+/// fn fun() {}
+///
+/// fn main() {
+///     unimplemented!();
+/// }
+/// ```
 ///
+/// We should not lint inside raw strings ...
+/// ```
+/// let string = r#"
+/// fn main() {
+///     unimplemented!();
+/// }
+/// "#;
+/// ```
+///
+/// ... or comments
+/// ```
+/// // fn main() {
+/// //     let _inception = 42;
+/// // }
+/// let _inception = 42;
+/// ```
+///
+/// We should not lint ignored examples:
 /// ```rust,ignore
 /// fn main() {
 ///     unimplemented!();
@@ -60,7 +121,6 @@ fn bad_doctests() {}
 /// ```
 ///
 /// Or even non-rust examples:
-///
 /// ```text
 /// fn main() {
 ///     is what starts the program
index 65d40ee6832f289d0130bf1d992b3f7b9f470d05..05c7f9d33a7924f3d1ea642b0ad665f7edbf06d7 100644 (file)
@@ -7,16 +7,22 @@ LL | /// fn main() {
    = note: `-D clippy::needless-doctest-main` implied by `-D warnings`
 
 error: needless `fn main` in doctest
-  --> $DIR/needless_doc_main.rs:15:4
+  --> $DIR/needless_doc_main.rs:14:4
+   |
+LL | /// fn main() -> () {
+   |    ^^^^^^^^^^^^^^^^^^
+
+error: needless `fn main` in doctest
+  --> $DIR/needless_doc_main.rs:21:4
    |
 LL | /// fn main() {
    |    ^^^^^^^^^^^^
 
 error: needless `fn main` in doctest
-  --> $DIR/needless_doc_main.rs:23:4
+  --> $DIR/needless_doc_main.rs:28:4
    |
 LL | /// fn main() {
    |    ^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
index ad20e2381073aae324d7f90474f89befe77aa24b..d849e093da7bb821a765122d0d966ba78adf4a39 100644 (file)
@@ -69,6 +69,23 @@ fn test_void_match(x: u32) {
     }
 }
 
+fn read_line() -> String {
+    use std::io::BufRead;
+    let stdin = ::std::io::stdin();
+    return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+    if value {
+        use std::io::BufRead;
+        let stdin = ::std::io::stdin();
+        let _a = stdin.lock().lines().next().unwrap().unwrap();
+        String::from("test")
+    } else {
+        String::new()
+    }
+}
+
 fn main() {
     let _ = test_end_of_fn();
     let _ = test_no_semicolon();
index af0cdfb207ff568f9e0cbc29225437196be17868..29f2bd1852af00e9f1df68916f1f7624046e4c93 100644 (file)
@@ -69,6 +69,23 @@ fn test_void_match(x: u32) {
     }
 }
 
+fn read_line() -> String {
+    use std::io::BufRead;
+    let stdin = ::std::io::stdin();
+    return stdin.lock().lines().next().unwrap().unwrap();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+    if value {
+        use std::io::BufRead;
+        let stdin = ::std::io::stdin();
+        let _a = stdin.lock().lines().next().unwrap().unwrap();
+        return String::from("test");
+    } else {
+        return String::new();
+    }
+}
+
 fn main() {
     let _ = test_end_of_fn();
     let _ = test_no_semicolon();
index c34eecbcbb639bc906ff845e5332e790cf3364ee..f73c833a801f3dc143bb92545a5e7ad41b566916 100644 (file)
@@ -72,5 +72,17 @@ error: unneeded `return` statement
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with an empty block: `{}`
 
-error: aborting due to 12 previous errors
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:83:9
+   |
+LL |         return String::from("test");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:85:9
+   |
+LL |         return String::new();
+   |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: aborting due to 14 previous errors
 
index 2c2d1e275893fae41e3965c9c4568f2afad8c6e8..e82873629a54b0de91ad1068fff7d552001fe81b 100644 (file)
@@ -137,9 +137,9 @@ pub fn new() -> *mut Self {
     }
 }
 
-struct MutPointerReturnerOk2;
+struct ConstPointerReturnerOk2;
 
-impl MutPointerReturnerOk2 {
+impl ConstPointerReturnerOk2 {
     // should not trigger lint
     pub fn new() -> *const Self {
         unimplemented!();
@@ -210,3 +210,133 @@ pub fn new<'b: 'a>(s: &'b str) -> WithLifetime<'b> {
         unimplemented!();
     }
 }
+
+mod issue5435 {
+    struct V;
+
+    pub trait TraitRetSelf {
+        // should not trigger lint
+        fn new() -> Self;
+    }
+
+    pub trait TraitRet {
+        // should trigger lint as we are in trait definition
+        fn new() -> String;
+    }
+    pub struct StructRet;
+    impl TraitRet for StructRet {
+        // should not trigger lint as we are in the impl block
+        fn new() -> String {
+            unimplemented!();
+        }
+    }
+
+    pub trait TraitRet2 {
+        // should trigger lint
+        fn new(_: String) -> String;
+    }
+
+    trait TupleReturnerOk {
+        // should not trigger lint
+        fn new() -> (Self, u32)
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+
+    trait TupleReturnerOk2 {
+        // should not trigger lint (it doesn't matter which element in the tuple is Self)
+        fn new() -> (u32, Self)
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+
+    trait TupleReturnerOk3 {
+        // should not trigger lint (tuple can contain multiple Self)
+        fn new() -> (Self, Self)
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+
+    trait TupleReturnerBad {
+        // should trigger lint
+        fn new() -> (u32, u32) {
+            unimplemented!();
+        }
+    }
+
+    trait MutPointerReturnerOk {
+        // should not trigger lint
+        fn new() -> *mut Self
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+
+    trait ConstPointerReturnerOk2 {
+        // should not trigger lint
+        fn new() -> *const Self
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+
+    trait MutPointerReturnerBad {
+        // should trigger lint
+        fn new() -> *mut V {
+            unimplemented!();
+        }
+    }
+
+    trait GenericReturnerOk {
+        // should not trigger lint
+        fn new() -> Option<Self>
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+
+    trait NestedReturnerOk {
+        // should not trigger lint
+        fn new() -> (Option<Self>, u32)
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+
+    trait NestedReturnerOk2 {
+        // should not trigger lint
+        fn new() -> ((Self, u32), u32)
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+
+    trait NestedReturnerOk3 {
+        // should not trigger lint
+        fn new() -> Option<(Self, u32)>
+        where
+            Self: Sized,
+        {
+            unimplemented!();
+        }
+    }
+}
index dd5a24bcbe7aed4c1ba4b792942ecac8f8cf9ea2..8217bc6187f93aa5cfce1ddbc5ce7ed4788b6173 100644 (file)
@@ -48,5 +48,33 @@ LL | |         unimplemented!();
 LL | |     }
    | |_____^
 
-error: aborting due to 6 previous errors
+error: methods called `new` usually return `Self`
+  --> $DIR/new_ret_no_self.rs:224:9
+   |
+LL |         fn new() -> String;
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: methods called `new` usually return `Self`
+  --> $DIR/new_ret_no_self.rs:236:9
+   |
+LL |         fn new(_: String) -> String;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: methods called `new` usually return `Self`
+  --> $DIR/new_ret_no_self.rs:271:9
+   |
+LL | /         fn new() -> (u32, u32) {
+LL | |             unimplemented!();
+LL | |         }
+   | |_________^
+
+error: methods called `new` usually return `Self`
+  --> $DIR/new_ret_no_self.rs:298:9
+   |
+LL | /         fn new() -> *mut V {
+LL | |             unimplemented!();
+LL | |         }
+   | |_________^
+
+error: aborting due to 10 previous errors
 
index 076692e6445175da398e3ae788c803c5b57ba901..07d7f0b45b0c2f2171cd29c29a4360deb7cff1ed 100644 (file)
@@ -38,4 +38,7 @@ fn main() {
 
     let _ = opt.as_deref();
     let _ = opt.as_deref_mut();
+
+    // Issue #5927
+    let _ = opt.as_deref();
 }
index 3bf5f715f8339525b8ad547d75eea77a96b0c1f2..6ae059c9425d35480c7afcc00ab7e6b63b0db3cf 100644 (file)
@@ -41,4 +41,7 @@ fn main() {
 
     let _ = opt.as_ref().map(|x| &**x);
     let _ = opt.as_mut().map(|x| &mut **x);
+
+    // Issue #5927
+    let _ = opt.as_ref().map(std::ops::Deref::deref);
 }
index a106582a633232b4bfcdc95c78308440d2480137..62f28232475282a1a51ade866365560ec6031386 100644 (file)
@@ -100,5 +100,11 @@ error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done
 LL |     let _ = opt.as_mut().map(|x| &mut **x);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
-error: aborting due to 16 previous errors
+error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
+  --> $DIR/option_as_ref_deref.rs:46:13
+   |
+LL |     let _ = opt.as_ref().map(std::ops::Deref::deref);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
+
+error: aborting due to 17 previous errors
 
index 4d284ae1319d321a4d867e91f10e71ae317bacd9..163bd044c178ee37fdcbc91763aec383e78b69bd 100644 (file)
@@ -48,6 +48,14 @@ fn main() {
     let _ = -1f64.to_degrees();
     let _ = -1f64.to_radians();
 
+    // Chains containing any non-odd function should trigger (issue #5924)
+    let _ = -(1.0_f64.cos().cos());
+    let _ = -(1.0_f64.cos().sin());
+    let _ = -(1.0_f64.sin().cos());
+
+    // Chains of odd functions shouldn't trigger
+    let _ = -1f64.sin().sin();
+
     let b = 3;
     trip!(b * 8);
 }
index 2d08e82f349ade91eb20d8fa8adb3f040a6d2908..8c849e3209b088909668d26df45246472be9defc 100644 (file)
@@ -48,6 +48,14 @@ fn main() {
     let _ = -1f64.to_degrees();
     let _ = -1f64.to_radians();
 
+    // Chains containing any non-odd function should trigger (issue #5924)
+    let _ = -1.0_f64.cos().cos();
+    let _ = -1.0_f64.cos().sin();
+    let _ = -1.0_f64.sin().cos();
+
+    // Chains of odd functions shouldn't trigger
+    let _ = -1f64.sin().sin();
+
     let b = 3;
     trip!(b * 8);
 }
index a2ed5392bfc7c643593dbf24434ab8c05d1d3c93..03d585b39750a6cc3932fdf8c539063c76e28b1d 100644 (file)
@@ -54,5 +54,23 @@ error: unary minus has lower precedence than method call
 LL |     -1f32.abs();
    |     ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())`
 
-error: aborting due to 9 previous errors
+error: unary minus has lower precedence than method call
+  --> $DIR/precedence.rs:52:13
+   |
+LL |     let _ = -1.0_f64.cos().cos();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())`
+
+error: unary minus has lower precedence than method call
+  --> $DIR/precedence.rs:53:13
+   |
+LL |     let _ = -1.0_f64.cos().sin();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())`
+
+error: unary minus has lower precedence than method call
+  --> $DIR/precedence.rs:54:13
+   |
+LL |     let _ = -1.0_f64.sin().cos();
+   |             ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())`
+
+error: aborting due to 12 previous errors
 
index 266358334587d0a6edb4a94e328e77d1a6515c93..6514fd6d1ac76bbe7ebbbd37453af5002e3c3308 100644 (file)
@@ -33,7 +33,7 @@ pub fn test5(a: Rc<bool>) {}
 
 // Rc<Box<T>>
 
-pub fn test6(a: Box<bool>) {}
+pub fn test6(a: Rc<bool>) {}
 
 // Box<&T>
 
index eaa57ce3024b604243166b7e78bcf1d58dc6f536..92e4f67f5db6e40e6f2f0f9bd8ed422b3ec32c0c 100644 (file)
@@ -28,7 +28,7 @@ error: usage of `Rc<Box<T>>`
   --> $DIR/redundant_allocation.rs:36:17
    |
 LL | pub fn test6(a: Rc<Box<bool>>) {}
-   |                 ^^^^^^^^^^^^^ help: try: `Box<bool>`
+   |                 ^^^^^^^^^^^^^ help: try: `Rc<bool>`
 
 error: usage of `Box<&T>`
   --> $DIR/redundant_allocation.rs:40:22
index 79f276634619e277c24cef3069afe85953948210..2735e41738f0d0ef4555759f74260fa5e82bca38 100644 (file)
@@ -1,4 +1,4 @@
-error: try not to call a closure in the expression where it is declared.
+error: try not to call a closure in the expression where it is declared
   --> $DIR/redundant_closure_call_early.rs:9:17
    |
 LL |     let mut k = (|m| m + 1)(i);
@@ -6,7 +6,7 @@ LL |     let mut k = (|m| m + 1)(i);
    |
    = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 
-error: try not to call a closure in the expression where it is declared.
+error: try not to call a closure in the expression where it is declared
   --> $DIR/redundant_closure_call_early.rs:12:9
    |
 LL |     k = (|a, b| a * b)(1, 5);
index 644161d9f5d885eb24da0c187bfcd5e63ffa1352..afd704ef12a934f913454334a42bd68c5a95a954 100644 (file)
@@ -1,4 +1,4 @@
-error: try not to call a closure in the expression where it is declared.
+error: try not to call a closure in the expression where it is declared
   --> $DIR/redundant_closure_call_fixable.rs:7:13
    |
 LL |     let a = (|| 42)();
index e29a1dce0c7e8f040f7dd3f262e505fbf4d21d9a..1f4864b72895bf10142574e214bdfe352eba896a 100644 (file)
@@ -24,4 +24,16 @@ fn main() {
     let shadowed_closure = || 2;
     i = shadowed_closure();
     i = shadowed_closure();
+
+    // Fix FP in #5916
+    let mut x;
+    let create = || 2 * 2;
+    x = create();
+    fun(move || {
+        x = create();
+    })
+}
+
+fn fun<T: 'static + FnMut()>(mut f: T) {
+    f();
 }
index ff1088f86f6470cc2c41256fdd8746d3161bb23b..bfe27e020445c77b83c4ddfb14f773eb4ee36b85 100644 (file)
@@ -86,4 +86,12 @@ struct A {
     for a in vec_a {
         vec12.push(2u8.pow(a.kind));
     }
+
+    // Fix #5902
+    let mut vec13: Vec<u8> = Vec::new();
+    let mut item = 0;
+    for _ in 0..10 {
+        vec13.push(item);
+        item += 10;
+    }
 }
diff --git a/src/tools/clippy/tests/ui/self_assignment.rs b/src/tools/clippy/tests/ui/self_assignment.rs
new file mode 100644 (file)
index 0000000..a7cbb9c
--- /dev/null
@@ -0,0 +1,67 @@
+#![warn(clippy::self_assignment)]
+
+pub struct S<'a> {
+    a: i32,
+    b: [i32; 10],
+    c: Vec<Vec<i32>>,
+    e: &'a mut i32,
+    f: &'a mut i32,
+}
+
+pub fn positives(mut a: usize, b: &mut u32, mut s: S) {
+    a = a;
+    *b = *b;
+    s = s;
+    s.a = s.a;
+    s.b[10] = s.b[5 + 5];
+    s.c[0][1] = s.c[0][1];
+    s.b[a] = s.b[a];
+    *s.e = *s.e;
+    s.b[a + 10] = s.b[10 + a];
+
+    let mut t = (0, 1);
+    t.1 = t.1;
+    t.0 = (t.0);
+}
+
+pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) {
+    dbg!(&a);
+    a = *b;
+    dbg!(&a);
+    s.b[1] += s.b[1];
+    s.b[1] = s.b[2];
+    s.c[1][0] = s.c[0][1];
+    s.b[a] = s.b[*b];
+    s.b[a + 10] = s.b[a + 11];
+    *s.e = *s.f;
+
+    let mut t = (0, 1);
+    t.0 = t.1;
+}
+
+#[allow(clippy::eval_order_dependence)]
+pub fn negatives_side_effects() {
+    let mut v = vec![1, 2, 3, 4, 5];
+    let mut i = 0;
+    v[{
+        i += 1;
+        i
+    }] = v[{
+        i += 1;
+        i
+    }];
+
+    fn next(n: &mut usize) -> usize {
+        let v = *n;
+        *n += 1;
+        v
+    }
+
+    let mut w = vec![1, 2, 3, 4, 5];
+    let mut i = 0;
+    let i = &mut i;
+    w[next(i)] = w[next(i)];
+    w[next(i)] = w[next(i)];
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/self_assignment.stderr b/src/tools/clippy/tests/ui/self_assignment.stderr
new file mode 100644 (file)
index 0000000..826e0d0
--- /dev/null
@@ -0,0 +1,70 @@
+error: self-assignment of `a` to `a`
+  --> $DIR/self_assignment.rs:12:5
+   |
+LL |     a = a;
+   |     ^^^^^
+   |
+   = note: `-D clippy::self-assignment` implied by `-D warnings`
+
+error: self-assignment of `*b` to `*b`
+  --> $DIR/self_assignment.rs:13:5
+   |
+LL |     *b = *b;
+   |     ^^^^^^^
+
+error: self-assignment of `s` to `s`
+  --> $DIR/self_assignment.rs:14:5
+   |
+LL |     s = s;
+   |     ^^^^^
+
+error: self-assignment of `s.a` to `s.a`
+  --> $DIR/self_assignment.rs:15:5
+   |
+LL |     s.a = s.a;
+   |     ^^^^^^^^^
+
+error: self-assignment of `s.b[5 + 5]` to `s.b[10]`
+  --> $DIR/self_assignment.rs:16:5
+   |
+LL |     s.b[10] = s.b[5 + 5];
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: self-assignment of `s.c[0][1]` to `s.c[0][1]`
+  --> $DIR/self_assignment.rs:17:5
+   |
+LL |     s.c[0][1] = s.c[0][1];
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+error: self-assignment of `s.b[a]` to `s.b[a]`
+  --> $DIR/self_assignment.rs:18:5
+   |
+LL |     s.b[a] = s.b[a];
+   |     ^^^^^^^^^^^^^^^
+
+error: self-assignment of `*s.e` to `*s.e`
+  --> $DIR/self_assignment.rs:19:5
+   |
+LL |     *s.e = *s.e;
+   |     ^^^^^^^^^^^
+
+error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]`
+  --> $DIR/self_assignment.rs:20:5
+   |
+LL |     s.b[a + 10] = s.b[10 + a];
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: self-assignment of `t.1` to `t.1`
+  --> $DIR/self_assignment.rs:23:5
+   |
+LL |     t.1 = t.1;
+   |     ^^^^^^^^^
+
+error: self-assignment of `(t.0)` to `t.0`
+  --> $DIR/self_assignment.rs:24:5
+   |
+LL |     t.0 = (t.0);
+   |     ^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs
new file mode 100644 (file)
index 0000000..6c5ffe6
--- /dev/null
@@ -0,0 +1,83 @@
+// edition:2018
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+    clippy::missing_errors_doc,
+    clippy::needless_pass_by_value,
+    clippy::must_use_candidate,
+    clippy::unused_self,
+    clippy::needless_lifetimes,
+    clippy::missing_safety_doc,
+    clippy::wrong_self_convention
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+
+pub struct T1;
+impl T1 {
+    // corner cases: should not lint
+
+    // no error, not public interface
+    pub(crate) fn drop(&mut self) {}
+
+    // no error, private function
+    fn neg(self) -> Self {
+        self
+    }
+
+    // no error, private function
+    fn eq(&self, other: Self) -> bool {
+        true
+    }
+
+    // No error; self is a ref.
+    fn sub(&self, other: Self) -> &Self {
+        self
+    }
+
+    // No error; different number of arguments.
+    fn div(self) -> Self {
+        self
+    }
+
+    // No error; wrong return type.
+    fn rem(self, other: Self) {}
+
+    // Fine
+    fn into_u32(self) -> u32 {
+        0
+    }
+
+    fn into_u16(&self) -> u16 {
+        0
+    }
+
+    fn to_something(self) -> u32 {
+        0
+    }
+
+    fn new(self) -> Self {
+        unimplemented!();
+    }
+
+    pub fn next<'b>(&'b mut self) -> Option<&'b mut T1> {
+        unimplemented!();
+    }
+}
+
+pub struct T2;
+impl T2 {
+    // Shouldn't trigger lint as it is unsafe.
+    pub unsafe fn add(self, rhs: Self) -> Self {
+        self
+    }
+
+    // Should not trigger lint since this is an async function.
+    pub async fn next(&mut self) -> Option<Self> {
+        None
+    }
+}
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs
new file mode 100644 (file)
index 0000000..f8d248f
--- /dev/null
@@ -0,0 +1,87 @@
+// edition:2018
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+    clippy::missing_errors_doc,
+    clippy::needless_pass_by_value,
+    clippy::must_use_candidate,
+    clippy::unused_self,
+    clippy::needless_lifetimes,
+    clippy::missing_safety_doc,
+    clippy::wrong_self_convention
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+pub struct T;
+
+impl T {
+    // *****************************************
+    // trait method list part 1, should lint all
+    // *****************************************
+    pub fn add(self, other: T) -> T {
+        unimplemented!()
+    }
+
+    pub fn as_mut(&mut self) -> &mut T {
+        unimplemented!()
+    }
+
+    pub fn as_ref(&self) -> &T {
+        unimplemented!()
+    }
+
+    pub fn bitand(self, rhs: T) -> T {
+        unimplemented!()
+    }
+
+    pub fn bitor(self, rhs: Self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn bitxor(self, rhs: Self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn borrow(&self) -> &str {
+        unimplemented!()
+    }
+
+    pub fn borrow_mut(&mut self) -> &mut str {
+        unimplemented!()
+    }
+
+    pub fn clone(&self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn cmp(&self, other: &Self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn default() -> Self {
+        unimplemented!()
+    }
+
+    pub fn deref(&self) -> &Self {
+        unimplemented!()
+    }
+
+    pub fn deref_mut(&mut self) -> &mut Self {
+        unimplemented!()
+    }
+
+    pub fn div(self, rhs: Self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn drop(&mut self) {
+        unimplemented!()
+    }
+    // **********
+    // part 1 end
+    // **********
+}
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr
new file mode 100644 (file)
index 0000000..2b7d462
--- /dev/null
@@ -0,0 +1,143 @@
+error: method `add` can be confused for the standard trait method `std::ops::Add::add`
+  --> $DIR/method_list_1.rs:25:5
+   |
+LL | /     pub fn add(self, other: T) -> T {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::should-implement-trait` implied by `-D warnings`
+   = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name
+
+error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut`
+  --> $DIR/method_list_1.rs:29:5
+   |
+LL | /     pub fn as_mut(&mut self) -> &mut T {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name
+
+error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref`
+  --> $DIR/method_list_1.rs:33:5
+   |
+LL | /     pub fn as_ref(&self) -> &T {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name
+
+error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand`
+  --> $DIR/method_list_1.rs:37:5
+   |
+LL | /     pub fn bitand(self, rhs: T) -> T {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name
+
+error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor`
+  --> $DIR/method_list_1.rs:41:5
+   |
+LL | /     pub fn bitor(self, rhs: Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name
+
+error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor`
+  --> $DIR/method_list_1.rs:45:5
+   |
+LL | /     pub fn bitxor(self, rhs: Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name
+
+error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow`
+  --> $DIR/method_list_1.rs:49:5
+   |
+LL | /     pub fn borrow(&self) -> &str {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name
+
+error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut`
+  --> $DIR/method_list_1.rs:53:5
+   |
+LL | /     pub fn borrow_mut(&mut self) -> &mut str {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name
+
+error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone`
+  --> $DIR/method_list_1.rs:57:5
+   |
+LL | /     pub fn clone(&self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name
+
+error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp`
+  --> $DIR/method_list_1.rs:61:5
+   |
+LL | /     pub fn cmp(&self, other: &Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name
+
+error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref`
+  --> $DIR/method_list_1.rs:69:5
+   |
+LL | /     pub fn deref(&self) -> &Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name
+
+error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut`
+  --> $DIR/method_list_1.rs:73:5
+   |
+LL | /     pub fn deref_mut(&mut self) -> &mut Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name
+
+error: method `div` can be confused for the standard trait method `std::ops::Div::div`
+  --> $DIR/method_list_1.rs:77:5
+   |
+LL | /     pub fn div(self, rhs: Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name
+
+error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop`
+  --> $DIR/method_list_1.rs:81:5
+   |
+LL | /     pub fn drop(&mut self) {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs
new file mode 100644 (file)
index 0000000..ed5e0d3
--- /dev/null
@@ -0,0 +1,88 @@
+// edition:2018
+
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+    clippy::missing_errors_doc,
+    clippy::needless_pass_by_value,
+    clippy::must_use_candidate,
+    clippy::unused_self,
+    clippy::needless_lifetimes,
+    clippy::missing_safety_doc,
+    clippy::wrong_self_convention
+)]
+
+use std::ops::Mul;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+pub struct T;
+
+impl T {
+    // *****************************************
+    // trait method list part 2, should lint all
+    // *****************************************
+
+    pub fn eq(&self, other: &Self) -> bool {
+        unimplemented!()
+    }
+
+    pub fn from_iter<T>(iter: T) -> Self {
+        unimplemented!()
+    }
+
+    pub fn from_str(s: &str) -> Result<Self, Self> {
+        unimplemented!()
+    }
+
+    pub fn hash(&self, state: &mut T) {
+        unimplemented!()
+    }
+
+    pub fn index(&self, index: usize) -> &Self {
+        unimplemented!()
+    }
+
+    pub fn index_mut(&mut self, index: usize) -> &mut Self {
+        unimplemented!()
+    }
+
+    pub fn into_iter(self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn mul(self, rhs: Self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn neg(self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn next(&mut self) -> Option<Self> {
+        unimplemented!()
+    }
+
+    pub fn not(self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn rem(self, rhs: Self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn shl(self, rhs: Self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn shr(self, rhs: Self) -> Self {
+        unimplemented!()
+    }
+
+    pub fn sub(self, rhs: Self) -> Self {
+        unimplemented!()
+    }
+    // **********
+    // part 2 end
+    // **********
+}
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr
new file mode 100644 (file)
index 0000000..b6fd435
--- /dev/null
@@ -0,0 +1,153 @@
+error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq`
+  --> $DIR/method_list_2.rs:26:5
+   |
+LL | /     pub fn eq(&self, other: &Self) -> bool {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::should-implement-trait` implied by `-D warnings`
+   = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name
+
+error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter`
+  --> $DIR/method_list_2.rs:30:5
+   |
+LL | /     pub fn from_iter<T>(iter: T) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name
+
+error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str`
+  --> $DIR/method_list_2.rs:34:5
+   |
+LL | /     pub fn from_str(s: &str) -> Result<Self, Self> {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name
+
+error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash`
+  --> $DIR/method_list_2.rs:38:5
+   |
+LL | /     pub fn hash(&self, state: &mut T) {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name
+
+error: method `index` can be confused for the standard trait method `std::ops::Index::index`
+  --> $DIR/method_list_2.rs:42:5
+   |
+LL | /     pub fn index(&self, index: usize) -> &Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name
+
+error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut`
+  --> $DIR/method_list_2.rs:46:5
+   |
+LL | /     pub fn index_mut(&mut self, index: usize) -> &mut Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name
+
+error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter`
+  --> $DIR/method_list_2.rs:50:5
+   |
+LL | /     pub fn into_iter(self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name
+
+error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul`
+  --> $DIR/method_list_2.rs:54:5
+   |
+LL | /     pub fn mul(self, rhs: Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name
+
+error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg`
+  --> $DIR/method_list_2.rs:58:5
+   |
+LL | /     pub fn neg(self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name
+
+error: method `next` can be confused for the standard trait method `std::iter::Iterator::next`
+  --> $DIR/method_list_2.rs:62:5
+   |
+LL | /     pub fn next(&mut self) -> Option<Self> {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name
+
+error: method `not` can be confused for the standard trait method `std::ops::Not::not`
+  --> $DIR/method_list_2.rs:66:5
+   |
+LL | /     pub fn not(self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name
+
+error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem`
+  --> $DIR/method_list_2.rs:70:5
+   |
+LL | /     pub fn rem(self, rhs: Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name
+
+error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl`
+  --> $DIR/method_list_2.rs:74:5
+   |
+LL | /     pub fn shl(self, rhs: Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name
+
+error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr`
+  --> $DIR/method_list_2.rs:78:5
+   |
+LL | /     pub fn shr(self, rhs: Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name
+
+error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub`
+  --> $DIR/method_list_2.rs:82:5
+   |
+LL | /     pub fn sub(self, rhs: Self) -> Self {
+LL | |         unimplemented!()
+LL | |     }
+   | |_____^
+   |
+   = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_char_push_str.fixed b/src/tools/clippy/tests/ui/single_char_push_str.fixed
new file mode 100644 (file)
index 0000000..0812c02
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+#![warn(clippy::single_char_push_str)]
+
+fn main() {
+    let mut string = String::new();
+    string.push('R');
+    string.push('\'');
+
+    string.push('u');
+    string.push_str("st");
+    string.push_str("");
+    string.push('\x52');
+    string.push('\u{0052}');
+    string.push('a');
+}
diff --git a/src/tools/clippy/tests/ui/single_char_push_str.rs b/src/tools/clippy/tests/ui/single_char_push_str.rs
new file mode 100644 (file)
index 0000000..ab293bb
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+#![warn(clippy::single_char_push_str)]
+
+fn main() {
+    let mut string = String::new();
+    string.push_str("R");
+    string.push_str("'");
+
+    string.push('u');
+    string.push_str("st");
+    string.push_str("");
+    string.push_str("\x52");
+    string.push_str("\u{0052}");
+    string.push_str(r##"a"##);
+}
diff --git a/src/tools/clippy/tests/ui/single_char_push_str.stderr b/src/tools/clippy/tests/ui/single_char_push_str.stderr
new file mode 100644 (file)
index 0000000..0e9bdaa
--- /dev/null
@@ -0,0 +1,34 @@
+error: calling `push_str()` using a single-character string literal
+  --> $DIR/single_char_push_str.rs:6:5
+   |
+LL |     string.push_str("R");
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')`
+   |
+   = note: `-D clippy::single-char-push-str` implied by `-D warnings`
+
+error: calling `push_str()` using a single-character string literal
+  --> $DIR/single_char_push_str.rs:7:5
+   |
+LL |     string.push_str("'");
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')`
+
+error: calling `push_str()` using a single-character string literal
+  --> $DIR/single_char_push_str.rs:12:5
+   |
+LL |     string.push_str("/x52");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')`
+
+error: calling `push_str()` using a single-character string literal
+  --> $DIR/single_char_push_str.rs:13:5
+   |
+LL |     string.push_str("/u{0052}");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')`
+
+error: calling `push_str()` using a single-character string literal
+  --> $DIR/single_char_push_str.rs:14:5
+   |
+LL |     string.push_str(r##"a"##);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')`
+
+error: aborting due to 5 previous errors
+
index b0b729ede48e5cccde3396c0920baaf2cc3d7a33..780389f32bc1c6c721e7549880146870c53dd685 100644 (file)
@@ -1,4 +1,4 @@
-error: Use sort_unstable instead of sort
+error: used sort instead of sort_unstable to sort primitive type `i32`
   --> $DIR/stable_sort_primitive.rs:7:5
    |
 LL |     vec.sort();
@@ -6,37 +6,37 @@ LL |     vec.sort();
    |
    = note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
 
-error: Use sort_unstable instead of sort
+error: used sort instead of sort_unstable to sort primitive type `bool`
   --> $DIR/stable_sort_primitive.rs:9:5
    |
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 
-error: Use sort_unstable instead of sort
+error: used sort instead of sort_unstable to sort primitive type `char`
   --> $DIR/stable_sort_primitive.rs:11:5
    |
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 
-error: Use sort_unstable instead of sort
+error: used sort instead of sort_unstable to sort primitive type `str`
   --> $DIR/stable_sort_primitive.rs:13:5
    |
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 
-error: Use sort_unstable instead of sort
+error: used sort instead of sort_unstable to sort primitive type `tuple`
   --> $DIR/stable_sort_primitive.rs:15:5
    |
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 
-error: Use sort_unstable instead of sort
+error: used sort instead of sort_unstable to sort primitive type `array`
   --> $DIR/stable_sort_primitive.rs:17:5
    |
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 
-error: Use sort_unstable instead of sort
+error: used sort instead of sort_unstable to sort primitive type `i32`
   --> $DIR/stable_sort_primitive.rs:19:5
    |
 LL |     arr.sort();
index 60c2f3ec9b652159db0b19696b1139ca9a8bff4c..5c280efac1a876dbb849b7c81940b6d3db1b48ad 100644 (file)
@@ -1,5 +1,7 @@
 #![warn(clippy::suspicious_arithmetic_impl)]
-use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub};
+use std::ops::{
+    Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub,
+};
 
 #[derive(Copy, Clone)]
 struct Foo(u32);
@@ -61,6 +63,54 @@ fn div(self, other: Self) -> Self {
     }
 }
 
+impl Rem for Foo {
+    type Output = Foo;
+
+    fn rem(self, other: Self) -> Self {
+        Foo(self.0 / other.0)
+    }
+}
+
+impl BitAnd for Foo {
+    type Output = Foo;
+
+    fn bitand(self, other: Self) -> Self {
+        Foo(self.0 | other.0)
+    }
+}
+
+impl BitOr for Foo {
+    type Output = Foo;
+
+    fn bitor(self, other: Self) -> Self {
+        Foo(self.0 ^ other.0)
+    }
+}
+
+impl BitXor for Foo {
+    type Output = Foo;
+
+    fn bitxor(self, other: Self) -> Self {
+        Foo(self.0 & other.0)
+    }
+}
+
+impl Shl for Foo {
+    type Output = Foo;
+
+    fn shl(self, other: Self) -> Self {
+        Foo(self.0 >> other.0)
+    }
+}
+
+impl Shr for Foo {
+    type Output = Foo;
+
+    fn shr(self, other: Self) -> Self {
+        Foo(self.0 << other.0)
+    }
+}
+
 struct Bar(i32);
 
 impl Add for Bar {
index 23d47e3f1ff085b647451daa653918cf31093863..388fc7400820947ab912c088099d94e20391995d 100644 (file)
@@ -1,5 +1,5 @@
 error: suspicious use of binary operator in `Add` impl
-  --> $DIR/suspicious_arithmetic_impl.rs:11:20
+  --> $DIR/suspicious_arithmetic_impl.rs:13:20
    |
 LL |         Foo(self.0 - other.0)
    |                    ^
@@ -7,7 +7,7 @@ LL |         Foo(self.0 - other.0)
    = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings`
 
 error: suspicious use of binary operator in `AddAssign` impl
-  --> $DIR/suspicious_arithmetic_impl.rs:17:23
+  --> $DIR/suspicious_arithmetic_impl.rs:19:23
    |
 LL |         *self = *self - other;
    |                       ^
@@ -15,10 +15,46 @@ LL |         *self = *self - other;
    = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default
 
 error: suspicious use of binary operator in `MulAssign` impl
-  --> $DIR/suspicious_arithmetic_impl.rs:30:16
+  --> $DIR/suspicious_arithmetic_impl.rs:32:16
    |
 LL |         self.0 /= other.0;
    |                ^^
 
-error: aborting due to 3 previous errors
+error: suspicious use of binary operator in `Rem` impl
+  --> $DIR/suspicious_arithmetic_impl.rs:70:20
+   |
+LL |         Foo(self.0 / other.0)
+   |                    ^
+
+error: suspicious use of binary operator in `BitAnd` impl
+  --> $DIR/suspicious_arithmetic_impl.rs:78:20
+   |
+LL |         Foo(self.0 | other.0)
+   |                    ^
+
+error: suspicious use of binary operator in `BitOr` impl
+  --> $DIR/suspicious_arithmetic_impl.rs:86:20
+   |
+LL |         Foo(self.0 ^ other.0)
+   |                    ^
+
+error: suspicious use of binary operator in `BitXor` impl
+  --> $DIR/suspicious_arithmetic_impl.rs:94:20
+   |
+LL |         Foo(self.0 & other.0)
+   |                    ^
+
+error: suspicious use of binary operator in `Shl` impl
+  --> $DIR/suspicious_arithmetic_impl.rs:102:20
+   |
+LL |         Foo(self.0 >> other.0)
+   |                    ^^
+
+error: suspicious use of binary operator in `Shr` impl
+  --> $DIR/suspicious_arithmetic_impl.rs:110:20
+   |
+LL |         Foo(self.0 << other.0)
+   |                    ^^
+
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/to_string_in_display.rs b/src/tools/clippy/tests/ui/to_string_in_display.rs
new file mode 100644 (file)
index 0000000..eb8105c
--- /dev/null
@@ -0,0 +1,69 @@
+#![warn(clippy::to_string_in_display)]
+#![allow(clippy::inherent_to_string_shadow_display)]
+
+use std::fmt;
+
+struct A;
+impl A {
+    fn fmt(&self) {
+        self.to_string();
+    }
+}
+
+trait B {
+    fn fmt(&self) {}
+}
+
+impl B for A {
+    fn fmt(&self) {
+        self.to_string();
+    }
+}
+
+impl fmt::Display for A {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", self.to_string())
+    }
+}
+
+fn fmt(a: A) {
+    a.to_string();
+}
+
+struct C;
+
+impl C {
+    fn to_string(&self) -> String {
+        String::from("I am C")
+    }
+}
+
+impl fmt::Display for C {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", self.to_string())
+    }
+}
+
+enum D {
+    E(String),
+    F,
+}
+
+impl std::fmt::Display for D {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match &self {
+            Self::E(string) => write!(f, "E {}", string.to_string()),
+            Self::F => write!(f, "F"),
+        }
+    }
+}
+
+fn main() {
+    let a = A;
+    a.to_string();
+    a.fmt();
+    fmt(a);
+
+    let c = C;
+    c.to_string();
+}
diff --git a/src/tools/clippy/tests/ui/to_string_in_display.stderr b/src/tools/clippy/tests/ui/to_string_in_display.stderr
new file mode 100644 (file)
index 0000000..5f26ef4
--- /dev/null
@@ -0,0 +1,10 @@
+error: using `to_string` in `fmt::Display` implementation might lead to infinite recursion
+  --> $DIR/to_string_in_display.rs:25:25
+   |
+LL |         write!(f, "{}", self.to_string())
+   |                         ^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::to-string-in-display` implied by `-D warnings`
+
+error: aborting due to previous error
+
index bb853d237047fbc33f6bd85b50c04ad8f2e5edf2..9f1948359e7d585d6d8ca2eacd26022d7330eeb8 100644 (file)
@@ -1,3 +1,4 @@
+#![feature(const_fn_transmute)]
 #![allow(dead_code)]
 
 extern crate core;
@@ -81,9 +82,26 @@ fn int_to_bool() {
 }
 
 #[warn(clippy::transmute_int_to_float)]
-fn int_to_float() {
-    let _: f32 = unsafe { std::mem::transmute(0_u32) };
-    let _: f32 = unsafe { std::mem::transmute(0_i32) };
+mod int_to_float {
+    fn test() {
+        let _: f32 = unsafe { std::mem::transmute(0_u32) };
+        let _: f32 = unsafe { std::mem::transmute(0_i32) };
+        let _: f64 = unsafe { std::mem::transmute(0_u64) };
+        let _: f64 = unsafe { std::mem::transmute(0_i64) };
+    }
+
+    mod issue_5747 {
+        const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) };
+        const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) };
+
+        const fn from_bits_32(v: i32) -> f32 {
+            unsafe { std::mem::transmute(v) }
+        }
+
+        const fn from_bits_64(v: u64) -> f64 {
+            unsafe { std::mem::transmute(v) }
+        }
+    }
 }
 
 fn bytes_to_str(b: &[u8], mb: &mut [u8]) {
index 8582080498f3e4febde7b3ef68d942c6a8eaf3bd..ad9953d12bcc626fb93c7849d0e8b6957406c76b 100644 (file)
@@ -1,5 +1,5 @@
 error: transmute from a type (`&T`) to itself
-  --> $DIR/transmute.rs:19:20
+  --> $DIR/transmute.rs:20:20
    |
 LL |     let _: &'a T = core::intrinsics::transmute(t);
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,67 +7,67 @@ LL |     let _: &'a T = core::intrinsics::transmute(t);
    = note: `-D clippy::useless-transmute` implied by `-D warnings`
 
 error: transmute from a reference to a pointer
-  --> $DIR/transmute.rs:23:23
+  --> $DIR/transmute.rs:24:23
    |
 LL |     let _: *const T = core::intrinsics::transmute(t);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
 
 error: transmute from a reference to a pointer
-  --> $DIR/transmute.rs:25:21
+  --> $DIR/transmute.rs:26:21
    |
 LL |     let _: *mut T = core::intrinsics::transmute(t);
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
 
 error: transmute from a reference to a pointer
-  --> $DIR/transmute.rs:27:23
+  --> $DIR/transmute.rs:28:23
    |
 LL |     let _: *const U = core::intrinsics::transmute(t);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> $DIR/transmute.rs:33:27
+  --> $DIR/transmute.rs:34:27
    |
 LL |         let _: Vec<i32> = core::intrinsics::transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> $DIR/transmute.rs:35:27
+  --> $DIR/transmute.rs:36:27
    |
 LL |         let _: Vec<i32> = core::mem::transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> $DIR/transmute.rs:37:27
+  --> $DIR/transmute.rs:38:27
    |
 LL |         let _: Vec<i32> = std::intrinsics::transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> $DIR/transmute.rs:39:27
+  --> $DIR/transmute.rs:40:27
    |
 LL |         let _: Vec<i32> = std::mem::transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> $DIR/transmute.rs:41:27
+  --> $DIR/transmute.rs:42:27
    |
 LL |         let _: Vec<i32> = my_transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from an integer to a pointer
-  --> $DIR/transmute.rs:43:31
+  --> $DIR/transmute.rs:44:31
    |
 LL |         let _: *const usize = std::mem::transmute(5_isize);
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize`
 
 error: transmute from an integer to a pointer
-  --> $DIR/transmute.rs:47:31
+  --> $DIR/transmute.rs:48:31
    |
 LL |         let _: *const usize = std::mem::transmute(1 + 1usize);
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize`
 
 error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
-  --> $DIR/transmute.rs:62:24
+  --> $DIR/transmute.rs:63:24
    |
 LL |         let _: Usize = core::intrinsics::transmute(int_const_ptr);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -75,25 +75,25 @@ LL |         let _: Usize = core::intrinsics::transmute(int_const_ptr);
    = note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
 
 error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
-  --> $DIR/transmute.rs:64:24
+  --> $DIR/transmute.rs:65:24
    |
 LL |         let _: Usize = core::intrinsics::transmute(int_mut_ptr);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
-  --> $DIR/transmute.rs:66:31
+  --> $DIR/transmute.rs:67:31
    |
 LL |         let _: *const Usize = core::intrinsics::transmute(my_int());
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
-  --> $DIR/transmute.rs:68:29
+  --> $DIR/transmute.rs:69:29
    |
 LL |         let _: *mut Usize = core::intrinsics::transmute(my_int());
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a `u32` to a `char`
-  --> $DIR/transmute.rs:74:28
+  --> $DIR/transmute.rs:75:28
    |
 LL |     let _: char = unsafe { std::mem::transmute(0_u32) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()`
@@ -101,13 +101,13 @@ LL |     let _: char = unsafe { std::mem::transmute(0_u32) };
    = note: `-D clippy::transmute-int-to-char` implied by `-D warnings`
 
 error: transmute from a `i32` to a `char`
-  --> $DIR/transmute.rs:75:28
+  --> $DIR/transmute.rs:76:28
    |
 LL |     let _: char = unsafe { std::mem::transmute(0_i32) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()`
 
 error: transmute from a `u8` to a `bool`
-  --> $DIR/transmute.rs:80:28
+  --> $DIR/transmute.rs:81:28
    |
 LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
@@ -115,21 +115,33 @@ LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
    = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
 
 error: transmute from a `u32` to a `f32`
-  --> $DIR/transmute.rs:85:27
+  --> $DIR/transmute.rs:87:31
    |
-LL |     let _: f32 = unsafe { std::mem::transmute(0_u32) };
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
+LL |         let _: f32 = unsafe { std::mem::transmute(0_u32) };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
    |
    = note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
 
 error: transmute from a `i32` to a `f32`
-  --> $DIR/transmute.rs:86:27
+  --> $DIR/transmute.rs:88:31
+   |
+LL |         let _: f32 = unsafe { std::mem::transmute(0_i32) };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
+
+error: transmute from a `u64` to a `f64`
+  --> $DIR/transmute.rs:89:31
+   |
+LL |         let _: f64 = unsafe { std::mem::transmute(0_u64) };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)`
+
+error: transmute from a `i64` to a `f64`
+  --> $DIR/transmute.rs:90:31
    |
-LL |     let _: f32 = unsafe { std::mem::transmute(0_i32) };
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
+LL |         let _: f64 = unsafe { std::mem::transmute(0_i64) };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)`
 
 error: transmute from a `&[u8]` to a `&str`
-  --> $DIR/transmute.rs:90:28
+  --> $DIR/transmute.rs:108:28
    |
 LL |     let _: &str = unsafe { std::mem::transmute(b) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()`
@@ -137,10 +149,10 @@ LL |     let _: &str = unsafe { std::mem::transmute(b) };
    = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings`
 
 error: transmute from a `&mut [u8]` to a `&mut str`
-  --> $DIR/transmute.rs:91:32
+  --> $DIR/transmute.rs:109:32
    |
 LL |     let _: &mut str = unsafe { std::mem::transmute(mb) };
    |                                ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
 
-error: aborting due to 22 previous errors
+error: aborting due to 24 previous errors
 
index ce942751ada82cee06778cf4599528b3073e5397..1040fee4b34d00c3b2cff7c9106ac16338d8ade7 100644 (file)
@@ -1,4 +1,5 @@
-#[warn(clippy::transmute_float_to_int)]
+#![feature(const_fn_transmute)]
+#![warn(clippy::transmute_float_to_int)]
 
 fn float_to_int() {
     let _: u32 = unsafe { std::mem::transmute(1f32) };
@@ -9,4 +10,17 @@ fn float_to_int() {
     let _: u64 = unsafe { std::mem::transmute(-1.0) };
 }
 
+mod issue_5747 {
+    const VALUE32: i32 = unsafe { std::mem::transmute(1f32) };
+    const VALUE64: u64 = unsafe { std::mem::transmute(1f64) };
+
+    const fn to_bits_32(v: f32) -> u32 {
+        unsafe { std::mem::transmute(v) }
+    }
+
+    const fn to_bits_64(v: f64) -> i64 {
+        unsafe { std::mem::transmute(v) }
+    }
+}
+
 fn main() {}
index eb786bb39f95aa5c1f7110d053acf78cd96b340b..5a40cf381d6147ee464da4cdcbe0e72ab1e75721 100644 (file)
@@ -1,5 +1,5 @@
 error: transmute from a `f32` to a `u32`
-  --> $DIR/transmute_float_to_int.rs:4:27
+  --> $DIR/transmute_float_to_int.rs:5:27
    |
 LL |     let _: u32 = unsafe { std::mem::transmute(1f32) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()`
@@ -7,31 +7,31 @@ LL |     let _: u32 = unsafe { std::mem::transmute(1f32) };
    = note: `-D clippy::transmute-float-to-int` implied by `-D warnings`
 
 error: transmute from a `f32` to a `i32`
-  --> $DIR/transmute_float_to_int.rs:5:27
+  --> $DIR/transmute_float_to_int.rs:6:27
    |
 LL |     let _: i32 = unsafe { std::mem::transmute(1f32) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32`
 
 error: transmute from a `f64` to a `u64`
-  --> $DIR/transmute_float_to_int.rs:6:27
+  --> $DIR/transmute_float_to_int.rs:7:27
    |
 LL |     let _: u64 = unsafe { std::mem::transmute(1f64) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()`
 
 error: transmute from a `f64` to a `i64`
-  --> $DIR/transmute_float_to_int.rs:7:27
+  --> $DIR/transmute_float_to_int.rs:8:27
    |
 LL |     let _: i64 = unsafe { std::mem::transmute(1f64) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64`
 
 error: transmute from a `f64` to a `u64`
-  --> $DIR/transmute_float_to_int.rs:8:27
+  --> $DIR/transmute_float_to_int.rs:9:27
    |
 LL |     let _: u64 = unsafe { std::mem::transmute(1.0) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()`
 
 error: transmute from a `f64` to a `u64`
-  --> $DIR/transmute_float_to_int.rs:9:27
+  --> $DIR/transmute_float_to_int.rs:10:27
    |
 LL |     let _: u64 = unsafe { std::mem::transmute(-1.0) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()`
index 316426f1cf181788dbc48dba7e69f244f4908531..e7e0a31febc45919ca44408c7772a83cc162126c 100644 (file)
@@ -97,6 +97,24 @@ fn a(b: &u16) {}
     pub fn c(d: &u16) {}
 }
 
+mod issue5876 {
+    // Don't lint here as it is always inlined
+    #[inline(always)]
+    fn foo_always(x: &i32) {
+        println!("{}", x);
+    }
+
+    #[inline(never)]
+    fn foo_never(x: &i32) {
+        println!("{}", x);
+    }
+
+    #[inline]
+    fn foo(x: &i32) {
+        println!("{}", x);
+    }
+}
+
 fn main() {
     let (mut foo, bar) = (Foo(0), Bar([0; 24]));
     let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
index be0914e4a7947f7197002ee591009b0c79fa8abd..ccc3cdb2b74264fec42b9741312f627315ee906b 100644 (file)
@@ -94,5 +94,17 @@ error: this argument (N byte) is passed by reference, but would be more efficien
 LL |     fn trait_method2(&self, _color: &Color);
    |                                     ^^^^^^ help: consider passing by value instead: `Color`
 
-error: aborting due to 15 previous errors
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+  --> $DIR/trivially_copy_pass_by_ref.rs:108:21
+   |
+LL |     fn foo_never(x: &i32) {
+   |                     ^^^^ help: consider passing by value instead: `i32`
+
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+  --> $DIR/trivially_copy_pass_by_ref.rs:113:15
+   |
+LL |     fn foo(x: &i32) {
+   |               ^^^^ help: consider passing by value instead: `i32`
+
+error: aborting due to 17 previous errors
 
index 2c9d4d39e6c7d9e7d18c8c7daf59fbe075b18b32..e785ac02feb320e4c4fb1c54e671a335a4a3d3fc 100644 (file)
@@ -90,3 +90,21 @@ fn check(mut encoded: &[u8]) {
         let _ = &encoded.clone();
     }
 }
+
+mod issue2076 {
+    use std::rc::Rc;
+
+    macro_rules! try_opt {
+        ($expr: expr) => {
+            match $expr {
+                Some(value) => value,
+                None => return None,
+            }
+        };
+    }
+
+    fn func() -> Option<Rc<u8>> {
+        let rc = Rc::new(42);
+        Some(try_opt!(Some(rc)).clone())
+    }
+}
index 113fab6900954c891b2610517dd728148aa3994b..5ffa6c4fd06167ce0c7e68545fef718f97c00418 100644 (file)
@@ -96,5 +96,11 @@ help: or try being explicit if you are sure, that you want to clone a reference
 LL |         let _ = &<&[u8]>::clone(encoded);
    |                  ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 11 previous errors
+error: using `.clone()` on a ref-counted pointer
+  --> $DIR/unnecessary_clone.rs:108:14
+   |
+LL |         Some(try_opt!(Some(rc)).clone())
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::<u8>::clone(&try_opt!(Some(rc)))`
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
new file mode 100644 (file)
index 0000000..fa66e68
--- /dev/null
@@ -0,0 +1,117 @@
+// run-rustfix
+#![warn(clippy::unnecessary_lazy_evaluations)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::bind_instead_of_map)]
+
+struct Deep(Option<usize>);
+
+#[derive(Copy, Clone)]
+struct SomeStruct {
+    some_field: usize,
+}
+
+impl SomeStruct {
+    fn return_some_field(&self) -> usize {
+        self.some_field
+    }
+}
+
+fn some_call<T: Default>() -> T {
+    T::default()
+}
+
+fn main() {
+    let astronomers_pi = 10;
+    let ext_arr: [usize; 1] = [2];
+    let ext_str = SomeStruct { some_field: 10 };
+
+    let mut opt = Some(42);
+    let ext_opt = Some(42);
+    let nested_opt = Some(Some(42));
+    let nested_tuple_opt = Some(Some((42, 43)));
+
+    // Should lint - Option
+    let _ = opt.unwrap_or(2);
+    let _ = opt.unwrap_or(astronomers_pi);
+    let _ = opt.unwrap_or(ext_str.some_field);
+    let _ = opt.unwrap_or(ext_arr[0]);
+    let _ = opt.and(ext_opt);
+    let _ = opt.or(ext_opt);
+    let _ = opt.or(None);
+    let _ = opt.get_or_insert(2);
+    let _ = opt.ok_or(2);
+    let _ = opt.ok_or(ext_arr[0]);
+
+    // Cases when unwrap is not called on a simple variable
+    let _ = Some(10).unwrap_or(2);
+    let _ = Some(10).and(ext_opt);
+    let _: Option<usize> = None.or(ext_opt);
+    let _ = None.get_or_insert(2);
+    let _: Result<usize, usize> = None.ok_or(2);
+    let _: Option<usize> = None.or(None);
+
+    let mut deep = Deep(Some(42));
+    let _ = deep.0.unwrap_or(2);
+    let _ = deep.0.and(ext_opt);
+    let _ = deep.0.or(None);
+    let _ = deep.0.get_or_insert(2);
+    let _ = deep.0.ok_or(2);
+
+    // Should not lint - Option
+    let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
+    let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
+    let _ = opt.or_else(some_call);
+    let _ = opt.or_else(|| some_call());
+    let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
+    let _: Result<usize, usize> = opt.ok_or_else(some_call);
+    let _ = deep.0.get_or_insert_with(|| some_call());
+    let _ = deep.0.or_else(some_call);
+    let _ = deep.0.or_else(|| some_call());
+
+    // These are handled by bind_instead_of_map
+    let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
+    let _ = Some(10).and_then(|idx| Some(idx));
+    let _: Option<usize> = None.or_else(|| Some(3));
+    let _ = deep.0.or_else(|| Some(3));
+    let _ = opt.or_else(|| Some(3));
+
+    // Should lint - Result
+    let res: Result<usize, usize> = Err(5);
+    let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
+
+    let _ = res2.unwrap_or(2);
+    let _ = res2.unwrap_or(astronomers_pi);
+    let _ = res2.unwrap_or(ext_str.some_field);
+
+    // Should not lint - Result
+    let _ = res.unwrap_or_else(|err| err);
+    let _ = res.unwrap_or_else(|err| ext_arr[err]);
+    let _ = res2.unwrap_or_else(|err| err.some_field);
+    let _ = res2.unwrap_or_else(|err| err.return_some_field());
+    let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
+
+    let _: Result<usize, usize> = res.and_then(|x| Ok(x));
+    let _: Result<usize, usize> = res.and_then(|x| Err(x));
+
+    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
+    let _: Result<usize, usize> = res.or_else(|err| Err(err));
+
+    // These are handled by bind_instead_of_map
+    let _: Result<usize, usize> = res.and_then(|_| Ok(2));
+    let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.and_then(|_| Err(2));
+    let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or_else(|_| Ok(2));
+    let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or_else(|_| Err(2));
+    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
new file mode 100644 (file)
index 0000000..04f47d1
--- /dev/null
@@ -0,0 +1,117 @@
+// run-rustfix
+#![warn(clippy::unnecessary_lazy_evaluations)]
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::bind_instead_of_map)]
+
+struct Deep(Option<usize>);
+
+#[derive(Copy, Clone)]
+struct SomeStruct {
+    some_field: usize,
+}
+
+impl SomeStruct {
+    fn return_some_field(&self) -> usize {
+        self.some_field
+    }
+}
+
+fn some_call<T: Default>() -> T {
+    T::default()
+}
+
+fn main() {
+    let astronomers_pi = 10;
+    let ext_arr: [usize; 1] = [2];
+    let ext_str = SomeStruct { some_field: 10 };
+
+    let mut opt = Some(42);
+    let ext_opt = Some(42);
+    let nested_opt = Some(Some(42));
+    let nested_tuple_opt = Some(Some((42, 43)));
+
+    // Should lint - Option
+    let _ = opt.unwrap_or_else(|| 2);
+    let _ = opt.unwrap_or_else(|| astronomers_pi);
+    let _ = opt.unwrap_or_else(|| ext_str.some_field);
+    let _ = opt.unwrap_or_else(|| ext_arr[0]);
+    let _ = opt.and_then(|_| ext_opt);
+    let _ = opt.or_else(|| ext_opt);
+    let _ = opt.or_else(|| None);
+    let _ = opt.get_or_insert_with(|| 2);
+    let _ = opt.ok_or_else(|| 2);
+    let _ = opt.ok_or_else(|| ext_arr[0]);
+
+    // Cases when unwrap is not called on a simple variable
+    let _ = Some(10).unwrap_or_else(|| 2);
+    let _ = Some(10).and_then(|_| ext_opt);
+    let _: Option<usize> = None.or_else(|| ext_opt);
+    let _ = None.get_or_insert_with(|| 2);
+    let _: Result<usize, usize> = None.ok_or_else(|| 2);
+    let _: Option<usize> = None.or_else(|| None);
+
+    let mut deep = Deep(Some(42));
+    let _ = deep.0.unwrap_or_else(|| 2);
+    let _ = deep.0.and_then(|_| ext_opt);
+    let _ = deep.0.or_else(|| None);
+    let _ = deep.0.get_or_insert_with(|| 2);
+    let _ = deep.0.ok_or_else(|| 2);
+
+    // Should not lint - Option
+    let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
+    let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
+    let _ = opt.or_else(some_call);
+    let _ = opt.or_else(|| some_call());
+    let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
+    let _: Result<usize, usize> = opt.ok_or_else(some_call);
+    let _ = deep.0.get_or_insert_with(|| some_call());
+    let _ = deep.0.or_else(some_call);
+    let _ = deep.0.or_else(|| some_call());
+
+    // These are handled by bind_instead_of_map
+    let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
+    let _ = Some(10).and_then(|idx| Some(idx));
+    let _: Option<usize> = None.or_else(|| Some(3));
+    let _ = deep.0.or_else(|| Some(3));
+    let _ = opt.or_else(|| Some(3));
+
+    // Should lint - Result
+    let res: Result<usize, usize> = Err(5);
+    let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
+
+    let _ = res2.unwrap_or_else(|_| 2);
+    let _ = res2.unwrap_or_else(|_| astronomers_pi);
+    let _ = res2.unwrap_or_else(|_| ext_str.some_field);
+
+    // Should not lint - Result
+    let _ = res.unwrap_or_else(|err| err);
+    let _ = res.unwrap_or_else(|err| ext_arr[err]);
+    let _ = res2.unwrap_or_else(|err| err.some_field);
+    let _ = res2.unwrap_or_else(|err| err.return_some_field());
+    let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
+
+    let _: Result<usize, usize> = res.and_then(|x| Ok(x));
+    let _: Result<usize, usize> = res.and_then(|x| Err(x));
+
+    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
+    let _: Result<usize, usize> = res.or_else(|err| Err(err));
+
+    // These are handled by bind_instead_of_map
+    let _: Result<usize, usize> = res.and_then(|_| Ok(2));
+    let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.and_then(|_| Err(2));
+    let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or_else(|_| Ok(2));
+    let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or_else(|_| Err(2));
+    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
new file mode 100644 (file)
index 0000000..5c1b2eb
--- /dev/null
@@ -0,0 +1,148 @@
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:34:13
+   |
+LL |     let _ = opt.unwrap_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)`
+   |
+   = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:35:13
+   |
+LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:36:13
+   |
+LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:37:13
+   |
+LL |     let _ = opt.unwrap_or_else(|| ext_arr[0]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:38:13
+   |
+LL |     let _ = opt.and_then(|_| ext_opt);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:39:13
+   |
+LL |     let _ = opt.or_else(|| ext_opt);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:40:13
+   |
+LL |     let _ = opt.or_else(|| None);
+   |             ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:41:13
+   |
+LL |     let _ = opt.get_or_insert_with(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:42:13
+   |
+LL |     let _ = opt.ok_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:43:13
+   |
+LL |     let _ = opt.ok_or_else(|| ext_arr[0]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:46:13
+   |
+LL |     let _ = Some(10).unwrap_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:47:13
+   |
+LL |     let _ = Some(10).and_then(|_| ext_opt);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:48:28
+   |
+LL |     let _: Option<usize> = None.or_else(|| ext_opt);
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:49:13
+   |
+LL |     let _ = None.get_or_insert_with(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:50:35
+   |
+LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
+   |                                   ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:51:28
+   |
+LL |     let _: Option<usize> = None.or_else(|| None);
+   |                            ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:54:13
+   |
+LL |     let _ = deep.0.unwrap_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:55:13
+   |
+LL |     let _ = deep.0.and_then(|_| ext_opt);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:56:13
+   |
+LL |     let _ = deep.0.or_else(|| None);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:57:13
+   |
+LL |     let _ = deep.0.get_or_insert_with(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:58:13
+   |
+LL |     let _ = deep.0.ok_or_else(|| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:84:13
+   |
+LL |     let _ = res2.unwrap_or_else(|_| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:85:13
+   |
+LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:86:13
+   |
+LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)`
+
+error: aborting due to 24 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.rs b/src/tools/clippy/tests/ui/unwrap_in_result.rs
new file mode 100644 (file)
index 0000000..2aa842a
--- /dev/null
@@ -0,0 +1,44 @@
+#![warn(clippy::unwrap_in_result)]
+
+struct A;
+
+impl A {
+    // should not be detected
+    fn good_divisible_by_3(i_str: String) -> Result<bool, String> {
+        // checks whether a string represents a number divisible by 3
+        let i_result = i_str.parse::<i32>();
+        match i_result {
+            Err(_e) => Err("Not a number".to_string()),
+            Ok(i) => {
+                if i % 3 == 0 {
+                    return Ok(true);
+                }
+                Err("Number is not divisible by 3".to_string())
+            },
+        }
+    }
+
+    // should be detected
+    fn bad_divisible_by_3(i_str: String) -> Result<bool, String> {
+        // checks whether a string represents a number divisible by 3
+        let i = i_str.parse::<i32>().unwrap();
+        if i % 3 == 0 {
+            Ok(true)
+        } else {
+            Err("Number is not divisible by 3".to_string())
+        }
+    }
+
+    fn example_option_expect(i_str: String) -> Option<bool> {
+        let i = i_str.parse::<i32>().expect("not a number");
+        if i % 3 == 0 {
+            return Some(true);
+        }
+        None
+    }
+}
+
+fn main() {
+    A::bad_divisible_by_3("3".to_string());
+    A::good_divisible_by_3("3".to_string());
+}
diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.stderr b/src/tools/clippy/tests/ui/unwrap_in_result.stderr
new file mode 100644 (file)
index 0000000..56bc2f2
--- /dev/null
@@ -0,0 +1,41 @@
+error: used unwrap or expect in a function that returns result or option
+  --> $DIR/unwrap_in_result.rs:22:5
+   |
+LL | /     fn bad_divisible_by_3(i_str: String) -> Result<bool, String> {
+LL | |         // checks whether a string represents a number divisible by 3
+LL | |         let i = i_str.parse::<i32>().unwrap();
+LL | |         if i % 3 == 0 {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::unwrap-in-result` implied by `-D warnings`
+   = help: unwrap and expect should not be used in a function that returns result or option
+note: potential non-recoverable error(s)
+  --> $DIR/unwrap_in_result.rs:24:17
+   |
+LL |         let i = i_str.parse::<i32>().unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: used unwrap or expect in a function that returns result or option
+  --> $DIR/unwrap_in_result.rs:32:5
+   |
+LL | /     fn example_option_expect(i_str: String) -> Option<bool> {
+LL | |         let i = i_str.parse::<i32>().expect("not a number");
+LL | |         if i % 3 == 0 {
+LL | |             return Some(true);
+LL | |         }
+LL | |         None
+LL | |     }
+   | |_____^
+   |
+   = help: unwrap and expect should not be used in a function that returns result or option
+note: potential non-recoverable error(s)
+  --> $DIR/unwrap_in_result.rs:33:17
+   |
+LL |         let i = i_str.parse::<i32>().expect("not a number");
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
index 813cdaecaa91a8864f8f285a8f3c3130c0cb5bc4..8a9b0cd3cf019466d3786f7be3eb08ceda2f0509 100644 (file)
@@ -64,4 +64,9 @@ fn main() {
     let _ = "".lines();
     let _ = vec![1, 2, 3].into_iter();
     let _: String = format!("Hello {}", "world");
+
+    // keep parenthesis around `a + b` for suggestion (see #4750)
+    let a: i32 = 1;
+    let b: i32 = 1;
+    let _ = (a + b) * 3;
 }
index 540fea23b36b98e0061e4a7566b0666e04254516..4faa1572973bc0ff5df24d3c2fe392e49d219c0d 100644 (file)
@@ -64,4 +64,9 @@ fn main() {
     let _ = "".lines().into_iter();
     let _ = vec![1, 2, 3].into_iter().into_iter();
     let _: String = format!("Hello {}", "world").into();
+
+    // keep parenthesis around `a + b` for suggestion (see #4750)
+    let a: i32 = 1;
+    let b: i32 = 1;
+    let _ = i32::from(a + b) * 3;
 }
index b958b0354520326577fdfcf17d56e932a6b813df..f1e880d2696c40c430e23e11f2a60aec52e5dc7f 100644 (file)
@@ -64,5 +64,11 @@ error: useless conversion to the same type
 LL |     let _: String = format!("Hello {}", "world").into();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")`
 
-error: aborting due to 10 previous errors
+error: useless conversion to the same type
+  --> $DIR/useless_conversion.rs:71:13
+   |
+LL |     let _ = i32::from(a + b) * 3;
+   |             ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)`
+
+error: aborting due to 11 previous errors
 
index e73a791891f890664cf3eddc2f60b63040c4e995..856771596202efb47f1c755759ccf60040133d00 100644 (file)
@@ -52,4 +52,11 @@ fn main() {
     for a in vec![NonCopy, NonCopy] {
         println!("{:?}", a);
     }
+
+    on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
+
+    // Ok
+    for a in vec![1; 201] {
+        println!("{:?}", a);
+    }
 }
index 3eb960f53d7af7dc33dc7baa14a51b36c7f65e05..03b8ee816658ccefbebb278a89e278cc36945691 100644 (file)
@@ -52,4 +52,11 @@ fn main() {
     for a in vec![NonCopy, NonCopy] {
         println!("{:?}", a);
     }
+
+    on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
+
+    // Ok
+    for a in vec![1; 201] {
+        println!("{:?}", a);
+    }
 }
index 67423e6ec1d19bca17b3acd4b55227ab6cfae2b8..287f8935327c561f0dd7b8543597fbfe5095b9ae 100644 (file)
@@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
 use wildcard_imports_helper::{ExternA, extern_foo};
 
 use std::io::prelude::*;
+use wildcard_imports_helper::prelude::v1::*;
 
 struct ReadFoo;
 
@@ -75,6 +76,7 @@ fn main() {
     let _ = A;
     let _ = inner_struct_mod::C;
     let _ = ExternA;
+    let _ = PreludeModAnywhere;
 
     double_struct_import_test!();
     double_struct_import_test!();
index 3ad1a29aebad1f985a19e272ac55f071263f511c..1f261159f4a94e2f32b2c44237e66e2ea62510d7 100644 (file)
@@ -20,6 +20,7 @@
 use wildcard_imports_helper::*;
 
 use std::io::prelude::*;
+use wildcard_imports_helper::prelude::v1::*;
 
 struct ReadFoo;
 
@@ -75,6 +76,7 @@ fn main() {
     let _ = A;
     let _ = inner_struct_mod::C;
     let _ = ExternA;
+    let _ = PreludeModAnywhere;
 
     double_struct_import_test!();
     double_struct_import_test!();
index fab43b738eb434d1ab6b453dffbc8967bfeeb222..351988f31ead5b881c5086951cb42fc2e6af9b69 100644 (file)
@@ -37,55 +37,55 @@ LL | use wildcard_imports_helper::*;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:89:13
+  --> $DIR/wildcard_imports.rs:91:13
    |
 LL |         use crate::fn_mod::*;
    |             ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:95:75
+  --> $DIR/wildcard_imports.rs:97:75
    |
 LL |         use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
    |                                                                           ^ help: try: `inner_extern_foo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:96:13
+  --> $DIR/wildcard_imports.rs:98:13
    |
 LL |         use wildcard_imports_helper::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:107:20
+  --> $DIR/wildcard_imports.rs:109:20
    |
 LL |         use self::{inner::*, inner2::*};
    |                    ^^^^^^^^ help: try: `inner::inner_foo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:107:30
+  --> $DIR/wildcard_imports.rs:109:30
    |
 LL |         use self::{inner::*, inner2::*};
    |                              ^^^^^^^^^ help: try: `inner2::inner_bar`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:114:13
+  --> $DIR/wildcard_imports.rs:116:13
    |
 LL |         use wildcard_imports_helper::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:143:9
+  --> $DIR/wildcard_imports.rs:145:9
    |
 LL |     use crate::in_fn_test::*;
    |         ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:152:9
+  --> $DIR/wildcard_imports.rs:154:9
    |
 LL |     use crate:: in_fn_test::  * ;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:153:9
+  --> $DIR/wildcard_imports.rs:155:9
    |
 LL |       use crate:: fn_mod::
    |  _________^
@@ -93,31 +93,31 @@ LL | |         *;
    | |_________^ help: try: `crate:: fn_mod::foo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:164:13
+  --> $DIR/wildcard_imports.rs:166:13
    |
 LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:199:17
+  --> $DIR/wildcard_imports.rs:201:17
    |
 LL |             use super::*;
    |                 ^^^^^^^^ help: try: `super::insidefoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:207:13
+  --> $DIR/wildcard_imports.rs:209:13
    |
 LL |         use super_imports::*;
    |             ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:216:17
+  --> $DIR/wildcard_imports.rs:218:17
    |
 LL |             use super::super::*;
    |                 ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:225:13
+  --> $DIR/wildcard_imports.rs:227:13
    |
 LL |         use super::super::super_imports::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
index 99652ca4470c2a47530812a3b78f0e2381f68ec3..f44305d7e483855614d8ce52a5ea3cb15bea0492 100644 (file)
@@ -1,3 +1,4 @@
+// edition:2018
 #![warn(clippy::wrong_self_convention)]
 #![warn(clippy::wrong_pub_self_convention)]
 #![allow(dead_code)]
@@ -75,3 +76,15 @@ fn into_t2(self: Rc<T>) {}
         fn into_t3(self: Arc<T>) {}
     }
 }
+
+// False positive for async (see #4037)
+mod issue4037 {
+    pub struct Foo;
+    pub struct Bar;
+
+    impl Foo {
+        pub async fn into_bar(self) -> Bar {
+            Bar
+        }
+    }
+}
index 0d0eb19cd072333012d91cc384e5b6a2031e199a..ef3ad73ebc7c18cada8aaf7825cb843318343af3 100644 (file)
@@ -1,5 +1,5 @@
 error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:17:17
+  --> $DIR/wrong_self_convention.rs:18:17
    |
 LL |     fn from_i32(self) {}
    |                 ^^^^
@@ -7,67 +7,67 @@ LL |     fn from_i32(self) {}
    = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
 
 error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:23:21
+  --> $DIR/wrong_self_convention.rs:24:21
    |
 LL |     pub fn from_i64(self) {}
    |                     ^^^^
 
 error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:35:15
+  --> $DIR/wrong_self_convention.rs:36:15
    |
 LL |     fn as_i32(self) {}
    |               ^^^^
 
 error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:37:17
+  --> $DIR/wrong_self_convention.rs:38:17
    |
 LL |     fn into_i32(&self) {}
    |                 ^^^^^
 
 error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:39:15
+  --> $DIR/wrong_self_convention.rs:40:15
    |
 LL |     fn is_i32(self) {}
    |               ^^^^
 
 error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:41:15
+  --> $DIR/wrong_self_convention.rs:42:15
    |
 LL |     fn to_i32(self) {}
    |               ^^^^
 
 error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:43:17
+  --> $DIR/wrong_self_convention.rs:44:17
    |
 LL |     fn from_i32(self) {}
    |                 ^^^^
 
 error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:45:19
+  --> $DIR/wrong_self_convention.rs:46:19
    |
 LL |     pub fn as_i64(self) {}
    |                   ^^^^
 
 error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:46:21
+  --> $DIR/wrong_self_convention.rs:47:21
    |
 LL |     pub fn into_i64(&self) {}
    |                     ^^^^^
 
 error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:47:19
+  --> $DIR/wrong_self_convention.rs:48:19
    |
 LL |     pub fn is_i64(self) {}
    |                   ^^^^
 
 error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:48:19
+  --> $DIR/wrong_self_convention.rs:49:19
    |
 LL |     pub fn to_i64(self) {}
    |                   ^^^^
 
 error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:49:21
+  --> $DIR/wrong_self_convention.rs:50:21
    |
 LL |     pub fn from_i64(self) {}
    |                     ^^^^
index 124a9adcab91a786600cc65a4460b97a1ecce057..965b20f5202e01de368b9608fade303036e1379a 100644 (file)
@@ -2782,6 +2782,18 @@ fn run_rmake_test(&self) {
             cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1");
         }
 
+        if self.config.bless {
+            cmd.env("RUSTC_BLESS_TEST", "--bless");
+            // Assume this option is active if the environment variable is "defined", with _any_ value.
+            // As an example, a `Makefile` can use this option by:
+            //
+            //   ifdef RUSTC_BLESS_TEST
+            //       cp "$(TMPDIR)"/actual_something.ext expected_something.ext
+            //   else
+            //       $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext
+            //   endif
+        }
+
         if self.config.target.contains("msvc") && self.config.cc != "" {
             // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
             // and that `lib.exe` lives next to it.
index 2d6d73fafe2f087354f7cea37297cd81316cae98..c2a2e25d0b050d70d6a355f9b7545a991fc8783a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 2d6d73fafe2f087354f7cea37297cd81316cae98
+Subproject commit c2a2e25d0b050d70d6a355f9b7545a991fc8783a
index 51416c8ce63649789704368d414397f3f9596feb..9cfde0c232b337deb85565851fc1f28903e78e6a 100755 (executable)
@@ -275,7 +275,12 @@ def update_latest(
         return message
 
 
-def main():
+# Warning: Do not try to add a function containing the body of this try block.
+# There are variables declared within that are implicitly global; it is unknown
+# which ones precisely but at least this is true for `github_token`.
+try:
+    if __name__ != '__main__':
+        exit(0)
     repo = os.environ.get('TOOLSTATE_VALIDATE_MAINTAINERS_REPO')
     if repo:
         github_token = os.environ.get('TOOLSTATE_REPO_ACCESS_TOKEN')
@@ -342,11 +347,6 @@ def main():
         }
     ))
     response.read()
-
-
-if __name__ == '__main__':
-    try:
-        main()
-    except urllib2.HTTPError as e:
-        print("HTTPError: %s\n%s" % (e, e.read()))
-        raise
+except urllib2.HTTPError as e:
+    print("HTTPError: %s\n%s" % (e, e.read()))
+    raise
index f984e5b61a5fd5462c812f52abd275763af7c830..ccdb4524d5c5afd7cbf86463431342cf2f9c350b 100644 (file)
@@ -5,7 +5,7 @@ authors = ["Alex Crichton <alex@alexcrichton.com>"]
 edition = "2018"
 
 [dependencies]
-cargo_metadata = "0.9.1"
+cargo_metadata = "0.11"
 regex = "1"
 lazy_static = "1"
 walkdir = "2"
index 4f98944e4c8ebb267f458c2b23fdfd8b691e0953..356705305d78b1af4a45b579bb50e7c59a33c561 100644 (file)
@@ -42,6 +42,7 @@
     ("crossbeam-queue", "MIT/Apache-2.0 AND BSD-2-Clause"), // rls via rayon
     ("arrayref", "BSD-2-Clause"),       // cargo-miri/directories/.../rust-argon2 (redox)
     ("instant", "BSD-3-Clause"),        // rustc_driver/tracing-subscriber/parking_lot
+    ("snap", "BSD-3-Clause"),           // rustc
     // FIXME: this dependency violates the documentation comment above:
     ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
 ];
     "crossbeam-queue",
     "crossbeam-utils",
     "datafrog",
+    "difference",
     "digest",
     "dlmalloc",
     "either",
     "ena",
     "env_logger",
+    "expect-test",
     "fake-simd",
     "filetime",
     "flate2",
     "serde_derive",
     "sha-1",
     "smallvec",
+    "snap",
     "stable_deref_trait",
     "stacker",
     "syn",