]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #83386 - mark-i-m:stabilize-pat2015, r=nikomatsakis
authorbors <bors@rust-lang.org>
Wed, 28 Apr 2021 20:35:17 +0000 (20:35 +0000)
committerbors <bors@rust-lang.org>
Wed, 28 Apr 2021 20:35:17 +0000 (20:35 +0000)
Stabilize `:pat_param` and remove `:pat2021`

Blocked on #83384

cc `@rust-lang/lang` #79278

If I understand `@nikomatsakis` in  https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/or.20patterns/near/231133873, another FCP is not needed.

r? `@nikomatsakis`

1091 files changed:
.gitignore
.gitmodules
Cargo.lock
compiler/rustc_ast/src/ast.rs
compiler/rustc_ast/src/lib.rs
compiler/rustc_ast/src/tokenstream.rs
compiler/rustc_ast_lowering/src/item.rs
compiler/rustc_ast_passes/src/ast_validation.rs
compiler/rustc_ast_passes/src/feature_gate.rs
compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
compiler/rustc_builtin_macros/src/test_harness.rs
compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
compiler/rustc_codegen_llvm/src/builder.rs
compiler/rustc_codegen_llvm/src/context.rs
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
compiler/rustc_codegen_llvm/src/intrinsic.rs
compiler/rustc_codegen_llvm/src/llvm/ffi.rs
compiler/rustc_codegen_ssa/Cargo.toml
compiler/rustc_codegen_ssa/src/mir/rvalue.rs
compiler/rustc_codegen_ssa/src/traits/builder.rs
compiler/rustc_data_structures/Cargo.toml
compiler/rustc_data_structures/src/sso/map.rs
compiler/rustc_error_codes/src/error_codes.rs
compiler/rustc_error_codes/src/error_codes/E0137.md
compiler/rustc_error_codes/src/error_codes/E0379.md
compiler/rustc_error_codes/src/error_codes/E0404.md
compiler/rustc_error_codes/src/error_codes/E0554.md
compiler/rustc_error_codes/src/error_codes/E0723.md [deleted file]
compiler/rustc_error_codes/src/error_codes/E0754.md
compiler/rustc_error_codes/src/error_codes/E0764.md
compiler/rustc_errors/src/lib.rs
compiler/rustc_errors/src/styled_buffer.rs
compiler/rustc_expand/src/mbe.rs
compiler/rustc_expand/src/mbe/transcribe.rs
compiler/rustc_feature/src/accepted.rs
compiler/rustc_feature/src/active.rs
compiler/rustc_feature/src/builtin_attrs.rs
compiler/rustc_feature/src/removed.rs
compiler/rustc_hir/src/hir.rs
compiler/rustc_hir/src/lib.rs
compiler/rustc_hir_pretty/src/lib.rs
compiler/rustc_index/Cargo.toml
compiler/rustc_index/src/bit_set.rs
compiler/rustc_index/src/lib.rs
compiler/rustc_infer/src/infer/combine.rs
compiler/rustc_infer/src/infer/nll_relate/mod.rs
compiler/rustc_infer/src/lib.rs
compiler/rustc_interface/Cargo.toml
compiler/rustc_interface/src/tests.rs
compiler/rustc_lint/src/builtin.rs
compiler/rustc_lint/src/early.rs
compiler/rustc_lint/src/levels.rs
compiler/rustc_lint/src/non_ascii_idents.rs
compiler/rustc_lint_defs/src/builtin.rs
compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
compiler/rustc_metadata/src/creader.rs
compiler/rustc_metadata/src/locator.rs
compiler/rustc_metadata/src/rmeta/decoder.rs
compiler/rustc_metadata/src/rmeta/encoder.rs
compiler/rustc_metadata/src/rmeta/mod.rs
compiler/rustc_middle/src/dep_graph/dep_node.rs
compiler/rustc_middle/src/dep_graph/mod.rs
compiler/rustc_middle/src/lib.rs
compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
compiler/rustc_middle/src/middle/limits.rs
compiler/rustc_middle/src/middle/region.rs
compiler/rustc_middle/src/mir/interpret/error.rs
compiler/rustc_middle/src/mir/mod.rs
compiler/rustc_middle/src/mir/mono.rs
compiler/rustc_middle/src/mir/query.rs
compiler/rustc_middle/src/query/mod.rs
compiler/rustc_middle/src/traits/mod.rs
compiler/rustc_middle/src/ty/assoc.rs
compiler/rustc_middle/src/ty/closure.rs
compiler/rustc_middle/src/ty/generics.rs
compiler/rustc_middle/src/ty/trait_def.rs
compiler/rustc_middle/src/ty/util.rs
compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs
compiler/rustc_mir/src/const_eval/fn_queries.rs
compiler/rustc_mir/src/const_eval/machine.rs
compiler/rustc_mir/src/dataflow/framework/mod.rs
compiler/rustc_mir/src/interpret/operand.rs
compiler/rustc_mir/src/lib.rs
compiler/rustc_mir/src/monomorphize/collector.rs
compiler/rustc_mir/src/transform/check_consts/ops.rs
compiler/rustc_mir/src/transform/check_consts/qualifs.rs
compiler/rustc_mir/src/transform/check_consts/validation.rs
compiler/rustc_mir/src/transform/check_unsafety.rs
compiler/rustc_mir/src/transform/const_prop.rs
compiler/rustc_mir/src/transform/coverage/debug.rs
compiler/rustc_mir/src/transform/coverage/mod.rs
compiler/rustc_mir/src/transform/coverage/spans.rs
compiler/rustc_mir/src/transform/coverage/tests.rs
compiler/rustc_mir/src/util/find_self_call.rs
compiler/rustc_mir_build/src/build/expr/as_place.rs
compiler/rustc_mir_build/src/build/scope.rs
compiler/rustc_mir_build/src/lib.rs
compiler/rustc_parse/src/parser/item.rs
compiler/rustc_passes/src/check_attr.rs
compiler/rustc_passes/src/entry.rs
compiler/rustc_passes/src/lib.rs
compiler/rustc_passes/src/liveness.rs
compiler/rustc_passes/src/region.rs
compiler/rustc_privacy/src/lib.rs
compiler/rustc_query_impl/src/plumbing.rs
compiler/rustc_query_system/src/lib.rs
compiler/rustc_resolve/src/build_reduced_graph.rs
compiler/rustc_resolve/src/diagnostics.rs
compiler/rustc_resolve/src/late.rs
compiler/rustc_resolve/src/late/diagnostics.rs
compiler/rustc_resolve/src/late/lifetimes.rs
compiler/rustc_resolve/src/lib.rs
compiler/rustc_session/src/config.rs
compiler/rustc_session/src/options.rs
compiler/rustc_session/src/session.rs
compiler/rustc_span/src/hygiene.rs
compiler/rustc_span/src/lib.rs
compiler/rustc_span/src/symbol.rs
compiler/rustc_target/src/abi/call/mod.rs
compiler/rustc_target/src/lib.rs
compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs
compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
compiler/rustc_trait_selection/src/traits/object_safety.rs
compiler/rustc_trait_selection/src/traits/select/confirmation.rs
compiler/rustc_trait_selection/src/traits/select/mod.rs
compiler/rustc_ty_utils/src/lib.rs
compiler/rustc_ty_utils/src/representability.rs [new file with mode: 0644]
compiler/rustc_ty_utils/src/ty.rs
compiler/rustc_typeck/Cargo.toml
compiler/rustc_typeck/src/astconv/generics.rs
compiler/rustc_typeck/src/astconv/mod.rs
compiler/rustc_typeck/src/check/cast.rs
compiler/rustc_typeck/src/check/check.rs
compiler/rustc_typeck/src/check/coercion.rs
compiler/rustc_typeck/src/check/demand.rs
compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
compiler/rustc_typeck/src/check/gather_locals.rs
compiler/rustc_typeck/src/check/intrinsic.rs
compiler/rustc_typeck/src/check/method/probe.rs
compiler/rustc_typeck/src/check/method/suggest.rs
compiler/rustc_typeck/src/check/mod.rs
compiler/rustc_typeck/src/check/upvar.rs
compiler/rustc_typeck/src/check/wfcheck.rs
compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
compiler/rustc_typeck/src/collect.rs
compiler/rustc_typeck/src/collect/type_of.rs
compiler/rustc_typeck/src/expr_use_visitor.rs
library/alloc/src/collections/binary_heap.rs
library/alloc/src/collections/vec_deque/mod.rs
library/alloc/src/lib.rs
library/alloc/src/raw_vec.rs
library/alloc/src/rc.rs
library/alloc/src/slice.rs
library/alloc/src/task.rs
library/alloc/src/tests.rs
library/alloc/src/vec/mod.rs
library/alloc/tests/binary_heap.rs
library/alloc/tests/lib.rs
library/alloc/tests/vec_deque.rs
library/backtrace
library/core/benches/fmt.rs
library/core/src/any.rs
library/core/src/array/mod.rs
library/core/src/ascii.rs
library/core/src/cmp.rs
library/core/src/convert/num.rs
library/core/src/ffi.rs
library/core/src/fmt/builders.rs
library/core/src/fmt/mod.rs
library/core/src/hash/mod.rs
library/core/src/hint.rs
library/core/src/iter/adapters/take.rs
library/core/src/iter/sources/empty.rs
library/core/src/iter/traits/collect.rs
library/core/src/iter/traits/iterator.rs
library/core/src/lib.rs
library/core/src/mem/mod.rs
library/core/src/num/int_macros.rs
library/core/src/num/nonzero.rs
library/core/src/num/uint_macros.rs
library/core/src/ops/control_flow.rs
library/core/src/ops/index.rs
library/core/src/ops/mod.rs
library/core/src/ops/try_trait.rs [new file with mode: 0644]
library/core/src/option.rs
library/core/src/ptr/const_ptr.rs
library/core/src/ptr/mut_ptr.rs
library/core/src/raw.rs
library/core/src/result.rs
library/core/src/slice/ascii.rs
library/core/src/slice/index.rs
library/core/src/str/iter.rs
library/core/src/task/poll.rs
library/core/src/time.rs
library/core/tests/fmt/builders.rs
library/core/tests/lib.rs
library/core/tests/mem.rs
library/core/tests/slice.rs
library/std/src/collections/hash/map.rs
library/std/src/collections/hash/set.rs
library/std/src/env.rs
library/std/src/fs/tests.rs
library/std/src/io/error.rs
library/std/src/io/mod.rs
library/std/src/io/stdio.rs
library/std/src/io/util.rs
library/std/src/lib.rs
library/std/src/net/ip.rs
library/std/src/primitive_docs.rs
library/std/src/process.rs
library/std/src/rt.rs
library/std/src/sync/barrier.rs
library/std/src/sync/condvar.rs
library/std/src/sync/mod.rs
library/std/src/sync/mpsc/mod.rs
library/std/src/sync/mutex.rs
library/std/src/sync/once.rs
library/std/src/sync/poison.rs [new file with mode: 0644]
library/std/src/sync/rwlock.rs
library/std/src/sys/hermit/args.rs
library/std/src/sys/hermit/fd.rs
library/std/src/sys/hermit/fs.rs
library/std/src/sys/hermit/io.rs [deleted file]
library/std/src/sys/hermit/mod.rs
library/std/src/sys/hermit/net.rs
library/std/src/sys/hermit/os.rs
library/std/src/sys/hermit/path.rs [deleted file]
library/std/src/sys/hermit/stack_overflow.rs [deleted file]
library/std/src/sys/hermit/thread_local_key.rs [deleted file]
library/std/src/sys/mod.rs
library/std/src/sys/sgx/args.rs
library/std/src/sys/sgx/ext/io.rs
library/std/src/sys/sgx/mod.rs
library/std/src/sys/sgx/net.rs
library/std/src/sys/sgx/os.rs
library/std/src/sys/sgx/stack_overflow.rs [deleted file]
library/std/src/sys/unix/args.rs
library/std/src/sys/unix/env.rs
library/std/src/sys/unix/ext/io.rs
library/std/src/sys/unix/ext/mod.rs
library/std/src/sys/unix/ext/net/datagram.rs
library/std/src/sys/unix/ext/net/listener.rs
library/std/src/sys/unix/ext/net/raw_fd.rs
library/std/src/sys/unix/ext/net/stream.rs
library/std/src/sys/unix/ext/process.rs
library/std/src/sys/unix/fs.rs
library/std/src/sys/unix/l4re.rs
library/std/src/sys/unix/mod.rs
library/std/src/sys/unix/os.rs
library/std/src/sys/unix/process/mod.rs
library/std/src/sys/unix/process/process_vxworks.rs [new file with mode: 0644]
library/std/src/sys/unix/rand.rs
library/std/src/sys/unix/rwlock.rs
library/std/src/sys/unix/thread_local_dtor.rs
library/std/src/sys/unsupported/args.rs
library/std/src/sys/unsupported/common.rs
library/std/src/sys/unsupported/fs.rs
library/std/src/sys/unsupported/mod.rs
library/std/src/sys/unsupported/net.rs
library/std/src/sys/unsupported/os.rs
library/std/src/sys/unsupported/pipe.rs
library/std/src/sys/unsupported/process.rs
library/std/src/sys/unsupported/stack_overflow.rs [deleted file]
library/std/src/sys/unsupported/thread.rs
library/std/src/sys/vxworks/env.rs [deleted file]
library/std/src/sys/vxworks/mod.rs [deleted file]
library/std/src/sys/vxworks/process/mod.rs [deleted file]
library/std/src/sys/vxworks/process/process_vxworks.rs [deleted file]
library/std/src/sys/vxworks/rand.rs [deleted file]
library/std/src/sys/vxworks/thread_local_dtor.rs [deleted file]
library/std/src/sys/wasi/args.rs
library/std/src/sys/wasi/ext/io.rs
library/std/src/sys/wasi/mod.rs
library/std/src/sys/wasi/net.rs
library/std/src/sys/wasi/os.rs
library/std/src/sys/wasi/thread.rs
library/std/src/sys/wasm/args.rs
library/std/src/sys/wasm/mod.rs
library/std/src/sys/wasm/thread.rs
library/std/src/sys/windows/args.rs
library/std/src/sys/windows/ext/io.rs
library/std/src/sys/windows/ext/process.rs
library/std/src/sys/windows/ext/thread.rs
library/std/src/sys/windows/fs.rs
library/std/src/sys/windows/mod.rs
library/std/src/sys/windows/net.rs
library/std/src/sys/windows/stack_overflow.rs
library/std/src/sys/windows/stack_overflow_uwp.rs
library/std/src/sys_common/at_exit_imp.rs [deleted file]
library/std/src/sys_common/mod.rs
library/std/src/sys_common/poison.rs [deleted file]
library/std/src/sys_common/rt.rs [new file with mode: 0644]
library/std/src/sys_common/rwlock.rs
library/std/src/thread/local.rs
library/std/src/thread/local/tests.rs
library/std/src/thread/mod.rs
library/test/src/bench.rs
library/test/src/lib.rs
src/bootstrap/dist.rs
src/bootstrap/doc.rs
src/bootstrap/install.rs
src/bootstrap/native.rs
src/bootstrap/tarball.rs
src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.config
src/ci/pgo.sh
src/doc/rustc/src/SUMMARY.md
src/doc/rustc/src/platform-support.md
src/doc/rustc/src/target-tier-policy.md [new file with mode: 0644]
src/doc/unstable-book/src/language-features/lang-items.md
src/doc/unstable-book/src/language-features/non-ascii-idents.md [deleted file]
src/librustdoc/Cargo.toml
src/librustdoc/clean/auto_trait.rs
src/librustdoc/clean/blanket_impl.rs
src/librustdoc/clean/inline.rs
src/librustdoc/clean/mod.rs
src/librustdoc/clean/types.rs
src/librustdoc/clean/utils.rs
src/librustdoc/core.rs
src/librustdoc/doctest.rs
src/librustdoc/doctree.rs
src/librustdoc/fold.rs
src/librustdoc/formats/cache.rs
src/librustdoc/formats/item_type.rs
src/librustdoc/formats/renderer.rs
src/librustdoc/html/format.rs
src/librustdoc/html/layout.rs
src/librustdoc/html/mod.rs
src/librustdoc/html/render/cache.rs
src/librustdoc/html/render/context.rs
src/librustdoc/html/render/mod.rs
src/librustdoc/html/render/print_item.rs
src/librustdoc/html/render/write_shared.rs
src/librustdoc/html/sources.rs
src/librustdoc/html/static/main.js
src/librustdoc/html/static/rustdoc.css
src/librustdoc/html/static/search.js [new file with mode: 0644]
src/librustdoc/html/static/storage.js
src/librustdoc/html/static/themes/ayu.css
src/librustdoc/html/static/themes/dark.css
src/librustdoc/html/static/themes/light.css
src/librustdoc/html/static_files.rs
src/librustdoc/html/tests.rs [new file with mode: 0644]
src/librustdoc/json/conversions.rs
src/librustdoc/json/mod.rs
src/librustdoc/lib.rs
src/librustdoc/passes/bare_urls.rs
src/librustdoc/passes/calculate_doc_coverage.rs
src/librustdoc/passes/check_code_block_syntax.rs
src/librustdoc/passes/collect_intra_doc_links.rs
src/librustdoc/passes/doc_test_lints.rs
src/librustdoc/passes/html_tags.rs
src/librustdoc/passes/propagate_doc_cfg.rs
src/librustdoc/passes/strip_hidden.rs
src/librustdoc/visit_ast.rs
src/llvm-project
src/test/codegen/issue-84268.rs [new file with mode: 0644]
src/test/codegen/simd-intrinsic/simd-intrinsic-float-abs.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-ceil.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-cos.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-exp.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-exp2.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-floor.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-fma.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-fsqrt.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-log.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-log10.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-log2.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-pow.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-powi.rs
src/test/codegen/simd-intrinsic/simd-intrinsic-float-sin.rs
src/test/codegen/wasm_casts_nontrapping.rs [deleted file]
src/test/codegen/wasm_casts_trapping.rs
src/test/debuginfo/multi-byte-chars.rs
src/test/incremental/issue-84252-global-alloc.rs [new file with mode: 0644]
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.async.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.async2.txt [new file with mode: 0644]
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.continue.txt [new file with mode: 0644]
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-83601.txt [new file with mode: 0644]
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_crate.txt [new file with mode: 0644]
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt [new file with mode: 0644]
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.partial_eq.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.try_error_result.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt
src/test/run-make-fulldeps/coverage/async.rs
src/test/run-make-fulldeps/coverage/async2.rs [new file with mode: 0644]
src/test/run-make-fulldeps/coverage/continue.rs [new file with mode: 0644]
src/test/run-make-fulldeps/coverage/issue-83601.rs [new file with mode: 0644]
src/test/run-make-fulldeps/coverage/no_cov_crate.rs [new file with mode: 0644]
src/test/run-make-fulldeps/coverage/no_cov_func.rs [new file with mode: 0644]
src/test/run-make-fulldeps/coverage/try_error_result.rs
src/test/rustdoc-gui/hash-item-expansion.goml [new file with mode: 0644]
src/test/rustdoc-gui/impl-default-expansion.goml [new file with mode: 0644]
src/test/rustdoc-gui/lib.rs
src/test/rustdoc-gui/nojs-attr-pos.goml [deleted file]
src/test/rustdoc-ui/intra-doc/anchors.stderr
src/test/rustdoc-ui/intra-doc/double-anchor.stderr
src/test/rustdoc/assoc-types.rs
src/test/rustdoc/attributes.rs
src/test/rustdoc/auxiliary/primitive-doc.rs [new file with mode: 0644]
src/test/rustdoc/check-styled-link.rs
src/test/rustdoc/const-generics/add-impl.rs
src/test/rustdoc/cross-crate-primitive-doc.rs [new file with mode: 0644]
src/test/rustdoc/decl_macro.rs
src/test/rustdoc/default-trait-method-link.rs
src/test/rustdoc/duplicate_impls/issue-33054.rs
src/test/rustdoc/empty-impls.rs [new file with mode: 0644]
src/test/rustdoc/intra-doc-crate/self.rs
src/test/rustdoc/intra-doc/anchors.rs
src/test/rustdoc/intra-doc/associated-defaults.rs
src/test/rustdoc/intra-doc/associated-items.rs
src/test/rustdoc/intra-doc/basic.rs
src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs
src/test/rustdoc/intra-doc/cross-crate/hidden.rs
src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs
src/test/rustdoc/intra-doc/disambiguators-removed.rs
src/test/rustdoc/intra-doc/enum-struct-field.rs
src/test/rustdoc/intra-doc/extern-type.rs
src/test/rustdoc/intra-doc/issue-82209.rs
src/test/rustdoc/intra-doc/mod-ambiguity.rs
src/test/rustdoc/intra-doc/prim-precedence.rs
src/test/rustdoc/intra-doc/private.rs
src/test/rustdoc/intra-doc/proc-macro.rs
src/test/rustdoc/intra-doc/pub-use.rs
src/test/rustdoc/intra-doc/raw-ident-self.rs
src/test/rustdoc/intra-doc/reexport-additional-docs.rs
src/test/rustdoc/intra-doc/self.rs
src/test/rustdoc/intra-doc/trait-impl.rs
src/test/rustdoc/intra-doc/trait-item.rs
src/test/rustdoc/intra-link-self-cache.rs
src/test/rustdoc/issue-21474.rs
src/test/rustdoc/issue-28478.rs
src/test/rustdoc/issue-29503.rs
src/test/rustdoc/issue-45584.rs
src/test/rustdoc/issue-50159.rs
src/test/rustdoc/issue-51236.rs
src/test/rustdoc/issue-53812.rs
src/test/rustdoc/issue-54705.rs
src/test/rustdoc/issue-55321.rs
src/test/rustdoc/issue-55364.rs
src/test/rustdoc/issue-56822.rs
src/test/rustdoc/issue-60726.rs
src/test/rustdoc/issue-72340.rs
src/test/rustdoc/item-hide-threshold.rs [new file with mode: 0644]
src/test/rustdoc/link-assoc-const.rs
src/test/rustdoc/prim-title.rs [deleted file]
src/test/rustdoc/proc-macro.rs
src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs
src/test/rustdoc/reexport-stability-tags-deprecated-and-portability.rs [new file with mode: 0644]
src/test/rustdoc/reexport-stability-tags-unstable-and-portability.rs [new file with mode: 0644]
src/test/rustdoc/struct-field.rs
src/test/rustdoc/synthetic_auto/basic.rs
src/test/rustdoc/synthetic_auto/complex.rs
src/test/rustdoc/synthetic_auto/lifetimes.rs
src/test/rustdoc/synthetic_auto/manual.rs
src/test/rustdoc/synthetic_auto/negative.rs
src/test/rustdoc/synthetic_auto/nested.rs
src/test/rustdoc/synthetic_auto/no-redundancy.rs
src/test/rustdoc/synthetic_auto/project.rs
src/test/rustdoc/synthetic_auto/self-referential.rs
src/test/rustdoc/synthetic_auto/static-region.rs
src/test/rustdoc/tab_title.rs [new file with mode: 0644]
src/test/rustdoc/trait-attributes.rs
src/test/rustdoc/trait-impl-items-links-and-anchors.rs
src/test/rustdoc/trait-self-link.rs
src/test/ui/assign-imm-local-twice.rs
src/test/ui/assign-imm-local-twice.stderr
src/test/ui/associated-type-bounds/issue-79949.rs [new file with mode: 0644]
src/test/ui/associated-type-bounds/issue-81193.rs [new file with mode: 0644]
src/test/ui/associated-type-bounds/issue-83017.rs [new file with mode: 0644]
src/test/ui/associated-type-bounds/issue-83017.stderr [new file with mode: 0644]
src/test/ui/async-await/async-trait-fn.rs
src/test/ui/async-await/async-trait-fn.stderr
src/test/ui/async-await/issue-61452.stderr
src/test/ui/async-await/large_moves.rs [new file with mode: 0644]
src/test/ui/async-await/large_moves.stderr [new file with mode: 0644]
src/test/ui/attr-main-2.rs [deleted file]
src/test/ui/attr-main.rs [deleted file]
src/test/ui/attr.rs [deleted file]
src/test/ui/bad/bad-const-type.rs [deleted file]
src/test/ui/bad/bad-const-type.stderr [deleted file]
src/test/ui/bad/bad-crate-name.rs [deleted file]
src/test/ui/bad/bad-crate-name.stderr [deleted file]
src/test/ui/bad/bad-env-capture.rs [deleted file]
src/test/ui/bad/bad-env-capture.stderr [deleted file]
src/test/ui/bad/bad-env-capture2.rs [deleted file]
src/test/ui/bad/bad-env-capture2.stderr [deleted file]
src/test/ui/bad/bad-env-capture3.rs [deleted file]
src/test/ui/bad/bad-env-capture3.stderr [deleted file]
src/test/ui/bad/bad-expr-lhs.rs [deleted file]
src/test/ui/bad/bad-expr-lhs.stderr [deleted file]
src/test/ui/bad/bad-expr-path.rs [deleted file]
src/test/ui/bad/bad-expr-path.stderr [deleted file]
src/test/ui/bad/bad-expr-path2.rs [deleted file]
src/test/ui/bad/bad-expr-path2.stderr [deleted file]
src/test/ui/bad/bad-extern-link-attrs.rs [deleted file]
src/test/ui/bad/bad-extern-link-attrs.stderr [deleted file]
src/test/ui/bad/bad-intrinsic-monomorphization.rs [deleted file]
src/test/ui/bad/bad-intrinsic-monomorphization.stderr [deleted file]
src/test/ui/bad/bad-lint-cap.rs [deleted file]
src/test/ui/bad/bad-lint-cap.stderr [deleted file]
src/test/ui/bad/bad-lint-cap2.rs [deleted file]
src/test/ui/bad/bad-lint-cap2.stderr [deleted file]
src/test/ui/bad/bad-lint-cap3.rs [deleted file]
src/test/ui/bad/bad-lint-cap3.stderr [deleted file]
src/test/ui/bad/bad-main.rs [deleted file]
src/test/ui/bad/bad-main.stderr [deleted file]
src/test/ui/bad/bad-method-typaram-kind.rs [deleted file]
src/test/ui/bad/bad-method-typaram-kind.stderr [deleted file]
src/test/ui/bad/bad-mid-path-type-params.rs [deleted file]
src/test/ui/bad/bad-mid-path-type-params.stderr [deleted file]
src/test/ui/bad/bad-module.rs [deleted file]
src/test/ui/bad/bad-module.stderr [deleted file]
src/test/ui/bad/bad-sized.rs [deleted file]
src/test/ui/bad/bad-sized.stderr [deleted file]
src/test/ui/bad/bad-type-env-capture.rs [deleted file]
src/test/ui/bad/bad-type-env-capture.stderr [deleted file]
src/test/ui/borrowck/borrowck-asm.stderr
src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr
src/test/ui/borrowck/immutable-arg.stderr
src/test/ui/borrowck/issue-45199.rs
src/test/ui/borrowck/issue-45199.stderr
src/test/ui/borrowck/issue-83309-ice-immut-in-for-loop.rs [new file with mode: 0644]
src/test/ui/borrowck/issue-83309-ice-immut-in-for-loop.stderr [new file with mode: 0644]
src/test/ui/cast/cast-ptr-to-int-const.rs
src/test/ui/cast/cast-ptr-to-int-const.stderr [deleted file]
src/test/ui/cast/cast-ptr-to-int-const.with_feature.stderr [new file with mode: 0644]
src/test/ui/cast/cast-ptr-to-int-const.without_feature.stderr [new file with mode: 0644]
src/test/ui/cast/fat-ptr-cast-rpass.rs
src/test/ui/cast/issue-84213.fixed [new file with mode: 0644]
src/test/ui/cast/issue-84213.rs [new file with mode: 0644]
src/test/ui/cast/issue-84213.stderr [new file with mode: 0644]
src/test/ui/closures/issue-84128.rs [new file with mode: 0644]
src/test/ui/closures/issue-84128.stderr [new file with mode: 0644]
src/test/ui/codemap_tests/two_files.stderr
src/test/ui/codemap_tests/unicode_2.rs
src/test/ui/codemap_tests/unicode_2.stderr
src/test/ui/command-line-diagnostics.stderr
src/test/ui/const-generics/defaults/auxiliary/const_defaulty.rs
src/test/ui/const-generics/defaults/complex-generic-default-expr.full.stderr [new file with mode: 0644]
src/test/ui/const-generics/defaults/complex-generic-default-expr.min.stderr [new file with mode: 0644]
src/test/ui/const-generics/defaults/complex-generic-default-expr.rs [new file with mode: 0644]
src/test/ui/const-generics/defaults/const-default.rs
src/test/ui/const-generics/defaults/const-param-as-default-value.rs [new file with mode: 0644]
src/test/ui/const-generics/defaults/const-param-in-ty-defaults.rs [new file with mode: 0644]
src/test/ui/const-generics/defaults/default-on-impl.full.stderr [new file with mode: 0644]
src/test/ui/const-generics/defaults/default-on-impl.min.stderr [new file with mode: 0644]
src/test/ui/const-generics/defaults/default-on-impl.rs [new file with mode: 0644]
src/test/ui/const-generics/defaults/default-param-wf-concrete.rs [new file with mode: 0644]
src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr [new file with mode: 0644]
src/test/ui/const-generics/defaults/external.rs
src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr
src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr
src/test/ui/const-generics/defaults/intermixed-lifetime.rs
src/test/ui/const-generics/defaults/mismatch.full.stderr [new file with mode: 0644]
src/test/ui/const-generics/defaults/mismatch.min.stderr [new file with mode: 0644]
src/test/ui/const-generics/defaults/mismatch.rs
src/test/ui/const-generics/defaults/mismatch.stderr [deleted file]
src/test/ui/const-generics/defaults/pretty-printing-ast.rs
src/test/ui/const-generics/defaults/pretty-printing-ast.stdout
src/test/ui/const-generics/defaults/repr-c-issue-82792.rs
src/test/ui/const-generics/defaults/simple-defaults.min.stderr [deleted file]
src/test/ui/const-generics/defaults/simple-defaults.rs
src/test/ui/const-generics/defaults/type-default-const-param-name.rs [new file with mode: 0644]
src/test/ui/const-generics/defaults/wrong-order.full.stderr
src/test/ui/const-generics/defaults/wrong-order.min.stderr
src/test/ui/const-generics/defaults/wrong-order.rs
src/test/ui/const-generics/issues/issue-70273-assoc-fn.rs [new file with mode: 0644]
src/test/ui/const-generics/issues/issue-84408.rs [new file with mode: 0644]
src/test/ui/const-generics/issues/issue70273-assoc-fn.rs [deleted file]
src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr
src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr
src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs
src/test/ui/consts/cast-discriminant-zst-enum.rs
src/test/ui/consts/const-eval/issue-49296.rs
src/test/ui/consts/const-fn-not-in-trait.rs
src/test/ui/consts/const-fn.rs
src/test/ui/consts/const_discriminant.rs
src/test/ui/consts/const_fn_trait_bound.gated.stderr [new file with mode: 0644]
src/test/ui/consts/const_fn_trait_bound.rs [new file with mode: 0644]
src/test/ui/consts/const_fn_trait_bound.stock.stderr [new file with mode: 0644]
src/test/ui/consts/const_fn_unsize.gated.stderr [new file with mode: 0644]
src/test/ui/consts/const_fn_unsize.rs [new file with mode: 0644]
src/test/ui/consts/const_fn_unsize.stock.stderr [new file with mode: 0644]
src/test/ui/consts/min_const_fn/min_const_fn.stderr
src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr
src/test/ui/consts/unsizing-cast-non-null.stderr
src/test/ui/consts/unstable-const-fn-in-libcore.rs
src/test/ui/crate-loading/missing-std.rs [new file with mode: 0644]
src/test/ui/crate-loading/missing-std.stderr [new file with mode: 0644]
src/test/ui/derives/derive-macro-const-default.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/bad-expr-lhs.rs [new file with mode: 0644]
src/test/ui/destructuring-assignment/bad-expr-lhs.stderr [new file with mode: 0644]
src/test/ui/did_you_mean/pub-macro-rules.rs [new file with mode: 0644]
src/test/ui/did_you_mean/pub-macro-rules.stderr [new file with mode: 0644]
src/test/ui/error-codes/E0137.rs [deleted file]
src/test/ui/error-codes/E0137.stderr [deleted file]
src/test/ui/error-codes/E0277.stderr
src/test/ui/feature-gates/feature-gate-const_fn.rs
src/test/ui/feature-gates/feature-gate-const_fn.stderr
src/test/ui/feature-gates/feature-gate-large-assignments.rs [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-large-assignments.stderr [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-main.rs [deleted file]
src/test/ui/feature-gates/feature-gate-main.stderr [deleted file]
src/test/ui/feature-gates/feature-gate-min_const_fn.rs
src/test/ui/feature-gates/feature-gate-min_const_fn.stderr
src/test/ui/feature-gates/feature-gate-no_coverage.rs [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-no_coverage.stderr [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-non_ascii_idents.rs [deleted file]
src/test/ui/feature-gates/feature-gate-non_ascii_idents.stderr [deleted file]
src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs [deleted file]
src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr [deleted file]
src/test/ui/feature-gates/feature-gate-unsized_fn_params.stderr
src/test/ui/feature-gates/feature-gate-unsized_locals.stderr
src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs
src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr
src/test/ui/feature-gates/thread-local-const-init.rs [new file with mode: 0644]
src/test/ui/feature-gates/thread-local-const-init.stderr [new file with mode: 0644]
src/test/ui/fn/bad-main.rs [new file with mode: 0644]
src/test/ui/fn/bad-main.stderr [new file with mode: 0644]
src/test/ui/generic-associated-types/gat-in-trait-path.rs
src/test/ui/generic-associated-types/gat-in-trait-path.stderr
src/test/ui/generic-associated-types/issue-67510-pass.rs
src/test/ui/generic-associated-types/issue-67510-pass.stderr
src/test/ui/generic-associated-types/issue-70303.rs [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-70304.rs [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-70304.stderr [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-71176.rs [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-71176.stderr [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-76535.rs
src/test/ui/generic-associated-types/issue-76535.stderr
src/test/ui/generic-associated-types/issue-78671.rs [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-78671.stderr [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-79422.rs
src/test/ui/generic-associated-types/issue-79422.stderr
src/test/ui/generic-associated-types/issue-79636-1.rs [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-79636-1.stderr [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-79636-2.rs [new file with mode: 0644]
src/test/ui/generic-associated-types/issue-79636-2.stderr [new file with mode: 0644]
src/test/ui/generic-associated-types/trait-objects.rs [new file with mode: 0644]
src/test/ui/generic-associated-types/trait-objects.stderr [new file with mode: 0644]
src/test/ui/generics/bad-mid-path-type-params.rs [new file with mode: 0644]
src/test/ui/generics/bad-mid-path-type-params.stderr [new file with mode: 0644]
src/test/ui/generics/generic-function-item-where-type.rs [new file with mode: 0644]
src/test/ui/generics/generic-function-item-where-type.stderr [new file with mode: 0644]
src/test/ui/generics/generic-non-trailing-defaults.rs
src/test/ui/generics/generic-non-trailing-defaults.stderr
src/test/ui/hygiene/trait_items.stderr
src/test/ui/impl-trait/no-method-suggested-traits.stderr
src/test/ui/imports/local-modularized-tricky-fail-2.rs
src/test/ui/imports/local-modularized-tricky-fail-2.stderr
src/test/ui/imports/local-modularized-tricky-fail-3.rs [deleted file]
src/test/ui/imports/local-modularized-tricky-fail-3.stderr [deleted file]
src/test/ui/imports/local-modularized-tricky-pass-1.rs [new file with mode: 0644]
src/test/ui/imports/local-modularized-tricky-pass-2.rs [new file with mode: 0644]
src/test/ui/imports/local-modularized-tricky-pass.rs [deleted file]
src/test/ui/intrinsics/bad-intrinsic-monomorphization.rs [new file with mode: 0644]
src/test/ui/intrinsics/bad-intrinsic-monomorphization.stderr [new file with mode: 0644]
src/test/ui/intrinsics/intrinsic-alignment.rs
src/test/ui/issues/issue-43189.stderr
src/test/ui/issues/issue-44023.rs
src/test/ui/issues/issue-44023.stderr
src/test/ui/issues/issue-56175.stderr
src/test/ui/issues/issue-5883.rs
src/test/ui/issues/issue-5883.stderr
src/test/ui/iterators/array-of-ranges.rs
src/test/ui/iterators/array-of-ranges.stderr [deleted file]
src/test/ui/iterators/array.rs
src/test/ui/iterators/array.stderr [deleted file]
src/test/ui/iterators/into-iter-on-arrays-2018.rs [new file with mode: 0644]
src/test/ui/iterators/into-iter-on-arrays-2018.stderr [new file with mode: 0644]
src/test/ui/iterators/into-iter-on-arrays-2021.rs [new file with mode: 0644]
src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs [new file with mode: 0644]
src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr [new file with mode: 0644]
src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs [new file with mode: 0644]
src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr [new file with mode: 0644]
src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr
src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr
src/test/ui/linkage-attr/bad-extern-link-attrs.rs [new file with mode: 0644]
src/test/ui/linkage-attr/bad-extern-link-attrs.stderr [new file with mode: 0644]
src/test/ui/lint/bad-lint-cap.rs [new file with mode: 0644]
src/test/ui/lint/bad-lint-cap.stderr [new file with mode: 0644]
src/test/ui/lint/bad-lint-cap2.rs [new file with mode: 0644]
src/test/ui/lint/bad-lint-cap2.stderr [new file with mode: 0644]
src/test/ui/lint/bad-lint-cap3.rs [new file with mode: 0644]
src/test/ui/lint/bad-lint-cap3.stderr [new file with mode: 0644]
src/test/ui/lint/dead-code/lint-dead-code-2.rs
src/test/ui/lint/issue-83477.rs [new file with mode: 0644]
src/test/ui/lint/issue-83477.stderr [new file with mode: 0644]
src/test/ui/lint/lint-non-snake-case-no-lowercase-equivalent.rs
src/test/ui/lint/lint-nonstandard-style-unicode-1.rs
src/test/ui/lint/lint-nonstandard-style-unicode-1.stderr
src/test/ui/lint/lint-nonstandard-style-unicode-2.rs
src/test/ui/lint/lint-nonstandard-style-unicode-2.stderr
src/test/ui/lint/lint-nonstandard-style-unicode-3.rs
src/test/ui/lint/lint-nonstandard-style-unicode-3.stderr
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables-2.rs
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.rs
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.stderr
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.rs
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.stderr
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs
src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.stderr
src/test/ui/lint/special-upper-lower-cases.rs
src/test/ui/lint/special-upper-lower-cases.stderr
src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs
src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr
src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs
src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr
src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs
src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr
src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs
src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr
src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr
src/test/ui/loops/loop-no-implicit-break.rs [new file with mode: 0644]
src/test/ui/loops/loop-no-implicit-break.stderr [new file with mode: 0644]
src/test/ui/macros/issue-84195-lint-anon-const.rs [new file with mode: 0644]
src/test/ui/macros/issue-84195-lint-anon-const.stderr [new file with mode: 0644]
src/test/ui/macros/macro-export-on-modularized-macros.rs [deleted file]
src/test/ui/macros/macro-export-on-modularized-macros.stderr [deleted file]
src/test/ui/macros/pub-macro-rules-fail.rs [deleted file]
src/test/ui/macros/pub-macro-rules-fail.stderr [deleted file]
src/test/ui/macros/pub-macro-rules.rs [deleted file]
src/test/ui/main-wrong-location.rs
src/test/ui/main-wrong-location.stderr
src/test/ui/match/issue-82392.rs [new file with mode: 0644]
src/test/ui/match/issue-82392.stdout [new file with mode: 0644]
src/test/ui/match/issue-84434.rs [new file with mode: 0644]
src/test/ui/multiple-main-2.rs [deleted file]
src/test/ui/multiple-main-2.stderr [deleted file]
src/test/ui/multiple-main-3.rs [deleted file]
src/test/ui/multiple-main-3.stderr [deleted file]
src/test/ui/mut/mut-pattern-internal-mutability.stderr
src/test/ui/never_type/defaulted-never-note.rs
src/test/ui/never_type/defaulted-never-note.stderr
src/test/ui/parser/bad-crate-name.rs [new file with mode: 0644]
src/test/ui/parser/bad-crate-name.stderr [new file with mode: 0644]
src/test/ui/parser/issue-48508.rs
src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs
src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr
src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr
src/test/ui/privacy/privacy-ns1.stderr
src/test/ui/privacy/privacy-ns2.stderr
src/test/ui/reify-intrinsic.rs
src/test/ui/reify-intrinsic.stderr
src/test/ui/resolve/auxiliary/issue-80079.rs [new file with mode: 0644]
src/test/ui/resolve/bad-env-capture.rs [new file with mode: 0644]
src/test/ui/resolve/bad-env-capture.stderr [new file with mode: 0644]
src/test/ui/resolve/bad-env-capture2.rs [new file with mode: 0644]
src/test/ui/resolve/bad-env-capture2.stderr [new file with mode: 0644]
src/test/ui/resolve/bad-env-capture3.rs [new file with mode: 0644]
src/test/ui/resolve/bad-env-capture3.stderr [new file with mode: 0644]
src/test/ui/resolve/bad-expr-path.rs [new file with mode: 0644]
src/test/ui/resolve/bad-expr-path.stderr [new file with mode: 0644]
src/test/ui/resolve/bad-expr-path2.rs [new file with mode: 0644]
src/test/ui/resolve/bad-expr-path2.stderr [new file with mode: 0644]
src/test/ui/resolve/bad-module.rs [new file with mode: 0644]
src/test/ui/resolve/bad-module.stderr [new file with mode: 0644]
src/test/ui/resolve/bad-type-env-capture.rs [new file with mode: 0644]
src/test/ui/resolve/bad-type-env-capture.stderr [new file with mode: 0644]
src/test/ui/resolve/issue-3907.stderr
src/test/ui/resolve/issue-5035-2.stderr
src/test/ui/resolve/issue-5035.stderr
src/test/ui/resolve/issue-80079.rs [new file with mode: 0644]
src/test/ui/resolve/issue-80079.stderr [new file with mode: 0644]
src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr
src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs
src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr
src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs
src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr
src/test/ui/rfc-2457/extern_block_nonascii_forbidden.rs
src/test/ui/rfc-2457/extern_block_nonascii_forbidden.stderr
src/test/ui/rfc-2457/idents-normalized.rs
src/test/ui/rfc-2457/mod_file_nonascii_forbidden.rs
src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr
src/test/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs
src/test/ui/rfc-2457/mod_inline_nonascii_allowed.rs
src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.rs
src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr
src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs
src/test/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs
src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs
src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs
src/test/ui/rfc-2632-const-trait-impl/stability.stderr
src/test/ui/rust-2018/trait-import-suggestions.stderr
src/test/ui/sanitize/address.rs
src/test/ui/sanitize/hwaddress.rs
src/test/ui/sanitize/leak.rs
src/test/ui/sanitize/memory.rs
src/test/ui/shadowed/shadowed-trait-methods.stderr
src/test/ui/simd/simd-intrinsic-float-math.rs
src/test/ui/specialization/issue-33017.rs [new file with mode: 0644]
src/test/ui/specialization/issue-33017.stderr [new file with mode: 0644]
src/test/ui/specialization/issue-51892.rs [new file with mode: 0644]
src/test/ui/specialization/issue-51892.stderr [new file with mode: 0644]
src/test/ui/static/bad-const-type.rs [new file with mode: 0644]
src/test/ui/static/bad-const-type.stderr [new file with mode: 0644]
src/test/ui/suggestions/as-ref-2.fixed [new file with mode: 0644]
src/test/ui/suggestions/as-ref-2.rs [new file with mode: 0644]
src/test/ui/suggestions/as-ref-2.stderr [new file with mode: 0644]
src/test/ui/suggestions/as-ref.rs
src/test/ui/suggestions/as-ref.stderr
src/test/ui/suggestions/import-trait-for-method-call.rs [new file with mode: 0644]
src/test/ui/suggestions/import-trait-for-method-call.stderr [new file with mode: 0644]
src/test/ui/suggestions/path-by-value.stderr
src/test/ui/test-attrs/test-main-not-dead-attr.rs
src/test/ui/test-attrs/test-runner-hides-buried-main.rs
src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed [new file with mode: 0644]
src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs [new file with mode: 0644]
src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr [new file with mode: 0644]
src/test/ui/traits/bad-method-typaram-kind.rs [new file with mode: 0644]
src/test/ui/traits/bad-method-typaram-kind.stderr [new file with mode: 0644]
src/test/ui/traits/bad-sized.rs [new file with mode: 0644]
src/test/ui/traits/bad-sized.stderr [new file with mode: 0644]
src/test/ui/traits/bound/not-on-bare-trait.stderr
src/test/ui/traits/item-privacy.stderr
src/test/ui/typeof/type_mismatch.rs [new file with mode: 0644]
src/test/ui/typeof/type_mismatch.stderr [new file with mode: 0644]
src/test/ui/unsized-tuple-impls.rs [deleted file]
src/test/ui/unsized.rs [deleted file]
src/test/ui/unsized/unsized-fn-arg.fixed [new file with mode: 0644]
src/test/ui/unsized/unsized-fn-arg.rs [new file with mode: 0644]
src/test/ui/unsized/unsized-fn-arg.stderr [new file with mode: 0644]
src/test/ui/unsized/unsized-tuple-impls.rs [new file with mode: 0644]
src/test/ui/unsized/unsized.rs [new file with mode: 0644]
src/test/ui/unsized/unsized2.rs [new file with mode: 0644]
src/test/ui/unsized/unsized3-rpass.rs [new file with mode: 0644]
src/test/ui/unsized/unsized3.rs [new file with mode: 0644]
src/test/ui/unsized/unsized3.stderr [new file with mode: 0644]
src/test/ui/unsized/unsized5.rs [new file with mode: 0644]
src/test/ui/unsized/unsized5.stderr [new file with mode: 0644]
src/test/ui/unsized/unsized6.rs [new file with mode: 0644]
src/test/ui/unsized/unsized6.stderr [new file with mode: 0644]
src/test/ui/unsized/unsized7.rs [new file with mode: 0644]
src/test/ui/unsized/unsized7.stderr [new file with mode: 0644]
src/test/ui/unsized2.rs [deleted file]
src/test/ui/unsized3-rpass.rs [deleted file]
src/test/ui/unsized3.rs [deleted file]
src/test/ui/unsized3.stderr [deleted file]
src/test/ui/unsized5.rs [deleted file]
src/test/ui/unsized5.stderr [deleted file]
src/test/ui/unsized6.rs [deleted file]
src/test/ui/unsized6.stderr [deleted file]
src/test/ui/unsized7.rs [deleted file]
src/test/ui/unsized7.stderr [deleted file]
src/test/ui/utf8_idents-rpass.rs
src/test/ui/utf8_idents.rs
src/test/ui/utf8_idents.stderr [deleted file]
src/tools/cargo
src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/booleans.rs
src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
src/tools/clippy/clippy_lints/src/checked_conversions.rs
src/tools/clippy/clippy_lints/src/collapsible_match.rs
src/tools/clippy/clippy_lints/src/comparison_chain.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/deprecated_lints.rs
src/tools/clippy/clippy_lints/src/entry.rs
src/tools/clippy/clippy_lints/src/excessive_bools.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/formatting.rs
src/tools/clippy/clippy_lints/src/from_over_into.rs
src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
src/tools/clippy/clippy_lints/src/implicit_hasher.rs
src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
src/tools/clippy/clippy_lints/src/infinite_iter.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs
src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
src/tools/clippy/clippy_lints/src/loops/mod.rs
src/tools/clippy/clippy_lints/src/loops/never_loop.rs
src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
src/tools/clippy/clippy_lints/src/macro_use.rs
src/tools/clippy/clippy_lints/src/manual_map.rs
src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
src/tools/clippy/clippy_lints/src/manual_ok_or.rs
src/tools/clippy/clippy_lints/src/manual_strip.rs
src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
src/tools/clippy/clippy_lints/src/map_identity.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/mem_replace.rs
src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs [deleted file]
src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs [deleted file]
src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs [deleted file]
src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
src/tools/clippy/clippy_lints/src/methods/utils.rs
src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
src/tools/clippy/clippy_lints/src/needless_bool.rs
src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
src/tools/clippy/clippy_lints/src/needless_question_mark.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/redundant_field_names.rs
src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
src/tools/clippy/clippy_lints/src/to_string_in_display.rs
src/tools/clippy/clippy_lints/src/transmute/mod.rs
src/tools/clippy/clippy_lints/src/transmuting_null.rs
src/tools/clippy/clippy_lints/src/try_err.rs
src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
src/tools/clippy/clippy_lints/src/unused_io_amount.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/src/ast_utils.rs
src/tools/clippy/clippy_utils/src/attrs.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/msrvs.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
src/tools/clippy/tests/ui/asm_syntax.rs
src/tools/clippy/tests/ui/asm_syntax.stderr
src/tools/clippy/tests/ui/bool_assert_comparison.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/bool_assert_comparison.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/cloned_instead_of_copied.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-5835.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-5835.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-6251.stderr
src/tools/clippy/tests/ui/crashes/ice-7126.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
src/tools/clippy/tests/ui/deprecated.rs
src/tools/clippy/tests/ui/deprecated.stderr
src/tools/clippy/tests/ui/entry.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/entry.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/entry.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/entry_fixable.fixed [deleted file]
src/tools/clippy/tests/ui/entry_fixable.rs [deleted file]
src/tools/clippy/tests/ui/entry_fixable.stderr [deleted file]
src/tools/clippy/tests/ui/entry_unfixable.rs [deleted file]
src/tools/clippy/tests/ui/entry_unfixable.stderr [deleted file]
src/tools/clippy/tests/ui/entry_with_else.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/entry_with_else.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/entry_with_else.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/filter_methods.rs [deleted file]
src/tools/clippy/tests/ui/filter_methods.stderr [deleted file]
src/tools/clippy/tests/ui/flat_map_option.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/flat_map_option.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/flat_map_option.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/for_loop_fixable.fixed
src/tools/clippy/tests/ui/for_loop_fixable.rs
src/tools/clippy/tests/ui/format.fixed
src/tools/clippy/tests/ui/format.rs
src/tools/clippy/tests/ui/format.stderr
src/tools/clippy/tests/ui/from_over_into.stderr
src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_cloned_collect.fixed
src/tools/clippy/tests/ui/iter_cloned_collect.rs
src/tools/clippy/tests/ui/iter_cloned_collect.stderr
src/tools/clippy/tests/ui/macro_use_imports.fixed
src/tools/clippy/tests/ui/macro_use_imports.rs
src/tools/clippy/tests/ui/manual_map_option.fixed
src/tools/clippy/tests/ui/manual_map_option.rs
src/tools/clippy/tests/ui/manual_unwrap_or.fixed
src/tools/clippy/tests/ui/manual_unwrap_or.rs
src/tools/clippy/tests/ui/manual_unwrap_or.stderr
src/tools/clippy/tests/ui/min_rust_version_attr.rs
src/tools/clippy/tests/ui/min_rust_version_attr.stderr
src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
src/tools/clippy/tests/ui/needless_question_mark.fixed
src/tools/clippy/tests/ui/needless_question_mark.rs
src/tools/clippy/tests/ui/needless_question_mark.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/panic_in_result_fn_debug_assertions.rs
src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr [deleted file]
src/tools/clippy/tests/ui/panicking_macros.rs
src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
src/tools/clippy/tests/ui/repl_uninit.rs
src/tools/clippy/tests/ui/single_component_path_imports.fixed
src/tools/clippy/tests/ui/single_component_path_imports.rs
src/tools/clippy/tests/ui/single_component_path_imports.stderr
src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/single_component_path_imports_macro.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/single_element_loop.fixed
src/tools/clippy/tests/ui/single_element_loop.rs
src/tools/clippy/tests/ui/single_element_loop.stderr
src/tools/clippy/tests/ui/single_match.rs
src/tools/clippy/tests/ui/single_match.stderr
src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
src/tools/clippy/tests/ui/string_lit_as_bytes.rs
src/tools/clippy/tests/ui/string_lit_as_bytes.stderr
src/tools/clippy/tests/ui/suspicious_else_formatting.rs
src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
src/tools/clippy/tests/ui/try_err.fixed
src/tools/clippy/tests/ui/try_err.rs
src/tools/clippy/tests/ui/try_err.stderr
src/tools/clippy/tests/ui/uninit.rs
src/tools/clippy/tests/ui/uninit.stderr
src/tools/clippy/tests/ui/unnecessary_self_imports.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/unnecessary_self_imports.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/unnecessary_self_imports.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/unused_io_amount.rs
src/tools/clippy/tests/ui/unused_io_amount.stderr
src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed
src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs
src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr
src/tools/clippy/tests/ui/wrong_self_convention2.rs
src/tools/clippy/util/gh-pages/index.html
src/tools/compiletest/src/header.rs
src/tools/compiletest/src/header/tests.rs
src/tools/compiletest/src/main.rs
src/tools/miri
src/tools/rls
src/tools/rust-analyzer
src/tools/rustc-workspace-hack/Cargo.toml
src/tools/rustdoc-js/tester.js
src/tools/tidy/src/deps.rs
src/tools/tidy/src/ui_tests.rs

index 3f77e6884b90f0796b59c3ca3a438d3339bead1f..0cd6b9f648d0fb4bfd0189a66969c39c58e55a40 100644 (file)
@@ -5,53 +5,71 @@
 # created during manual debugging and many people like to clean up instead of
 # having git ignore such leftovers. You can use `.git/info/exclude` to
 # configure your local ignore list.
-# FIXME: This needs cleanup.
-*~
+
+## File system
+.DS_Store
+desktop.ini
+
+## Editor
 *.swp
 *.swo
-.#*
-.DS_Store
+Session.vim
 .cproject
-.hg/
-.hgignore
 .idea
 *.iml
-__pycache__/
-*.py[cod]
-*$py.class
+.vscode
 .project
+.favorites.json
 .settings/
+
+## Tool
 .valgrindrc
-.vscode
-.favorites.json
-/Makefile
-/build/
+.cargo
+# Included because it is part of the test case
+!/src/test/run-make/thumb-none-qemu/example/.cargo
+
+## Configuration
 /config.toml
-/dist/
+/Makefile
+config.mk
+config.stamp
+no_llvm_build
+
+## Build
 /dl/
 /doc/
 /inst/
 /llvm/
 /mingw-build/
-/src/tools/x/target
-# Created by default with `src/ci/docker/run.sh`:
-/obj/
+/build/
+/dist/
 /unicode-downloads
 /target
-# Generated by compiletest for incremental:
+/src/tools/x/target
+# Generated by compiletest for incremental
 /tmp/
+# Created by default with `src/ci/docker/run.sh`
+/obj/
+
+## Temporary files
+*~
+\#*
+\#*\#
+.#*
+
+## Tags
 tags
 tags.*
 TAGS
 TAGS.*
-\#*
-\#*\#
-config.mk
-config.stamp
-Session.vim
-.cargo
-!/src/test/run-make/thumb-none-qemu/example/.cargo
-no_llvm_build
+
+## Python
+__pycache__/
+*.py[cod]
+*$py.class
+
+## Node
 **node_modules
 **package-lock.json
+
 # Before adding new lines, see the comment at the top.
index 8618f61f04acdc8042dd8e06c8494eed56ee9389..d4a80efc277e31627c6c31d09acb378a54a499cf 100644 (file)
@@ -37,7 +37,7 @@
 [submodule "src/llvm-project"]
        path = src/llvm-project
        url = https://github.com/rust-lang/llvm-project.git
-       branch = rustc/12.0-2021-02-03
+       branch = rustc/12.0-2021-04-15
 [submodule "src/doc/embedded-book"]
        path = src/doc/embedded-book
        url = https://github.com/rust-embedded/book.git
index 8fec4bf128f7ea0a74cccd5baac84cf79eaecec4..b3fa511839b1d3cda830d93edc0cb62d15d80db9 100644 (file)
@@ -104,16 +104,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271"
 
 [[package]]
-name = "arrayref"
-version = "0.3.6"
+name = "arrayvec"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
 
 [[package]]
 name = "arrayvec"
-version = "0.5.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7"
 
 [[package]]
 name = "atty"
@@ -132,12 +132,6 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
 
-[[package]]
-name = "base64"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
-
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -153,17 +147,6 @@ dependencies = [
  "typenum",
 ]
 
-[[package]]
-name = "blake2b_simd"
-version = "0.5.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
-dependencies = [
- "arrayref",
- "arrayvec",
- "constant_time_eq",
-]
-
 [[package]]
 name = "block-buffer"
 version = "0.7.3"
@@ -292,7 +275,7 @@ dependencies = [
  "cargo-util",
  "clap",
  "crates-io",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils 0.8.3",
  "curl",
  "curl-sys",
  "env_logger 0.8.1",
@@ -723,18 +706,6 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
-[[package]]
-name = "const_fn"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
-
-[[package]]
-name = "constant_time_eq"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
-
 [[package]]
 name = "core"
 version = "0.0.0"
@@ -796,7 +767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
 dependencies = [
  "cfg-if 1.0.0",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils 0.8.3",
 ]
 
 [[package]]
@@ -849,13 +820,12 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.0"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
+checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
 dependencies = [
  "autocfg",
  "cfg-if 1.0.0",
- "const_fn",
  "lazy_static",
 ]
 
@@ -982,9 +952,9 @@ dependencies = [
 
 [[package]]
 name = "directories"
-version = "3.0.1"
+version = "3.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8fed639d60b58d0f53498ab13d26f621fd77569cc6edb031f4cc36a2ad9da0f"
+checksum = "e69600ff1703123957937708eb27f7a564e48885c537782722ed0ba3189ce1d7"
 dependencies = [
  "dirs-sys",
 ]
@@ -1011,12 +981,12 @@ dependencies = [
 
 [[package]]
 name = "dirs-sys"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
+checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
 dependencies = [
  "libc",
- "redox_users 0.3.4",
+ "redox_users",
  "winapi 0.3.9",
 ]
 
@@ -1027,7 +997,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
 dependencies = [
  "libc",
- "redox_users 0.4.0",
+ "redox_users",
  "winapi 0.3.9",
 ]
 
@@ -1239,12 +1209,9 @@ checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
 
 [[package]]
 name = "fst"
-version = "0.3.5"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6"
-dependencies = [
- "byteorder",
-]
+checksum = "d79238883cf0307100b90aba4a755d8051a3182305dfe7f649a1e9dc0517006f"
 
 [[package]]
 name = "fuchsia-zircon"
@@ -1646,11 +1613,11 @@ checksum = "c3360c7b59e5ffa2653671fb74b4741a5d343c03f331c0a4aeda42b5c2b0ec7d"
 
 [[package]]
 name = "ignore"
-version = "0.4.16"
+version = "0.4.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22dcbf2a4a289528dbef21686354904e1c694ac642610a9bff9e7df730d9ec72"
+checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c"
 dependencies = [
- "crossbeam-utils 0.7.2",
+ "crossbeam-utils 0.8.3",
  "globset",
  "lazy_static",
  "log",
@@ -1744,18 +1711,18 @@ checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
 
 [[package]]
 name = "jobserver"
-version = "0.1.21"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
+checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "json"
-version = "0.11.15"
+version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92c245af8786f6ac35f95ca14feca9119e71339aaab41e878e7cdd655c97e9e5"
+checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
 
 [[package]]
 name = "jsondocck"
@@ -2165,7 +2132,7 @@ dependencies = [
  "parking_lot",
  "perf-event-open-sys",
  "rustc-hash",
- "smallvec 1.6.1",
+ "smallvec",
 ]
 
 [[package]]
@@ -2324,7 +2291,7 @@ dependencies = [
  "rustc-workspace-hack",
  "rustc_version",
  "shell-escape",
- "smallvec 1.6.1",
+ "smallvec",
 ]
 
 [[package]]
@@ -2548,7 +2515,7 @@ dependencies = [
  "instant",
  "libc",
  "redox_syscall 0.2.5",
- "smallvec 1.6.1",
+ "smallvec",
  "winapi 0.3.9",
 ]
 
@@ -3046,17 +3013,6 @@ dependencies = [
  "bitflags",
 ]
 
-[[package]]
-name = "redox_users"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
-dependencies = [
- "getrandom 0.1.14",
- "redox_syscall 0.1.57",
- "rust-argon2",
-]
-
 [[package]]
 name = "redox_users"
 version = "0.4.0"
@@ -3127,7 +3083,7 @@ dependencies = [
  "futures 0.3.12",
  "heck",
  "home",
- "itertools 0.8.2",
+ "itertools 0.9.0",
  "jsonrpc-core",
  "lazy_static",
  "log",
@@ -3162,14 +3118,14 @@ dependencies = [
 
 [[package]]
 name = "rls-analysis"
-version = "0.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534032993e1b60e5db934eab2dde54da7afd1e46c3465fddb2b29eb47cb1ed3a"
+version = "0.18.2"
 dependencies = [
  "derive-new",
+ "env_logger 0.7.1",
  "fst",
- "itertools 0.8.2",
+ "itertools 0.9.0",
  "json",
+ "lazy_static",
  "log",
  "rls-data",
  "rls-span",
@@ -3233,18 +3189,6 @@ dependencies = [
  "rls-span",
 ]
 
-[[package]]
-name = "rust-argon2"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
-dependencies = [
- "base64",
- "blake2b_simd",
- "constant_time_eq",
- "crossbeam-utils 0.7.2",
-]
-
 [[package]]
 name = "rust-demangler"
 version = "0.0.1"
@@ -3269,7 +3213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "259cca0e975ecb05fd289ace45280c30ff792efc04e856a7f18b7fc86a3cb610"
 dependencies = [
  "rustc-ap-rustc_data_structures",
- "smallvec 1.6.1",
+ "smallvec",
 ]
 
 [[package]]
@@ -3285,7 +3229,7 @@ dependencies = [
  "rustc-ap-rustc_macros",
  "rustc-ap-rustc_serialize",
  "rustc-ap-rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -3343,7 +3287,7 @@ version = "712.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9cbfa7f82517a1b2efe7106c864c3f930b1da8aff07a27fd317af2f36522fd2e"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.5.2",
  "bitflags",
  "cfg-if 0.1.10",
  "crossbeam-utils 0.7.2",
@@ -3360,7 +3304,7 @@ dependencies = [
  "rustc-hash",
  "rustc-rayon",
  "rustc-rayon-core",
- "smallvec 1.6.1",
+ "smallvec",
  "stable_deref_trait",
  "stacker",
  "tempfile",
@@ -3408,7 +3352,7 @@ dependencies = [
  "rustc-ap-rustc_serialize",
  "rustc-ap-rustc_session",
  "rustc-ap-rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -3440,7 +3384,7 @@ version = "712.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f6f53afc4f7111c82295cb7ea3878f520bbac6a2c5a12e125b4ca9156498cff"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.5.2",
  "rustc-ap-rustc_macros",
  "rustc-ap-rustc_serialize",
 ]
@@ -3496,7 +3440,7 @@ dependencies = [
  "rustc-ap-rustc_lexer",
  "rustc-ap-rustc_session",
  "rustc-ap-rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
  "unicode-normalization",
 ]
@@ -3508,7 +3452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d33c710120953c0214f47a6caf42064d7e241003b4af36c98a6d6156e70335f1"
 dependencies = [
  "indexmap",
- "smallvec 1.6.1",
+ "smallvec",
 ]
 
 [[package]]
@@ -3650,15 +3594,15 @@ name = "rustc-workspace-hack"
 version = "1.0.0"
 dependencies = [
  "byteorder",
- "crossbeam-utils 0.7.2",
+ "crossbeam-utils 0.8.3",
  "libc",
  "libz-sys",
  "proc-macro2",
  "quote",
+ "rand_core 0.5.1",
  "serde",
  "serde_json",
- "smallvec 0.6.14",
- "smallvec 1.6.1",
+ "smallvec",
  "syn",
  "url 2.1.1",
  "winapi 0.3.9",
@@ -3669,7 +3613,7 @@ name = "rustc_apfloat"
 version = "0.0.0"
 dependencies = [
  "bitflags",
- "smallvec 1.6.1",
+ "smallvec",
 ]
 
 [[package]]
@@ -3677,7 +3621,7 @@ name = "rustc_arena"
 version = "0.0.0"
 dependencies = [
  "rustc_data_structures",
- "smallvec 1.6.1",
+ "smallvec",
 ]
 
 [[package]]
@@ -3691,7 +3635,7 @@ dependencies = [
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -3709,7 +3653,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -3772,7 +3716,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -3801,7 +3745,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
  "snap",
  "tracing",
 ]
@@ -3840,10 +3784,10 @@ dependencies = [
 name = "rustc_data_structures"
 version = "0.0.0"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.7.0",
  "bitflags",
  "cfg-if 0.1.10",
- "crossbeam-utils 0.7.2",
+ "crossbeam-utils 0.8.3",
  "ena",
  "indexmap",
  "jobserver",
@@ -3858,7 +3802,7 @@ dependencies = [
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
- "smallvec 1.6.1",
+ "smallvec",
  "stable_deref_trait",
  "stacker",
  "tempfile",
@@ -3941,7 +3885,7 @@ dependencies = [
  "rustc_serialize",
  "rustc_session",
  "rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -3973,7 +3917,7 @@ dependencies = [
  "rustc_serialize",
  "rustc_span",
  "rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4010,7 +3954,7 @@ dependencies = [
 name = "rustc_index"
 version = "0.0.0"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.7.0",
  "rustc_macros",
  "rustc_serialize",
 ]
@@ -4031,7 +3975,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4075,7 +4019,7 @@ dependencies = [
  "rustc_traits",
  "rustc_ty_utils",
  "rustc_typeck",
- "smallvec 1.6.1",
+ "smallvec",
  "tempfile",
  "tracing",
  "winapi 0.3.9",
@@ -4164,7 +4108,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
  "snap",
  "stable_deref_trait",
  "tracing",
@@ -4196,7 +4140,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_type_ir",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4227,7 +4171,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4250,7 +4194,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4267,7 +4211,7 @@ dependencies = [
  "rustc_lexer",
  "rustc_session",
  "rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
  "unicode-normalization",
 ]
@@ -4368,7 +4312,7 @@ dependencies = [
  "rustc_serialize",
  "rustc_session",
  "rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4392,7 +4336,7 @@ dependencies = [
  "rustc_middle",
  "rustc_session",
  "rustc_span",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4421,7 +4365,7 @@ version = "0.0.0"
 dependencies = [
  "indexmap",
  "rustc_macros",
- "smallvec 1.6.1",
+ "smallvec",
 ]
 
 [[package]]
@@ -4518,7 +4462,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4538,7 +4482,7 @@ dependencies = [
  "rustc_middle",
  "rustc_span",
  "rustc_trait_selection",
- "smallvec 1.6.1",
+ "smallvec",
  "tracing",
 ]
 
@@ -4588,7 +4532,8 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "smallvec 1.6.1",
+ "rustc_ty_utils",
+ "smallvec",
  "tracing",
 ]
 
@@ -4605,7 +4550,7 @@ dependencies = [
 name = "rustdoc"
 version = "0.0.0"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.7.0",
  "expect-test",
  "itertools 0.9.0",
  "minifier",
@@ -4615,7 +4560,7 @@ dependencies = [
  "rustdoc-json-types",
  "serde",
  "serde_json",
- "smallvec 1.6.1",
+ "smallvec",
  "tempfile",
  "tracing",
  "tracing-subscriber",
@@ -4961,15 +4906,6 @@ version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
 
-[[package]]
-name = "smallvec"
-version = "0.6.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
-dependencies = [
- "maybe-uninit",
-]
-
 [[package]]
 name = "smallvec"
 version = "1.6.1"
@@ -5169,14 +5105,14 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.1.0"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "libc",
- "rand 0.7.3",
- "redox_syscall 0.1.57",
+ "rand 0.8.3",
+ "redox_syscall 0.2.5",
  "remove_dir_all",
  "winapi 0.3.9",
 ]
@@ -5311,7 +5247,7 @@ name = "tidy"
 version = "0.1.0"
 dependencies = [
  "cargo_metadata 0.11.1",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils 0.8.3",
  "lazy_static",
  "regex",
  "walkdir",
@@ -5489,7 +5425,7 @@ dependencies = [
  "serde",
  "serde_json",
  "sharded-slab",
- "smallvec 1.6.1",
+ "smallvec",
  "thread_local",
  "tracing",
  "tracing-core",
index da9accd18391afe0b8a042a763394be164b40a73..e7f19f06ebef5c07197d45a2213c2f5722dcc4fc 100644 (file)
@@ -100,6 +100,7 @@ pub struct Path {
 }
 
 impl PartialEq<Symbol> for Path {
+    #[inline]
     fn eq(&self, symbol: &Symbol) -> bool {
         self.segments.len() == 1 && { self.segments[0].ident.name == *symbol }
     }
@@ -1345,7 +1346,7 @@ pub enum ExprKind {
     Field(P<Expr>, Ident),
     /// An indexing operation (e.g., `foo[2]`).
     Index(P<Expr>, P<Expr>),
-    /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment).
+    /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assignment).
     Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
     /// An underscore, used in destructuring assignment to ignore a value.
     Underscore,
index 1e6da044ec03969c4e05353829e831cee5788ec3..ffec28a395fdb15fdc6131d8b462df5e9f4e6ce9 100644 (file)
@@ -10,7 +10,8 @@
 )]
 #![feature(box_syntax)]
 #![feature(box_patterns)]
-#![feature(const_fn)] // For the `transmute` in `P::new`
+#![cfg_attr(bootstrap, feature(const_fn))] // For the `transmute` in `P::new`
+#![cfg_attr(not(bootstrap), feature(const_fn_unsize))] // For the `transmute` in `P::new`
 #![feature(const_fn_transmute)]
 #![feature(const_panic)]
 #![feature(crate_visibility_modifier)]
index 8318b242cae94462e8415e4b11186aa61563f1e8..2d463a4588c561ecd8e1df5da5eca65000a10b5e 100644 (file)
@@ -301,7 +301,7 @@ pub fn to_tokenstream(&self) -> TokenStream {
 /// tokens.
 ///
 /// For example, `#[cfg(FALSE)] struct Foo {}` would
-/// have an `attrs` field contaiing the `#[cfg(FALSE)]` attr,
+/// have an `attrs` field containing the `#[cfg(FALSE)]` attr,
 /// and a `tokens` field storing the (unparesd) tokens `struct Foo {}`
 #[derive(Clone, Debug, Encodable, Decodable)]
 pub struct AttributesData {
index ea01632d75d6a975d19eea985306df7af3d101e8..5fd8f7eb33a1fa882fdc068c3397cdaf0024dec6 100644 (file)
@@ -836,9 +836,17 @@ fn lower_trait_item(&mut self, i: &AssocItem) -> hir::TraitItem<'hir> {
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)))
             }
             AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, Some(ref body))) => {
-                let body_id = self.lower_fn_body_block(i.span, &sig.decl, Some(body));
-                let (generics, sig) =
-                    self.lower_method_sig(generics, sig, trait_item_def_id, false, None, i.id);
+                let asyncness = sig.header.asyncness;
+                let body_id =
+                    self.lower_maybe_async_body(i.span, &sig.decl, asyncness, Some(&body));
+                let (generics, sig) = self.lower_method_sig(
+                    generics,
+                    sig,
+                    trait_item_def_id,
+                    false,
+                    asyncness.opt_return_id(),
+                    i.id,
+                );
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)))
             }
             AssocItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref default)) => {
index bb09f701531cf196ce6305986d5cdf22ecff2df8..6d6438920c09b9d04e3c76dbe02ed359947fd51f 100644 (file)
@@ -754,7 +754,7 @@ fn validate_generic_param_order(
             GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident),
             GenericParamKind::Const { ref ty, kw_span: _, default: _ } => {
                 let ty = pprust::ty_to_string(ty);
-                let unordered = sess.features_untracked().const_generics;
+                let unordered = sess.features_untracked().unordered_const_ty_params();
                 (ParamKindOrd::Const { unordered }, Some(format!("const {}: {}", param.ident, ty)))
             }
         };
@@ -1213,8 +1213,41 @@ fn visit_generics(&mut self, generics: &'a Generics) {
                 deny_equality_constraints(self, predicate, generics);
             }
         }
-
-        visit::walk_generics(self, generics)
+        walk_list!(self, visit_generic_param, &generics.params);
+        for predicate in &generics.where_clause.predicates {
+            match predicate {
+                WherePredicate::BoundPredicate(bound_pred) => {
+                    // A type binding, eg `for<'c> Foo: Send+Clone+'c`
+                    self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
+
+                    // This is slightly complicated. Our representation for poly-trait-refs contains a single
+                    // binder and thus we only allow a single level of quantification. However,
+                    // the syntax of Rust permits quantification in two places in where clauses,
+                    // e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. If both are
+                    // defined, then error.
+                    if !bound_pred.bound_generic_params.is_empty() {
+                        for bound in &bound_pred.bounds {
+                            match bound {
+                                GenericBound::Trait(t, _) => {
+                                    if !t.bound_generic_params.is_empty() {
+                                        struct_span_err!(
+                                            self.err_handler(),
+                                            t.span,
+                                            E0316,
+                                            "nested quantification of lifetimes"
+                                        )
+                                        .emit();
+                                    }
+                                }
+                                GenericBound::Outlives(_) => {}
+                            }
+                        }
+                    }
+                }
+                _ => {}
+            }
+            self.visit_where_predicate(predicate);
+        }
     }
 
     fn visit_generic_param(&mut self, param: &'a GenericParam) {
@@ -1263,14 +1296,6 @@ fn visit_pat(&mut self, pat: &'a Pat) {
         visit::walk_pat(self, pat)
     }
 
-    fn visit_where_predicate(&mut self, p: &'a WherePredicate) {
-        if let &WherePredicate::BoundPredicate(ref bound_predicate) = p {
-            // A type binding, eg `for<'c> Foo: Send+Clone+'c`
-            self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params);
-        }
-        visit::walk_where_predicate(self, p);
-    }
-
     fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
         self.check_late_bound_lifetime_defs(&t.bound_generic_params);
         visit::walk_poly_trait_ref(self, t, m);
index 87c4bfae1af23627037a42dd2fac385fbffe74d9..da516f5cb412928c4272ae7bdaa52fefde992f74 100644 (file)
@@ -8,7 +8,7 @@
 use rustc_session::parse::{feature_err, feature_err_issue};
 use rustc_session::Session;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
 use rustc_span::Span;
 
 use tracing::debug;
@@ -328,17 +328,6 @@ macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
         }
     }
 
-    fn visit_name(&mut self, sp: Span, name: Symbol) {
-        if !name.as_str().is_ascii() {
-            gate_feature_post!(
-                &self,
-                non_ascii_idents,
-                self.sess.parse_sess.source_map().guess_head_span(sp),
-                "non-ascii idents are not fully supported"
-            );
-        }
-    }
-
     fn visit_item(&mut self, i: &'a ast::Item) {
         match i.kind {
             ast::ItemKind::ForeignMod(ref foreign_module) => {
@@ -366,16 +355,6 @@ fn visit_item(&mut self, i: &'a ast::Item) {
                          over time"
                     );
                 }
-                if self.sess.contains_name(&i.attrs[..], sym::main) {
-                    gate_feature_post!(
-                        &self,
-                        main,
-                        i.span,
-                        "declaration of a non-standard `#[main]` \
-                         function may change over time, for now \
-                         a top-level `fn main()` is required"
-                    );
-                }
             }
 
             ast::ItemKind::Struct(..) => {
@@ -607,12 +586,7 @@ fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) {
 
     fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
         let is_fn = match i.kind {
-            ast::AssocItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) => {
-                if let (ast::Const::Yes(_), AssocCtxt::Trait) = (sig.header.constness, ctxt) {
-                    gate_feature_post!(&self, const_fn, i.span, "const fn is unstable");
-                }
-                true
-            }
+            ast::AssocItemKind::Fn(_) => true,
             ast::AssocItemKind::TyAlias(box ast::TyAliasKind(_, ref generics, _, ref ty)) => {
                 if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
                     gate_feature_post!(
@@ -712,7 +686,6 @@ macro_rules! gate_all {
         // involved, so we only emit errors where there are no other parsing errors.
         gate_all!(destructuring_assignment, "destructuring assignments are unstable");
     }
-    gate_all!(pub_macro_rules, "`pub` on `macro_rules` items is unstable");
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
@@ -748,16 +721,46 @@ macro_rules! gate_all {
 }
 
 fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
+    use rustc_errors::Applicability;
+
     if !sess.opts.unstable_features.is_nightly_build() {
+        let lang_features = &sess.features_untracked().declared_lang_features;
         for attr in krate.attrs.iter().filter(|attr| sess.check_name(attr, sym::feature)) {
-            struct_span_err!(
+            let mut err = struct_span_err!(
                 sess.parse_sess.span_diagnostic,
                 attr.span,
                 E0554,
                 "`#![feature]` may not be used on the {} release channel",
                 option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
-            )
-            .emit();
+            );
+            let mut all_stable = true;
+            for ident in
+                attr.meta_item_list().into_iter().flatten().map(|nested| nested.ident()).flatten()
+            {
+                let name = ident.name;
+                let stable_since = lang_features
+                    .iter()
+                    .flat_map(|&(feature, _, since)| if feature == name { since } else { None })
+                    .next();
+                if let Some(since) = stable_since {
+                    err.help(&format!(
+                        "the feature `{}` has been stable since {} and no longer requires \
+                                  an attribute to enable",
+                        name, since
+                    ));
+                } else {
+                    all_stable = false;
+                }
+            }
+            if all_stable {
+                err.span_suggestion(
+                    attr.span,
+                    "remove the attribute",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            }
+            err.emit();
         }
     }
 }
index 79f35ad5819f1c2919969f74cd39a361672b1d2f..5a4e7fd9d07b4691fa70ee404373263f5531db49 100644 (file)
@@ -16,9 +16,19 @@ pub fn expand_deriving_eq(
     push: &mut dyn FnMut(Annotatable),
 ) {
     let inline = cx.meta_word(span, sym::inline);
+    let no_coverage_ident =
+        rustc_ast::attr::mk_nested_word_item(Ident::new(sym::no_coverage, span));
+    let no_coverage_feature =
+        rustc_ast::attr::mk_list_item(Ident::new(sym::feature, span), vec![no_coverage_ident]);
+    let no_coverage = cx.meta_word(span, sym::no_coverage);
     let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span));
     let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]);
-    let attrs = vec![cx.attribute(inline), cx.attribute(doc)];
+    let attrs = vec![
+        cx.attribute(inline),
+        cx.attribute(no_coverage_feature),
+        cx.attribute(no_coverage),
+        cx.attribute(doc),
+    ];
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
index 04753926c3e2a7d7bd5d2cee60d201a6fc4a5377..a3decff3ae7e1892fa075881cb859a80fc41a697 100644 (file)
@@ -541,7 +541,7 @@ fn create_derived_impl(
             self.generics.to_generics(cx, self.span, type_ident, generics);
 
         // Create the generic parameters
-        params.extend(generics.params.iter().map(|param| match param.kind {
+        params.extend(generics.params.iter().map(|param| match &param.kind {
             GenericParamKind::Lifetime { .. } => param.clone(),
             GenericParamKind::Type { .. } => {
                 // I don't think this can be moved out of the loop, since
@@ -561,7 +561,18 @@ fn create_derived_impl(
 
                 cx.typaram(self.span, param.ident, vec![], bounds, None)
             }
-            GenericParamKind::Const { .. } => param.clone(),
+            GenericParamKind::Const { ty, kw_span, .. } => {
+                let const_nodefault_kind = GenericParamKind::Const {
+                    ty: ty.clone(),
+                    kw_span: kw_span.clone(),
+
+                    // We can't have default values inside impl block
+                    default: None,
+                };
+                let mut param_clone = param.clone();
+                param_clone.kind = const_nodefault_kind;
+                param_clone
+            }
         }));
 
         // and similarly for where clauses
index 28e8259784387078e064de865d09c7c62e8cb286..c8a7ff67b4d507cceaca965827ad9f9d6d860110 100644 (file)
@@ -142,7 +142,7 @@ fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPoin
         ast::ItemKind::Fn(..) => {
             if sess.contains_name(&item.attrs, sym::start) {
                 EntryPointType::Start
-            } else if sess.contains_name(&item.attrs, sym::main) {
+            } else if sess.contains_name(&item.attrs, sym::rustc_main) {
                 EntryPointType::MainAttr
             } else if item.ident.name == sym::main {
                 if depth == 1 {
@@ -187,7 +187,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
                     let attrs = attrs
                         .into_iter()
                         .filter(|attr| {
-                            !self.sess.check_name(attr, sym::main)
+                            !self.sess.check_name(attr, sym::rustc_main)
                                 && !self.sess.check_name(attr, sym::start)
                         })
                         .chain(iter::once(allow_dead_code))
@@ -220,7 +220,7 @@ fn generate_test_harness(
     let expn_id = ext_cx.resolver.expansion_for_ast_pass(
         DUMMY_SP,
         AstPass::TestHarness,
-        &[sym::main, sym::test, sym::rustc_attrs],
+        &[sym::test, sym::rustc_attrs],
         None,
     );
     let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);
@@ -247,7 +247,7 @@ fn generate_test_harness(
 /// By default this expands to
 ///
 /// ```
-/// #[main]
+/// #[rustc_main]
 /// pub fn main() {
 ///     extern crate test;
 ///     test::test_main_static(&[
@@ -297,8 +297,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     let test_extern_stmt =
         ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None)));
 
-    // #[main]
-    let main_meta = ecx.meta_word(sp, sym::main);
+    // #[rustc_main]
+    let main_meta = ecx.meta_word(sp, sym::rustc_main);
     let main_attr = ecx.attribute(main_meta);
 
     // pub fn main() { ... }
index c7ce32b385e9434a29fd4410e2b8b308112d867c..27fc2abedc7e97710bef585ced6dde777dd16ccc 100644 (file)
@@ -277,5 +277,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
         // simd_select
         // simd_rem
         // simd_neg
+        // simd_trunc
+        // simd_floor
     }
 }
index 896e56a9a1e3a6410d128cb6fccccad337e47464..053cda1e7cc9cf89a09f6f414dca059433783080 100644 (file)
@@ -2,6 +2,7 @@
 use crate::context::CodegenCx;
 use crate::llvm::{self, BasicBlock, False};
 use crate::llvm::{AtomicOrdering, AtomicRmwBinOp, SynchronizationScope};
+use crate::llvm_util;
 use crate::type_::Type;
 use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
@@ -16,7 +17,7 @@
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::{sym, Span};
+use rustc_span::Span;
 use rustc_target::abi::{self, Align, Size};
 use rustc_target::spec::{HasTargetSpec, Target};
 use std::borrow::Cow;
@@ -261,7 +262,7 @@ fn unreachable(&mut self) {
     fn fadd_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
@@ -269,7 +270,7 @@ fn fadd_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
     fn fsub_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
@@ -277,7 +278,7 @@ fn fsub_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
     fn fmul_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
@@ -285,7 +286,7 @@ fn fmul_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
     fn fdiv_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
@@ -293,7 +294,7 @@ fn fdiv_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
     fn frem_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
@@ -669,81 +670,47 @@ fn sext(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
     }
 
     fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
-        // WebAssembly has saturating floating point to integer casts if the
-        // `nontrapping-fptoint` target feature is activated. We'll use those if
-        // they are available.
-        if self.sess().target.arch == "wasm32"
-            && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint)
-        {
+        if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() {
             let src_ty = self.cx.val_ty(val);
             let float_width = self.cx.float_width(src_ty);
             let int_width = self.cx.int_width(dest_ty);
-            let name = match (int_width, float_width) {
-                (32, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f32"),
-                (32, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f64"),
-                (64, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f32"),
-                (64, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f64"),
-                _ => None,
-            };
-            if let Some(name) = name {
-                let intrinsic = self.get_intrinsic(name);
-                return Some(self.call(intrinsic, &[val], None));
-            }
+            let name = format!("llvm.fptoui.sat.i{}.f{}", int_width, float_width);
+            let intrinsic = self.get_intrinsic(&name);
+            return Some(self.call(intrinsic, &[val], None));
         }
+
         None
     }
 
     fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
-        // WebAssembly has saturating floating point to integer casts if the
-        // `nontrapping-fptoint` target feature is activated. We'll use those if
-        // they are available.
-        if self.sess().target.arch == "wasm32"
-            && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint)
-        {
+        if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() {
             let src_ty = self.cx.val_ty(val);
             let float_width = self.cx.float_width(src_ty);
             let int_width = self.cx.int_width(dest_ty);
-            let name = match (int_width, float_width) {
-                (32, 32) => Some("llvm.wasm.trunc.saturate.signed.i32.f32"),
-                (32, 64) => Some("llvm.wasm.trunc.saturate.signed.i32.f64"),
-                (64, 32) => Some("llvm.wasm.trunc.saturate.signed.i64.f32"),
-                (64, 64) => Some("llvm.wasm.trunc.saturate.signed.i64.f64"),
-                _ => None,
-            };
-            if let Some(name) = name {
-                let intrinsic = self.get_intrinsic(name);
-                return Some(self.call(intrinsic, &[val], None));
-            }
+            let name = format!("llvm.fptosi.sat.i{}.f{}", int_width, float_width);
+            let intrinsic = self.get_intrinsic(&name);
+            return Some(self.call(intrinsic, &[val], None));
         }
-        None
-    }
 
-    fn fptosui_may_trap(&self, val: &'ll Value, dest_ty: &'ll Type) -> bool {
-        // Most of the time we'll be generating the `fptosi` or `fptoui`
-        // instruction for floating-point-to-integer conversions. These
-        // instructions by definition in LLVM do not trap. For the WebAssembly
-        // target, however, we'll lower in some cases to intrinsic calls instead
-        // which may trap. If we detect that this is a situation where we'll be
-        // using the intrinsics then we report that the call map trap, which
-        // callers might need to handle.
-        if !self.wasm_and_missing_nontrapping_fptoint() {
-            return false;
-        }
-        let src_ty = self.cx.val_ty(val);
-        let float_width = self.cx.float_width(src_ty);
-        let int_width = self.cx.int_width(dest_ty);
-        matches!((int_width, float_width), (32, 32) | (32, 64) | (64, 32) | (64, 64))
+        None
     }
 
     fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
-        // When we can, use the native wasm intrinsics which have tighter
-        // codegen. Note that this has a semantic difference in that the
-        // intrinsic can trap whereas `fptoui` never traps. That difference,
-        // however, is handled by `fptosui_may_trap` above.
+        // On WebAssembly the `fptoui` and `fptosi` instructions currently have
+        // poor codegen. The reason for this is that the corresponding wasm
+        // instructions, `i32.trunc_f32_s` for example, will trap when the float
+        // is out-of-bounds, infinity, or nan. This means that LLVM
+        // automatically inserts control flow around `fptoui` and `fptosi`
+        // because the LLVM instruction `fptoui` is defined as producing a
+        // poison value, not having UB on out-of-bounds values.
         //
-        // Note that we skip the wasm intrinsics for vector types where `fptoui`
-        // must be used instead.
-        if self.wasm_and_missing_nontrapping_fptoint() {
+        // This method, however, is only used with non-saturating casts that
+        // have UB on out-of-bounds values. This means that it's ok if we use
+        // the raw wasm instruction since out-of-bounds values can do whatever
+        // we like. To ensure that LLVM picks the right instruction we choose
+        // the raw wasm intrinsic functions which avoid LLVM inserting all the
+        // other control flow automatically.
+        if self.sess().target.arch == "wasm32" {
             let src_ty = self.cx.val_ty(val);
             if self.cx.type_kind(src_ty) != TypeKind::Vector {
                 let float_width = self.cx.float_width(src_ty);
@@ -765,7 +732,8 @@ fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
     }
 
     fn fptosi(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
-        if self.wasm_and_missing_nontrapping_fptoint() {
+        // see `fptoui` above for why wasm is different here
+        if self.sess().target.arch == "wasm32" {
             let src_ty = self.cx.val_ty(val);
             if self.cx.type_kind(src_ty) != TypeKind::Vector {
                 let float_width = self.cx.float_width(src_ty);
@@ -1242,14 +1210,14 @@ pub fn vector_reduce_fmul(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll V
     pub fn vector_reduce_fadd_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
     pub fn vector_reduce_fmul_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
@@ -1282,7 +1250,7 @@ pub fn vector_reduce_fmin_fast(&mut self, src: &'ll Value) -> &'ll Value {
         unsafe {
             let instr =
                 llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
@@ -1290,7 +1258,7 @@ pub fn vector_reduce_fmax_fast(&mut self, src: &'ll Value) -> &'ll Value {
         unsafe {
             let instr =
                 llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true);
-            llvm::LLVMRustSetHasUnsafeAlgebra(instr);
+            llvm::LLVMRustSetFastMath(instr);
             instr
         }
     }
@@ -1420,8 +1388,11 @@ fn add_incoming_to_phi(&mut self, phi: &'ll Value, val: &'ll Value, bb: &'ll Bas
         }
     }
 
-    fn wasm_and_missing_nontrapping_fptoint(&self) -> bool {
-        self.sess().target.arch == "wasm32"
-            && !self.sess().target_features.contains(&sym::nontrapping_dash_fptoint)
+    fn fptoint_sat_broken_in_llvm(&self) -> bool {
+        match self.tcx.sess.target.arch.as_str() {
+            // FIXME - https://bugs.llvm.org/show_bug.cgi?id=50083
+            "riscv64" => llvm_util::get_version() < (13, 0, 0),
+            _ => false,
+        }
     }
 }
index 773c0c16328e79cf699885db72cda9e3232a3cbc..f5c54b11c08e73d93fdff88bea8812cf3b6c94f6 100644 (file)
@@ -503,14 +503,6 @@ macro_rules! mk_struct {
         let t_f32 = self.type_f32();
         let t_f64 = self.type_f64();
 
-        ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f32", fn(t_f32) -> t_i32);
-        ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f64", fn(t_f64) -> t_i32);
-        ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f32", fn(t_f32) -> t_i64);
-        ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f64", fn(t_f64) -> t_i64);
-        ifn!("llvm.wasm.trunc.saturate.signed.i32.f32", fn(t_f32) -> t_i32);
-        ifn!("llvm.wasm.trunc.saturate.signed.i32.f64", fn(t_f64) -> t_i32);
-        ifn!("llvm.wasm.trunc.saturate.signed.i64.f32", fn(t_f32) -> t_i64);
-        ifn!("llvm.wasm.trunc.saturate.signed.i64.f64", fn(t_f64) -> t_i64);
         ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32);
         ifn!("llvm.wasm.trunc.unsigned.i32.f64", fn(t_f64) -> t_i32);
         ifn!("llvm.wasm.trunc.unsigned.i64.f32", fn(t_f32) -> t_i64);
@@ -520,6 +512,28 @@ macro_rules! mk_struct {
         ifn!("llvm.wasm.trunc.signed.i64.f32", fn(t_f32) -> t_i64);
         ifn!("llvm.wasm.trunc.signed.i64.f64", fn(t_f64) -> t_i64);
 
+        ifn!("llvm.fptosi.sat.i8.f32", fn(t_f32) -> t_i8);
+        ifn!("llvm.fptosi.sat.i16.f32", fn(t_f32) -> t_i16);
+        ifn!("llvm.fptosi.sat.i32.f32", fn(t_f32) -> t_i32);
+        ifn!("llvm.fptosi.sat.i64.f32", fn(t_f32) -> t_i64);
+        ifn!("llvm.fptosi.sat.i128.f32", fn(t_f32) -> t_i128);
+        ifn!("llvm.fptosi.sat.i8.f64", fn(t_f64) -> t_i8);
+        ifn!("llvm.fptosi.sat.i16.f64", fn(t_f64) -> t_i16);
+        ifn!("llvm.fptosi.sat.i32.f64", fn(t_f64) -> t_i32);
+        ifn!("llvm.fptosi.sat.i64.f64", fn(t_f64) -> t_i64);
+        ifn!("llvm.fptosi.sat.i128.f64", fn(t_f64) -> t_i128);
+
+        ifn!("llvm.fptoui.sat.i8.f32", fn(t_f32) -> t_i8);
+        ifn!("llvm.fptoui.sat.i16.f32", fn(t_f32) -> t_i16);
+        ifn!("llvm.fptoui.sat.i32.f32", fn(t_f32) -> t_i32);
+        ifn!("llvm.fptoui.sat.i64.f32", fn(t_f32) -> t_i64);
+        ifn!("llvm.fptoui.sat.i128.f32", fn(t_f32) -> t_i128);
+        ifn!("llvm.fptoui.sat.i8.f64", fn(t_f64) -> t_i8);
+        ifn!("llvm.fptoui.sat.i16.f64", fn(t_f64) -> t_i16);
+        ifn!("llvm.fptoui.sat.i32.f64", fn(t_f64) -> t_i32);
+        ifn!("llvm.fptoui.sat.i64.f64", fn(t_f64) -> t_i64);
+        ifn!("llvm.fptoui.sat.i128.f64", fn(t_f64) -> t_i128);
+
         ifn!("llvm.trap", fn() -> void);
         ifn!("llvm.debugtrap", fn() -> void);
         ifn!("llvm.frameaddress", fn(t_i32) -> i8p);
index 2ac814bf22838a4c56b747606def9209d4cdaa77..1faaa7e86f619ae300031dd51772c7ab990740fa 100644 (file)
@@ -8,6 +8,7 @@
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
 use rustc_llvm::RustString;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::coverage::CodeRegion;
 use rustc_span::Symbol;
 
@@ -280,6 +281,10 @@ fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
 
     let mut unused_def_ids_by_file: FxHashMap<Symbol, Vec<DefId>> = FxHashMap::default();
     for &non_codegenned_def_id in all_def_ids.difference(codegenned_def_ids) {
+        let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id);
+        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
+            continue;
+        }
         // Make sure the non-codegenned (unused) function has a file_name
         if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) {
             let def_ids =
index af366f93b919e1a3a07b714eeccebfbc89ed51af..fc6c1abf4af5639ca8604399be1666419cbdead9 100644 (file)
@@ -1053,46 +1053,48 @@ macro_rules! return_error {
         let vec_ty = bx.type_vector(elem_ty, in_len);
 
         let (intr_name, fn_ty) = match name {
-            sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
             sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)),
-            sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)),
+            sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)),
+            sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)),
             sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)),
+            sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)),
             sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)),
             sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)),
             sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)),
+            sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
+            sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
             sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)),
             sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)),
-            sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
+            sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
+            sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)),
+            sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)),
+            sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)),
             _ => return_error!("unrecognized intrinsic `{}`", name),
         };
-
         let llvm_name = &format!("llvm.{0}.v{1}{2}", intr_name, in_len, elem_ty_str);
         let f = bx.declare_cfn(&llvm_name, llvm::UnnamedAddr::No, fn_ty);
         let c = bx.call(f, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None);
-        unsafe { llvm::LLVMRustSetHasUnsafeAlgebra(c) };
         Ok(c)
     }
 
     if std::matches!(
         name,
-        sym::simd_fsqrt
-            | sym::simd_fsin
-            | sym::simd_fcos
+        sym::simd_ceil
             | sym::simd_fabs
-            | sym::simd_floor
-            | sym::simd_ceil
-            | sym::simd_fexp
+            | sym::simd_fcos
             | sym::simd_fexp2
+            | sym::simd_fexp
             | sym::simd_flog10
             | sym::simd_flog2
             | sym::simd_flog
-            | sym::simd_fpowi
-            | sym::simd_fpow
+            | sym::simd_floor
             | sym::simd_fma
+            | sym::simd_fpow
+            | sym::simd_fpowi
+            | sym::simd_fsin
+            | sym::simd_fsqrt
+            | sym::simd_round
+            | sym::simd_trunc
     ) {
         return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args);
     }
index 54a8249b175b01f6a898b29c746994599bb918c4..32b1526f6e44c3463440a0e3abfad1ea6f3b70cf 100644 (file)
@@ -1354,7 +1354,7 @@ pub fn LLVMBuildXor(
     pub fn LLVMBuildNeg(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
     pub fn LLVMBuildFNeg(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
     pub fn LLVMBuildNot(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
-    pub fn LLVMRustSetHasUnsafeAlgebra(Instr: &Value);
+    pub fn LLVMRustSetFastMath(Instr: &Value);
 
     // Memory
     pub fn LLVMBuildAlloca(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value;
index 7c1aaebb9abaf65da8105b4be2d71203a8914016..7a3d715df6dcd55df6b9eeaab051e58e44945d5a 100644 (file)
@@ -13,8 +13,8 @@ cc = "1.0.1"
 itertools = "0.9"
 tracing = "0.1"
 libc = "0.2.50"
-jobserver = "0.1.11"
-tempfile = "3.1"
+jobserver = "0.1.22"
+tempfile = "3.2"
 pathdiff = "0.2.0"
 
 rustc_serialize = { path = "../rustc_serialize" }
index 629cb64d43ee1c55f01a5a9bce4421df09464120..9917c23f121501f6294debbc4f4c52fb391e2cb5 100644 (file)
@@ -11,7 +11,7 @@
 use rustc_hir::lang_items::LangItem;
 use rustc_middle::mir;
 use rustc_middle::ty::cast::{CastTy, IntTy};
-use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
+use rustc_middle::ty::layout::HasTyCtxt;
 use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
 use rustc_span::source_map::{Span, DUMMY_SP};
 use rustc_span::symbol::sym;
@@ -385,10 +385,10 @@ pub fn codegen_rvalue_operand(
                                 bx.inttoptr(usize_llval, ll_t_out)
                             }
                             (CastTy::Float, CastTy::Int(IntTy::I)) => {
-                                cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out, cast)
+                                cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out)
                             }
                             (CastTy::Float, CastTy::Int(_)) => {
-                                cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out, cast)
+                                cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out)
                             }
                             _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty),
                         };
@@ -790,7 +790,6 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     x: Bx::Value,
     float_ty: Bx::Type,
     int_ty: Bx::Type,
-    int_layout: TyAndLayout<'tcx>,
 ) -> Bx::Value {
     if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts {
         return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
@@ -891,134 +890,39 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128);
     let zero = bx.cx().const_uint(int_ty, 0);
 
-    // The codegen here differs quite a bit depending on whether our builder's
-    // `fptosi` and `fptoui` instructions may trap for out-of-bounds values. If
-    // they don't trap then we can start doing everything inline with a
-    // `select` instruction because it's ok to execute `fptosi` and `fptoui`
-    // even if we don't use the results.
-    if !bx.fptosui_may_trap(x, int_ty) {
-        // Step 1 ...
-        let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
-        let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min);
-        let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max);
-
-        // Step 2: We use two comparisons and two selects, with %s1 being the
-        // result:
-        //     %less_or_nan = fcmp ult %x, %f_min
-        //     %greater = fcmp olt %x, %f_max
-        //     %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result
-        //     %s1 = select %greater, int_ty::MAX, %s0
-        // Note that %less_or_nan uses an *unordered* comparison. This
-        // comparison is true if the operands are not comparable (i.e., if x is
-        // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if
-        // x is NaN.
-        //
-        // Performance note: Unordered comparison can be lowered to a "flipped"
-        // comparison and a negation, and the negation can be merged into the
-        // select. Therefore, it not necessarily any more expensive than a
-        // ordered ("normal") comparison. Whether these optimizations will be
-        // performed is ultimately up to the backend, but at least x86 does
-        // perform them.
-        let s0 = bx.select(less_or_nan, int_min, fptosui_result);
-        let s1 = bx.select(greater, int_max, s0);
-
-        // Step 3: NaN replacement.
-        // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN.
-        // Therefore we only need to execute this step for signed integer types.
-        if signed {
-            // LLVM has no isNaN predicate, so we use (x == x) instead
-            let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x);
-            bx.select(cmp, s1, zero)
-        } else {
-            s1
-        }
+    // Step 1 ...
+    let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) };
+    let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min);
+    let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max);
+
+    // Step 2: We use two comparisons and two selects, with %s1 being the
+    // result:
+    //     %less_or_nan = fcmp ult %x, %f_min
+    //     %greater = fcmp olt %x, %f_max
+    //     %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result
+    //     %s1 = select %greater, int_ty::MAX, %s0
+    // Note that %less_or_nan uses an *unordered* comparison. This
+    // comparison is true if the operands are not comparable (i.e., if x is
+    // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if
+    // x is NaN.
+    //
+    // Performance note: Unordered comparison can be lowered to a "flipped"
+    // comparison and a negation, and the negation can be merged into the
+    // select. Therefore, it not necessarily any more expensive than a
+    // ordered ("normal") comparison. Whether these optimizations will be
+    // performed is ultimately up to the backend, but at least x86 does
+    // perform them.
+    let s0 = bx.select(less_or_nan, int_min, fptosui_result);
+    let s1 = bx.select(greater, int_max, s0);
+
+    // Step 3: NaN replacement.
+    // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN.
+    // Therefore we only need to execute this step for signed integer types.
+    if signed {
+        // LLVM has no isNaN predicate, so we use (x == x) instead
+        let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x);
+        bx.select(cmp, s1, zero)
     } else {
-        // In this case we cannot execute `fptosi` or `fptoui` and then later
-        // discard the result. The builder is telling us that these instructions
-        // will trap on out-of-bounds values, so we need to use basic blocks and
-        // control flow to avoid executing the `fptosi` and `fptoui`
-        // instructions.
-        //
-        // The general idea of what we're constructing here is, for f64 -> i32:
-        //
-        //      ;; block so far... %0 is the argument
-        //      %result = alloca i32, align 4
-        //      %inbound_lower = fcmp oge double %0, 0xC1E0000000000000
-        //      %inbound_upper = fcmp ole double %0, 0x41DFFFFFFFC00000
-        //      ;; match (inbound_lower, inbound_upper) {
-        //      ;;     (true, true) => %0 can be converted without trapping
-        //      ;;     (false, false) => %0 is a NaN
-        //      ;;     (true, false) => %0 is too large
-        //      ;;     (false, true) => %0 is too small
-        //      ;; }
-        //      ;;
-        //      ;; The (true, true) check, go to %convert if so.
-        //      %inbounds = and i1 %inbound_lower, %inbound_upper
-        //      br i1 %inbounds, label %convert, label %specialcase
-        //
-        //  convert:
-        //      %cvt = call i32 @llvm.wasm.trunc.signed.i32.f64(double %0)
-        //      store i32 %cvt, i32* %result, align 4
-        //      br label %done
-        //
-        //  specialcase:
-        //      ;; Handle the cases where the number is NaN, too large or too small
-        //
-        //      ;; Either (true, false) or (false, true)
-        //      %is_not_nan = or i1 %inbound_lower, %inbound_upper
-        //      ;; Figure out which saturated value we are interested in if not `NaN`
-        //      %saturated = select i1 %inbound_lower, i32 2147483647, i32 -2147483648
-        //      ;; Figure out between saturated and NaN representations
-        //      %result_nan = select i1 %is_not_nan, i32 %saturated, i32 0
-        //      store i32 %result_nan, i32* %result, align 4
-        //      br label %done
-        //
-        //  done:
-        //      %r = load i32, i32* %result, align 4
-        //      ;; ...
-        let done = bx.build_sibling_block("float_cast_done");
-        let mut convert = bx.build_sibling_block("float_cast_convert");
-        let mut specialcase = bx.build_sibling_block("float_cast_specialcase");
-
-        let result = PlaceRef::alloca(bx, int_layout);
-        result.storage_live(bx);
-
-        // Use control flow to figure out whether we can execute `fptosi` in a
-        // basic block, or whether we go to a different basic block to implement
-        // the saturating logic.
-        let inbound_lower = bx.fcmp(RealPredicate::RealOGE, x, f_min);
-        let inbound_upper = bx.fcmp(RealPredicate::RealOLE, x, f_max);
-        let inbounds = bx.and(inbound_lower, inbound_upper);
-        bx.cond_br(inbounds, convert.llbb(), specialcase.llbb());
-
-        // Translation of the `convert` basic block
-        let cvt = if signed { convert.fptosi(x, int_ty) } else { convert.fptoui(x, int_ty) };
-        convert.store(cvt, result.llval, result.align);
-        convert.br(done.llbb());
-
-        // Translation of the `specialcase` basic block. Note that like above
-        // we try to be a bit clever here for unsigned conversions. In those
-        // cases the `int_min` is zero so we don't need two select instructions,
-        // just one to choose whether we need `int_max` or not. If
-        // `inbound_lower` is true then we're guaranteed to not be `NaN` and
-        // since we're greater than zero we must be saturating to `int_max`. If
-        // `inbound_lower` is false then we're either NaN or less than zero, so
-        // we saturate to zero.
-        let result_nan = if signed {
-            let is_not_nan = specialcase.or(inbound_lower, inbound_upper);
-            let saturated = specialcase.select(inbound_lower, int_max, int_min);
-            specialcase.select(is_not_nan, saturated, zero)
-        } else {
-            specialcase.select(inbound_lower, int_max, int_min)
-        };
-        specialcase.store(result_nan, result.llval, result.align);
-        specialcase.br(done.llbb());
-
-        // Translation of the `done` basic block, positioning ourselves to
-        // continue from that point as well.
-        *bx = done;
-        let ret = bx.load(result.llval, result.align);
-        result.storage_dead(bx);
-        ret
+        s1
     }
 }
index d5bd2780388e63cd2160f28269fe68b86ad63147..1bc05f30e5c3785abf2e6be40ba059654745b71a 100644 (file)
@@ -171,7 +171,6 @@ fn atomic_store(
     fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
     fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>;
     fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>;
-    fn fptosui_may_trap(&self, val: Self::Value, dest_ty: Self::Type) -> bool;
     fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
     fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
     fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
index d32598e716e1df0c3a05fdb636e9f231f024526d..507425d64e3721e2277c45152a9a539bf605ce84 100644 (file)
@@ -8,7 +8,7 @@ edition = "2018"
 doctest = false
 
 [dependencies]
-arrayvec = { version = "0.5.1", default-features = false }
+arrayvec = { version = "0.7", default-features = false }
 ena = "0.14"
 indexmap = "1.5.1"
 tracing = "0.1"
@@ -17,7 +17,7 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_graphviz = { path = "../rustc_graphviz" }
 cfg-if = "0.1.2"
-crossbeam-utils = { version = "0.7", features = ["nightly"] }
+crossbeam-utils = { version = "0.8", features = ["nightly"] }
 stable_deref_trait = "1.0.0"
 rayon = { version = "0.3.1", package = "rustc-rayon" }
 rayon-core = { version = "0.3.1", package = "rustc-rayon-core" }
@@ -28,7 +28,7 @@ bitflags = "1.2.1"
 measureme = "9.1.0"
 libc = "0.2"
 stacker = "0.1.12"
-tempfile = "3.0.5"
+tempfile = "3.2"
 
 [dependencies.parking_lot]
 version = "0.11"
index 06e8442d47539841fb20fc776be7c241e3e43a54..e249886e9bc016f78661a0043bf5288388fd1634 100644 (file)
 // for reasonably small arrays that stay
 // small in vast majority of cases.
 //
-// '8' is choosen as a sane default, to be
+// '8' is chosen as a sane default, to be
 // reevaluated later.
-//
-// Note: As of now ArrayVec design prevents
-//       us from making it user-customizable.
 const SSO_ARRAY_SIZE: usize = 8;
 
 /// Small-storage-optimized implementation of a map.
@@ -70,7 +67,7 @@
 
 #[derive(Clone)]
 pub enum SsoHashMap<K, V> {
-    Array(ArrayVec<[(K, V); SSO_ARRAY_SIZE]>),
+    Array(ArrayVec<(K, V), SSO_ARRAY_SIZE>),
     Map(FxHashMap<K, V>),
 }
 
@@ -411,7 +408,7 @@ fn extend_reserve(&mut self, additional: usize) {
 
 impl<K, V> IntoIterator for SsoHashMap<K, V> {
     type IntoIter = EitherIter<
-        <ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
+        <ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
         <FxHashMap<K, V> as IntoIterator>::IntoIter,
     >;
     type Item = <Self::IntoIter as Iterator>::Item;
@@ -441,7 +438,7 @@ fn adapt_array_mut_it<K, V>(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) {
 impl<'a, K, V> IntoIterator for &'a SsoHashMap<K, V> {
     type IntoIter = EitherIter<
         std::iter::Map<
-            <&'a ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
+            <&'a ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
             fn(&'a (K, V)) -> (&'a K, &'a V),
         >,
         <&'a FxHashMap<K, V> as IntoIterator>::IntoIter,
@@ -459,7 +456,7 @@ fn into_iter(self) -> Self::IntoIter {
 impl<'a, K, V> IntoIterator for &'a mut SsoHashMap<K, V> {
     type IntoIter = EitherIter<
         std::iter::Map<
-            <&'a mut ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
+            <&'a mut ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
             fn(&'a mut (K, V)) -> (&'a K, &'a mut V),
         >,
         <&'a mut FxHashMap<K, V> as IntoIterator>::IntoIter,
index 4b529734328c7153387a4f2df61b5ce9cab8e7f2..41a1fa488d31b1e16091662f922b3512d207028d 100644 (file)
 E0718: include_str!("./error_codes/E0718.md"),
 E0719: include_str!("./error_codes/E0719.md"),
 E0720: include_str!("./error_codes/E0720.md"),
-E0723: include_str!("./error_codes/E0723.md"),
 E0724: include_str!("./error_codes/E0724.md"),
 E0725: include_str!("./error_codes/E0725.md"),
 E0727: include_str!("./error_codes/E0727.md"),
     E0717, // rustc_promotable without stability attribute
 //  E0721, // `await` keyword
     E0722, // Malformed `#[optimize]` attribute
+//    E0723, unstable feature in `const` context
     E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
 //  E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
     E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]`
index 0a02913d2364d6716caba2ab49e45410edc00be2..d4e19170f3f7b5ce699a9f2f8e2bcc4a3f543563 100644 (file)
@@ -1,8 +1,10 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 More than one function was declared with the `#[main]` attribute.
 
 Erroneous code example:
 
-```compile_fail,E0137
+```compile_fail
 #![feature(main)]
 
 #[main]
@@ -16,7 +18,7 @@ This error indicates that the compiler found multiple functions with the
 `#[main]` attribute. This is an error because there must be a unique entry
 point into a Rust program. Example:
 
-```
+```compile_fail
 #![feature(main)]
 
 #[main]
index 930204847ecd872347c7d34077848ebf52f70288..ab438e41447125634365e46ab03a5e2d8e7bbf8a 100644 (file)
@@ -3,8 +3,6 @@ A trait method was declared const.
 Erroneous code example:
 
 ```compile_fail,E0379
-#![feature(const_fn)]
-
 trait Foo {
     const fn bar() -> u32; // error!
 }
index 1360cc99afcc432eeded5e3a9249d176c456e936..d6fa51e618c4c121b502aeaeaf919fad8aff8f8d 100644 (file)
@@ -8,14 +8,15 @@ struct Foo;
 struct Bar;
 
 impl Foo for Bar {} // error: `Foo` is not a trait
+fn baz<T: Foo>(t: T) {} // error: `Foo` is not a trait
 ```
 
 Another erroneous code example:
 
 ```compile_fail,E0404
-struct Foo;
+type Foo = Iterator<Item=String>;
 
-fn bar<T: Foo>(t: T) {} // error: `Foo` is not a trait
+fn bar<T: Foo>(t: T) {} // error: `Foo` is a type alias
 ```
 
 Please verify that the trait's name was not misspelled or that the right
@@ -30,14 +31,27 @@ struct Bar;
 impl Foo for Bar { // ok!
     // functions implementation
 }
+
+fn baz<T: Foo>(t: T) {} // ok!
 ```
 
-or:
+Alternatively, you could introduce a new trait with your desired restrictions
+as a super trait:
 
 ```
-trait Foo {
-    // some functions
-}
+# trait Foo {}
+# struct Bar;
+# impl Foo for Bar {}
+trait Qux: Foo {} // Anything that implements Qux also needs to implement Foo
+fn baz<T: Qux>(t: T) {} // also ok!
+```
+
+Finally, if you are on nightly and want to use a trait alias
+instead of a type alias, you should use `#![feature(trait_alias)]`:
+
+```
+#![feature(trait_alias)]
+trait Foo = Iterator<Item=String>;
 
 fn bar<T: Foo>(t: T) {} // ok!
 ```
index e55fa4c6ede8b458a1db11b2ab3f24ba088a5b45..3178bf21919e855e029d920c1c87056bc7b7d179 100644 (file)
@@ -4,8 +4,8 @@ beta compilers will not comply.
 Erroneous code example:
 
 ```ignore (depends on release channel)
-#![feature(non_ascii_idents)] // error: `#![feature]` may not be used on the
-                              //        stable release channel
+#![feature(lang_items)] // error: `#![feature]` may not be used on the
+                        //        stable release channel
 ```
 
 If you need the feature, make sure to use a nightly release of the compiler
diff --git a/compiler/rustc_error_codes/src/error_codes/E0723.md b/compiler/rustc_error_codes/src/error_codes/E0723.md
deleted file mode 100644 (file)
index bc22442..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-An unstable feature in `const` contexts was used.
-
-Erroneous code example:
-
-```compile_fail,E0723
-const fn foo<T: Copy>(_: T) { // error!
-   // ...
-}
-```
-
-To enable this feature on a nightly version of rustc, add the `const_fn`
-feature flag:
-
-```
-#![feature(const_fn)]
-
-const fn foo<T: Copy>(_: T) { // ok!
-   // ...
-}
-```
index 9f4b19cfda6b40d857e837b57339d8917b3227e9..acddb69aaba56cd8d62571c1c29881afa52b43e0 100644 (file)
@@ -3,7 +3,6 @@ A non-ASCII identifier was used in an invalid context.
 Erroneous code examples:
 
 ```compile_fail,E0754
-# #![feature(non_ascii_idents)]
 
 mod řųśť; // error!
 
@@ -17,8 +16,6 @@ Non-ASCII can be used as module names if it is inlined or if a `#[path]`
 attribute is specified. For example:
 
 ```
-# #![feature(non_ascii_idents)]
-
 mod řųśť { // ok!
     const IS_GREAT: bool = true;
 }
index 0a2e2290e77c2e7ad2cfcd23054a34de130456aa..152627cf6545f5efb4b12f02dea2d4d4c4394371 100644 (file)
@@ -3,7 +3,6 @@ A mutable reference was used in a constant.
 Erroneous code example:
 
 ```compile_fail,E0764
-#![feature(const_fn)]
 #![feature(const_mut_refs)]
 
 fn main() {
@@ -27,7 +26,6 @@ Remember: you cannot use a function call inside a constant or static. However,
 you can totally use it in constant functions:
 
 ```
-#![feature(const_fn)]
 #![feature(const_mut_refs)]
 
 const fn foo(x: usize) -> usize {
index 0d1f55a6b00eee194ff4eadb5936338ca86db0bc..f1a31f0d4f5c9eac35b9fd29d8520afc838f26a3 100644 (file)
@@ -321,7 +321,7 @@ struct HandlerInner {
 
     /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid
     /// emitting the same diagnostic with extended help (`--teach`) twice, which
-    /// would be uneccessary repetition.
+    /// would be unnecessary repetition.
     taught_diagnostics: FxHashSet<DiagnosticId>,
 
     /// Used to suggest rustc --explain <error code>
index ec122e7be6e8efd52462a4ca5f5ec2685b9ec639..e262d95bb70f251bc4caf76bbaf7f823d9b5c084 100644 (file)
@@ -1,39 +1,52 @@
 // Code for creating styled buffers
 
 use crate::snippet::{Style, StyledString};
-use std::iter;
 
 #[derive(Debug)]
 pub struct StyledBuffer {
-    text: Vec<Vec<char>>,
-    styles: Vec<Vec<Style>>,
+    lines: Vec<Vec<StyledChar>>,
+}
+
+#[derive(Debug, Clone)]
+struct StyledChar {
+    chr: char,
+    style: Style,
+}
+
+impl StyledChar {
+    const SPACE: Self = StyledChar::new(' ', Style::NoStyle);
+
+    const fn new(chr: char, style: Style) -> Self {
+        StyledChar { chr, style }
+    }
 }
 
 impl StyledBuffer {
     pub fn new() -> StyledBuffer {
-        StyledBuffer { text: vec![], styles: vec![] }
+        StyledBuffer { lines: vec![] }
     }
 
+    /// Returns content of `StyledBuffer` splitted by lines and line styles
     pub fn render(&self) -> Vec<Vec<StyledString>> {
         // Tabs are assumed to have been replaced by spaces in calling code.
-        debug_assert!(self.text.iter().all(|r| !r.contains(&'\t')));
+        debug_assert!(self.lines.iter().all(|r| !r.iter().any(|sc| sc.chr == '\t')));
 
         let mut output: Vec<Vec<StyledString>> = vec![];
         let mut styled_vec: Vec<StyledString> = vec![];
 
-        for (row, row_style) in iter::zip(&self.text, &self.styles) {
+        for styled_line in &self.lines {
             let mut current_style = Style::NoStyle;
             let mut current_text = String::new();
 
-            for (&c, &s) in iter::zip(row, row_style) {
-                if s != current_style {
+            for sc in styled_line {
+                if sc.style != current_style {
                     if !current_text.is_empty() {
                         styled_vec.push(StyledString { text: current_text, style: current_style });
                     }
-                    current_style = s;
+                    current_style = sc.style;
                     current_text = String::new();
                 }
-                current_text.push(c);
+                current_text.push(sc.chr);
             }
             if !current_text.is_empty() {
                 styled_vec.push(StyledString { text: current_text, style: current_style });
@@ -49,29 +62,25 @@ pub fn render(&self) -> Vec<Vec<StyledString>> {
     }
 
     fn ensure_lines(&mut self, line: usize) {
-        while line >= self.text.len() {
-            self.text.push(vec![]);
-            self.styles.push(vec![]);
+        if line >= self.lines.len() {
+            self.lines.resize(line + 1, Vec::new());
         }
     }
 
+    /// Sets `chr` with `style` for given `line`, `col`.
+    /// If `line` does not exist in our buffer, adds empty lines up to the given
+    /// and fills the last line with unstyled whitespace.
     pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
         self.ensure_lines(line);
-        if col < self.text[line].len() {
-            self.text[line][col] = chr;
-            self.styles[line][col] = style;
-        } else {
-            let mut i = self.text[line].len();
-            while i < col {
-                self.text[line].push(' ');
-                self.styles[line].push(Style::NoStyle);
-                i += 1;
-            }
-            self.text[line].push(chr);
-            self.styles[line].push(style);
+        if col >= self.lines[line].len() {
+            self.lines[line].resize(col + 1, StyledChar::SPACE);
         }
+        self.lines[line][col] = StyledChar::new(chr, style);
     }
 
+    /// Sets `string` with `style` for given `line`, starting from `col`.
+    /// If `line` does not exist in our buffer, adds empty lines up to the given
+    /// and fills the last line with unstyled whitespace.
     pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
         let mut n = col;
         for c in string.chars() {
@@ -80,32 +89,40 @@ pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
         }
     }
 
+    /// For given `line` inserts `string` with `style` before old content of that line,
+    /// adding lines if needed
     pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
         self.ensure_lines(line);
         let string_len = string.chars().count();
 
-        // Push the old content over to make room for new content
-        for _ in 0..string_len {
-            self.styles[line].insert(0, Style::NoStyle);
-            self.text[line].insert(0, ' ');
+        if !self.lines[line].is_empty() {
+            // Push the old content over to make room for new content
+            for _ in 0..string_len {
+                self.lines[line].insert(0, StyledChar::SPACE);
+            }
         }
 
         self.puts(line, 0, string, style);
     }
 
+    /// For given `line` inserts `string` with `style` after old content of that line,
+    /// adding lines if needed
     pub fn append(&mut self, line: usize, string: &str, style: Style) {
-        if line >= self.text.len() {
+        if line >= self.lines.len() {
             self.puts(line, 0, string, style);
         } else {
-            let col = self.text[line].len();
+            let col = self.lines[line].len();
             self.puts(line, col, string, style);
         }
     }
 
     pub fn num_lines(&self) -> usize {
-        self.text.len()
+        self.lines.len()
     }
 
+    /// Set `style` for `line`, `col_start..col_end` range if:
+    /// 1. That line and column range exist in `StyledBuffer`
+    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
     pub fn set_style_range(
         &mut self,
         line: usize,
@@ -119,10 +136,13 @@ pub fn set_style_range(
         }
     }
 
+    /// Set `style` for `line`, `col` if:
+    /// 1. That line and column exist in `StyledBuffer`
+    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
     pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
-        if let Some(ref mut line) = self.styles.get_mut(line) {
-            if let Some(s) = line.get_mut(col) {
-                if *s == Style::NoStyle || *s == Style::Quotation || overwrite {
+        if let Some(ref mut line) = self.lines.get_mut(line) {
+            if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
+                if overwrite || *s == Style::NoStyle || *s == Style::Quotation {
                     *s = style;
                 }
             }
index cbc4d14a65a1bf605a8515e80e4633493f6fd4c6..5244ac36bba5de5badcc923c8728078b06e229b8 100644 (file)
@@ -69,7 +69,7 @@ enum KleeneOp {
     ZeroOrMore,
     /// Kleene plus (`+`) for one or more repetitions
     OneOrMore,
-    /// Kleene optional (`?`) for zero or one reptitions
+    /// Kleene optional (`?`) for zero or one repetitions
     ZeroOrOne,
 }
 
index dde65d998d81b2a3531a7f87a1ee71f06d387d88..f9e7c4254bc495b29b08898fa1b8018f0c34ca61 100644 (file)
@@ -209,7 +209,7 @@ pub(super) fn transcribe<'a>(
                             }
                         } else {
                             // 0 is the initial counter (we have done 0 repretitions so far). `len`
-                            // is the total number of reptitions we should generate.
+                            // is the total number of repetitions we should generate.
                             repeats.push((0, len));
 
                             // The first time we encounter the sequence we push it to the stack. It
@@ -362,7 +362,7 @@ fn with(self, other: LockstepIterSize) -> LockstepIterSize {
 /// appropriate meta-vars in `interpolations`.
 ///
 /// Note that if `repeats` does not match the exact correct depth of a meta-var,
-/// `lookup_cur_matched` will return `None`, which is why this still works even in the presnece of
+/// `lookup_cur_matched` will return `None`, which is why this still works even in the presence of
 /// multiple nested matcher sequences.
 fn lockstep_iter_size(
     tree: &mbe::TokenTree,
index f006351647e40972b2f0273d5b28695b5aa92cc8..e8642a52749c46201ee0e1c7116fd919c33e32e1 100644 (file)
@@ -279,6 +279,8 @@ macro_rules! declare_features {
     (accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668), None),
     /// Allows the use of or-patterns (e.g., `0 | 1`).
     (accepted, or_patterns, "1.53.0", Some(54883), None),
+    /// Allows defining identifiers beyond ASCII.
+    (accepted, non_ascii_idents, "1.53.0", Some(55467), None),
 
     // -------------------------------------------------------------------------
     // feature-group-end: accepted features
index 3151aa349d63611df535da48b762d8a0eda56b01..2492db5b6340ddfef96e11806e8d42ffa6aaa327 100644 (file)
@@ -63,6 +63,10 @@ pub fn enabled(&self, feature: Symbol) -> bool {
                     _ => panic!("`{}` was not listed in `declare_features`", feature),
                 }
             }
+
+            pub fn unordered_const_ty_params(&self) -> bool {
+                self.const_generics || self.const_generics_defaults
+            }
         }
     };
 }
@@ -134,9 +138,6 @@ pub fn set(&self, features: &mut Features, span: Span) {
     /// Allows using the `box $expr` syntax.
     (active, box_syntax, "1.0.0", Some(49733), None),
 
-    /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls.
-    (active, main, "1.0.0", Some(29634), None),
-
     /// Allows using `#[start]` on a function indicating that it is the program entrypoint.
     (active, start, "1.0.0", Some(29633), None),
 
@@ -258,9 +259,6 @@ pub fn set(&self, features: &mut Features, span: Span) {
     // feature-group-start: actual feature gates
     // -------------------------------------------------------------------------
 
-    /// Allows defining identifiers beyond ASCII.
-    (active, non_ascii_idents, "1.0.0", Some(55467), None),
-
     /// Allows using `#[plugin_registrar]` on functions.
     (active, plugin_registrar, "1.0.0", Some(29597), None),
 
@@ -627,15 +625,15 @@ pub fn set(&self, features: &mut Features, span: Span) {
     /// Allows macro attributes to observe output of `#[derive]`.
     (active, macro_attributes_in_derive_output, "1.51.0", Some(81119), None),
 
-    /// Allows `pub` on `macro_rules` items.
-    (active, pub_macro_rules, "1.52.0", Some(78855), None),
-
     /// Allows the use of type alias impl trait in function return positions
     (active, min_type_alias_impl_trait, "1.52.0", Some(63063), None),
 
     /// Allows associated types in inherent impls.
     (active, inherent_associated_types, "1.52.0", Some(8995), None),
 
+    // Allows setting the threshold for the `large_assignments` lint.
+    (active, large_assignments, "1.52.0", Some(83518), None),
+
     /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries.
     (active, c_unwind, "1.52.0", Some(74990), None),
 
@@ -645,6 +643,16 @@ pub fn set(&self, features: &mut Features, span: Span) {
     /// Allows `extern "wasm" fn`
     (active, wasm_abi, "1.53.0", Some(83788), None),
 
+    /// Allows function attribute `#[no_coverage]`, to bypass coverage
+    /// instrumentation of that function.
+    (active, no_coverage, "1.53.0", Some(84605), None),
+
+    /// Allows trait bounds in `const fn`.
+    (active, const_fn_trait_bound, "1.53.0", Some(57563), None),
+
+    /// Allows unsizing coercions in `const fn`.
+    (active, const_fn_unsize, "1.53.0", Some(64992), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
index 7df9b3f0a796059025305ad316c331599f5b7e12..5474fea9c78577c7b843d922e685c00a2bb8e83a 100644 (file)
@@ -227,8 +227,8 @@ macro_rules! experimental {
         template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
     ),
     ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")),
-    ungated!(no_link, Normal, template!(Word)),
-    ungated!(repr, Normal, template!(List: "C")),
+    ungated!(no_link, AssumedUsed, template!(Word)),
+    ungated!(repr, AssumedUsed, template!(List: "C")),
     ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")),
     ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")),
     ungated!(no_mangle, AssumedUsed, template!(Word)),
@@ -241,6 +241,10 @@ macro_rules! experimental {
         const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
         experimental!(const_eval_limit)
     ),
+    gated!(
+        move_size_limit, CrateLevel, template!(NameValueStr: "N"), large_assignments,
+        experimental!(move_size_limit)
+    ),
 
     // Entry point:
     ungated!(main, Normal, template!(Word)),
@@ -269,6 +273,13 @@ macro_rules! experimental {
         template!(List: "address, memory, thread"),
         experimental!(no_sanitize)
     ),
+    ungated!(
+        // Not exclusively gated at the crate level (though crate-level is
+        // supported). The feature can alternatively be enabled on individual
+        // functions.
+        no_coverage, AssumedUsed,
+        template!(Word),
+    ),
 
     // FIXME: #14408 assume docs are used since rustdoc looks at them.
     ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
@@ -317,7 +328,7 @@ macro_rules! experimental {
         "custom test frameworks are an unstable feature",
     ),
     // RFC #1268
-    gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)),
+    gated!(marker, AssumedUsed, template!(Word), marker_trait_attr, experimental!(marker)),
     gated!(
         thread_local, AssumedUsed, template!(Word),
         "`#[thread_local]` is an experimental feature, and does not currently handle destructors",
@@ -536,6 +547,15 @@ macro_rules! experimental {
         rustc_specialization_trait, Normal, template!(Word),
         "the `#[rustc_specialization_trait]` attribute is used to check specializations"
     ),
+    rustc_attr!(
+        rustc_main, Normal, template!(Word),
+        "the `#[rustc_main]` attribute is used internally to specify test entry point function",
+    ),
+    rustc_attr!(
+        rustc_skip_array_during_method_dispatch, Normal, template!(Word),
+        "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
+        from method dispatch when the receiver is an array, for compatibility in editions < 2021."
+    ),
 
     // ==========================================================================
     // Internal attributes, Testing:
index e1491576616627d13dc538b5442d163c3be065ea..fa8ef182aeddf2a0eaf815fe6503742cbb304697 100644 (file)
@@ -132,6 +132,10 @@ macro_rules! declare_features {
     (removed, link_args, "1.53.0", Some(29596), None,
      Some("removed in favor of using `-C link-arg=ARG` on command line, \
            which is available from cargo build scripts with `cargo:rustc-link-arg` now")),
+    /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls.
+    (removed, main, "1.53.0", Some(29634), None, None),
+    (removed, pub_macro_rules, "1.53.0", Some(78855), None,
+     Some("removed due to being incomplete, in particular it does not work across crates")),
 
     // -------------------------------------------------------------------------
     // feature-group-end: removed features
index 1051fb8cea27940fedba9dc77fc21b881adda0d8..5baaaad7370fc6f2bbf1c813154a147743d367e2 100644 (file)
@@ -296,7 +296,9 @@ pub fn to_ord(&self, feats: &rustc_feature::Features) -> ast::ParamKindOrd {
         match self {
             GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime,
             GenericArg::Type(_) => ast::ParamKindOrd::Type,
-            GenericArg::Const(_) => ast::ParamKindOrd::Const { unordered: feats.const_generics },
+            GenericArg::Const(_) => {
+                ast::ParamKindOrd::Const { unordered: feats.unordered_const_ty_params() }
+            }
         }
     }
 }
@@ -2201,7 +2203,7 @@ impl PrimTy {
 
     /// Like [`PrimTy::name`], but returns a &str instead of a symbol.
     ///
-    /// Used by rustdoc.
+    /// Used by clippy.
     pub fn name_str(self) -> &'static str {
         match self {
             PrimTy::Int(i) => i.name_str(),
index 36a30900fb26cdbcbf926001292991ddcf061be5..65c99535c4e3474e830c6b20f45dddfbea45437b 100644 (file)
@@ -3,7 +3,6 @@
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
 
 #![feature(crate_visibility_modifier)]
-#![feature(const_fn)] // For the unsizing cast on `&[]`
 #![feature(const_panic)]
 #![feature(extended_key_value_attributes)]
 #![feature(in_band_lifetimes)]
index 5820e7a261230832f99b64b247e5a157d76fdcef..77d083fc5e967b4cae32fd111f2efdc40646c105 100644 (file)
@@ -1095,8 +1095,8 @@ pub fn print_block_maybe_unclosed(
 
     fn print_else(&mut self, els: Option<&hir::Expr<'_>>) {
         match els {
-            Some(_else) => {
-                match _else.kind {
+            Some(else_) => {
+                match else_.kind {
                     // "another else-if"
                     hir::ExprKind::If(ref i, ref then, ref e) => {
                         self.cbox(INDENT_UNIT - 1);
@@ -1114,6 +1114,26 @@ fn print_else(&mut self, els: Option<&hir::Expr<'_>>) {
                         self.s.word(" else ");
                         self.print_block(&b)
                     }
+                    hir::ExprKind::Match(ref expr, arms, _) => {
+                        // else if let desugared to match
+                        assert!(arms.len() == 2, "if let desugars to match with two arms");
+
+                        self.s.word(" else ");
+                        self.s.word("{");
+
+                        self.cbox(INDENT_UNIT);
+                        self.ibox(INDENT_UNIT);
+                        self.word_nbsp("match");
+                        self.print_expr_as_cond(&expr);
+                        self.s.space();
+                        self.bopen();
+                        for arm in arms {
+                            self.print_arm(arm);
+                        }
+                        self.bclose(expr.span);
+
+                        self.s.word("}");
+                    }
                     // BLEAH, constraints would be great here
                     _ => {
                         panic!("print_if saw if with weird alternative");
index 6e1471df195b1a9b002880c056fcf85233ee759e..4b1f0b86475a28469d453ba201f48743d3ddc40e 100644 (file)
@@ -8,6 +8,6 @@ edition = "2018"
 doctest = false
 
 [dependencies]
-arrayvec = { version = "0.5.1", default-features = false }
+arrayvec = { version = "0.7", default-features = false }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_macros = { path = "../rustc_macros" }
index cfea5092bc30655f7cac2acf11e1bb7e99a9e9a1..d26ab1939e3bf145349ed2100dc541eb626d227e 100644 (file)
@@ -375,7 +375,7 @@ fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool
 #[derive(Clone, Debug)]
 pub struct SparseBitSet<T> {
     domain_size: usize,
-    elems: ArrayVec<[T; SPARSE_MAX]>,
+    elems: ArrayVec<T, SPARSE_MAX>,
 }
 
 impl<T: Idx> SparseBitSet<T> {
index 3ced3920cfdfe82118fa0914c970dd0bf98c8b97..4c73b7bf612c7c07f9ca7de161f7b4f2ffe11577 100644 (file)
@@ -1,5 +1,5 @@
 #![feature(allow_internal_unstable)]
-#![feature(const_fn)]
+#![feature(bench_black_box)]
 #![feature(const_panic)]
 #![feature(extend_one)]
 #![feature(iter_zip)]
index debf108253793964800bbadce78277f02d66ca06..30214e94203d81d078d413709ea2365f8b8487fa 100644 (file)
@@ -191,7 +191,7 @@ pub fn super_combine_consts<R>(
     ///
     /// This also tests if the given const `ct` contains an inference variable which was previously
     /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
-    /// would result in an infinite type as we continously replace an inference variable
+    /// would result in an infinite type as we continuously replace an inference variable
     /// in `ct` with `ct` itself.
     ///
     /// This is especially important as unevaluated consts use their parents generics.
index fc9ea07866c214bdb1c56a4a2e23563f9c478dce..077d2cc20a25c3fdaf7bc8f20c4325f024baf5d1 100644 (file)
@@ -279,7 +279,7 @@ fn relate_projection_ty(
     /// Relate a type inference variable with a value type. This works
     /// by creating a "generalization" G of the value where all the
     /// lifetimes are replaced with fresh inference values. This
-    /// genearlization G becomes the value of the inference variable,
+    /// generalization G becomes the value of the inference variable,
     /// and is then related in turn to the value. So e.g. if you had
     /// `vid = ?0` and `value = &'a u32`, we might first instantiate
     /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable,
index 25a262d7e482b6e1e77c5c90139da39d81b8444d..15b4a7ed2071784eef55ce060f6f8bfd4f39470e 100644 (file)
@@ -16,7 +16,6 @@
 #![feature(bool_to_option)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
-#![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(extend_one)]
 #![feature(iter_zip)]
index 3bfe8da505f1c61dac8f06bcb648be1a4a4d8b32..194464dd0bf9c974c76392cd03c100569d9eebfb 100644 (file)
@@ -45,7 +45,7 @@ rustc_query_impl = { path = "../rustc_query_impl" }
 rustc_resolve = { path = "../rustc_resolve" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_ty_utils = { path = "../rustc_ty_utils" }
-tempfile = "3.0.5"
+tempfile = "3.2"
 
 [target.'cfg(windows)'.dependencies]
 winapi = { version = "0.3", features = ["libloaderapi"] }
index 2270b2b33e2bab2476bb9cb0399b9bacb9487249..9685d21762b7c0948d78f5d3fd5603fd70653ed1 100644 (file)
@@ -405,7 +405,6 @@ macro_rules! untracked {
     untracked!(incremental, Some(String::from("abc")));
     // `link_arg` is omitted because it just forwards to `link_args`.
     untracked!(link_args, vec![String::from("abc"), String::from("def")]);
-    untracked!(link_dead_code, Some(true));
     untracked!(link_self_contained, Some(true));
     untracked!(linker, Some(PathBuf::from("linker")));
     untracked!(linker_flavor, Some(LinkerFlavor::Gcc));
@@ -433,6 +432,7 @@ macro_rules! tracked {
     tracked!(force_unwind_tables, Some(true));
     tracked!(inline_threshold, Some(0xf007ba11));
     tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
+    tracked!(link_dead_code, Some(true));
     tracked!(llvm_args, vec![String::from("1"), String::from("2")]);
     tracked!(lto, LtoCli::Fat);
     tracked!(metadata, vec![String::from("A"), String::from("B")]);
index 7f9e459635a76ca2165a25a7bc8f51d200494dca..3965a3dcdfd5d9d623a2d2a634cb553f2fb02829 100644 (file)
@@ -3022,7 +3022,7 @@ fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
             false
         }
 
-        /// test if experssion is the literal `0`
+        /// test if expression is the literal `0`
         fn is_zero(expr: &hir::Expr<'_>) -> bool {
             match &expr.kind {
                 rustc_hir::ExprKind::Lit(ref lit) => {
index 647ecad046962c551642c1c8595c0d5edfffbb26..54fcaef414f2238169f2a991b901b631ebc111bd 100644 (file)
@@ -109,6 +109,7 @@ fn visit_pat(&mut self, p: &'a ast::Pat) {
 
     fn visit_anon_const(&mut self, c: &'a ast::AnonConst) {
         run_early_pass!(self, check_anon_const, c);
+        self.check_id(c.id);
         ast_visit::walk_anon_const(self, c);
     }
 
index e0857ad1eb997c11eaf9efc533bb276233c1d730..54909381a10a509dd1075146f6d399e7f5a6e815 100644 (file)
@@ -236,10 +236,9 @@ pub(crate) fn push(
                 Some(lvl) => lvl,
             };
 
-            let meta = unwrap_or!(attr.meta(), continue);
             self.sess.mark_attr_used(attr);
 
-            let mut metas = unwrap_or!(meta.meta_item_list(), continue);
+            let mut metas = unwrap_or!(attr.meta_item_list(), continue);
 
             if metas.is_empty() {
                 // FIXME (#55112): issue unused-attributes lint for `#[level()]`
@@ -255,8 +254,6 @@ pub(crate) fn push(
                     ast::MetaItemKind::Word => {} // actual lint names handled later
                     ast::MetaItemKind::NameValue(ref name_value) => {
                         if item.path == sym::reason {
-                            // found reason, reslice meta list to exclude it
-                            metas = &metas[0..metas.len() - 1];
                             // FIXME (#55112): issue unused-attributes lint if we thereby
                             // don't have any lint names (`#[level(reason = "foo")]`)
                             if let ast::LitKind::Str(rationale, _) = name_value.kind {
@@ -275,6 +272,8 @@ pub(crate) fn push(
                                     .span_label(name_value.span, "reason must be a string literal")
                                     .emit();
                             }
+                            // found reason, reslice meta list to exclude it
+                            metas.pop().unwrap();
                         } else {
                             bad_attr(item.span)
                                 .span_label(item.span, "bad attribute argument")
@@ -288,10 +287,10 @@ pub(crate) fn push(
             }
 
             for li in metas {
-                let meta_item = match li.meta_item() {
-                    Some(meta_item) if meta_item.is_word() => meta_item,
+                let sp = li.span();
+                let mut meta_item = match li {
+                    ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
                     _ => {
-                        let sp = li.span();
                         let mut err = bad_attr(sp);
                         let mut add_label = true;
                         if let Some(item) = li.meta_item() {
@@ -330,15 +329,19 @@ pub(crate) fn push(
                         continue;
                     }
 
-                    Some(tool_ident.name)
+                    Some(meta_item.path.segments.remove(0).ident.name)
                 } else {
                     None
                 };
-                let name = meta_item.path.segments.last().expect("empty lint name").ident.name;
-                let lint_result = store.check_lint_name(&name.as_str(), tool_name);
+                let name = pprust::path_to_string(&meta_item.path);
+                let lint_result = store.check_lint_name(&name, tool_name);
                 match &lint_result {
                     CheckLintNameResult::Ok(ids) => {
-                        let src = LintLevelSource::Node(name, li.span(), reason);
+                        let src = LintLevelSource::Node(
+                            meta_item.path.segments.last().expect("empty lint name").ident.name,
+                            sp,
+                            reason,
+                        );
                         for &id in *ids {
                             self.check_gated_lint(id, attr.span);
                             self.insert_spec(&mut specs, id, (level, src));
@@ -351,7 +354,7 @@ pub(crate) fn push(
                                 let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
                                 let src = LintLevelSource::Node(
                                     Symbol::intern(complete_name),
-                                    li.span(),
+                                    sp,
                                     reason,
                                 );
                                 for id in ids {
@@ -367,7 +370,7 @@ pub(crate) fn push(
                                     lint,
                                     lvl,
                                     src,
-                                    Some(li.span().into()),
+                                    Some(sp.into()),
                                     |lint| {
                                         let msg = format!(
                                             "lint name `{}` is deprecated \
@@ -376,7 +379,7 @@ pub(crate) fn push(
                                         );
                                         lint.build(&msg)
                                             .span_suggestion(
-                                                li.span(),
+                                                sp,
                                                 "change it to",
                                                 new_lint_name.to_string(),
                                                 Applicability::MachineApplicable,
@@ -387,7 +390,7 @@ pub(crate) fn push(
 
                                 let src = LintLevelSource::Node(
                                     Symbol::intern(&new_lint_name),
-                                    li.span(),
+                                    sp,
                                     reason,
                                 );
                                 for id in ids {
@@ -414,12 +417,12 @@ pub(crate) fn push(
                             lint,
                             renamed_lint_level,
                             src,
-                            Some(li.span().into()),
+                            Some(sp.into()),
                             |lint| {
                                 let mut err = lint.build(&msg);
                                 if let Some(new_name) = &renamed {
                                     err.span_suggestion(
-                                        li.span(),
+                                        sp,
                                         "use the new name",
                                         new_name.to_string(),
                                         Applicability::MachineApplicable,
@@ -433,30 +436,23 @@ pub(crate) fn push(
                         let lint = builtin::UNKNOWN_LINTS;
                         let (level, src) =
                             self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
-                        struct_lint_level(
-                            self.sess,
-                            lint,
-                            level,
-                            src,
-                            Some(li.span().into()),
-                            |lint| {
-                                let name = if let Some(tool_name) = tool_name {
-                                    format!("{}::{}", tool_name, name)
-                                } else {
-                                    name.to_string()
-                                };
-                                let mut db = lint.build(&format!("unknown lint: `{}`", name));
-                                if let Some(suggestion) = suggestion {
-                                    db.span_suggestion(
-                                        li.span(),
-                                        "did you mean",
-                                        suggestion.to_string(),
-                                        Applicability::MachineApplicable,
-                                    );
-                                }
-                                db.emit();
-                            },
-                        );
+                        struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
+                            let name = if let Some(tool_name) = tool_name {
+                                format!("{}::{}", tool_name, name)
+                            } else {
+                                name.to_string()
+                            };
+                            let mut db = lint.build(&format!("unknown lint: `{}`", name));
+                            if let Some(suggestion) = suggestion {
+                                db.span_suggestion(
+                                    sp,
+                                    "did you mean",
+                                    suggestion.to_string(),
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                            db.emit();
+                        });
                     }
                 }
                 // If this lint was renamed, apply the new lint instead of ignoring the attribute.
@@ -466,8 +462,7 @@ pub(crate) fn push(
                     // Ignore any errors or warnings that happen because the new name is inaccurate
                     // NOTE: `new_name` already includes the tool name, so we don't have to add it again.
                     if let CheckLintNameResult::Ok(ids) = store.check_lint_name(&new_name, None) {
-                        let src =
-                            LintLevelSource::Node(Symbol::intern(&new_name), li.span(), reason);
+                        let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
                         for &id in ids {
                             self.check_gated_lint(id, attr.span);
                             self.insert_spec(&mut specs, id, (level, src));
index a1c7e47e749add17a11cfc1d52536fe937342371..301e607fc58ada7bb586eafb47bdb994895224bc 100644 (file)
@@ -10,7 +10,6 @@
     ///
     /// ```rust,compile_fail
     /// # #![allow(unused)]
-    /// #![feature(non_ascii_idents)]
     /// #![deny(non_ascii_idents)]
     /// fn main() {
     ///     let föö = 1;
     ///
     /// ### Explanation
     ///
-    /// Currently on stable Rust, identifiers must contain ASCII characters.
-    /// The [`non_ascii_idents`] nightly-only feature allows identifiers to
-    /// contain non-ASCII characters. This lint allows projects that wish to
-    /// retain the limit of only using ASCII characters to switch this lint to
-    /// "forbid" (for example to ease collaboration or for security reasons).
+    /// This lint allows projects that wish to retain the limit of only using
+    /// ASCII characters to switch this lint to "forbid" (for example to ease
+    /// collaboration or for security reasons).
     /// See [RFC 2457] for more details.
     ///
-    /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
     /// [RFC 2457]: https://github.com/rust-lang/rfcs/blob/master/text/2457-non-ascii-idents.md
     pub NON_ASCII_IDENTS,
     Allow,
@@ -44,7 +40,6 @@
     ///
     /// ```rust
     /// # #![allow(unused)]
-    /// #![feature(non_ascii_idents)]
     /// const µ: f64 = 0.000001;
     /// ```
     ///
     ///
     /// ### Explanation
     ///
-    /// With the [`non_ascii_idents`] nightly-only feature enabled,
-    /// identifiers are allowed to use non-ASCII characters. This lint warns
-    /// about using characters which are not commonly used, and may cause
-    /// visual confusion.
+    /// This lint warns about using characters which are not commonly used, and may
+    /// cause visual confusion.
     ///
     /// This lint is triggered by identifiers that contain a codepoint that is
     /// not part of the set of "Allowed" codepoints as described by [Unicode®
@@ -66,7 +59,6 @@
     /// that if you "forbid" this lint that existing code may fail in the
     /// future.
     ///
-    /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
     /// [TR39Allowed]: https://www.unicode.org/reports/tr39/#General_Security_Profile
     pub UNCOMMON_CODEPOINTS,
     Warn,
@@ -81,8 +73,6 @@
     /// ### Example
     ///
     /// ```rust
-    /// #![feature(non_ascii_idents)]
-    ///
     /// // Latin Capital Letter E With Caron
     /// pub const Ě: i32 = 1;
     /// // Latin Capital Letter E With Breve
     ///
     /// ### Explanation
     ///
-    /// With the [`non_ascii_idents`] nightly-only feature enabled,
-    /// identifiers are allowed to use non-ASCII characters. This lint warns
-    /// when different identifiers may appear visually similar, which can
-    /// cause confusion.
+    /// This lint warns when different identifiers may appear visually similar,
+    /// which can cause confusion.
     ///
     /// The confusable detection algorithm is based on [Unicode® Technical
     /// Standard #39 Unicode Security Mechanisms Section 4 Confusable
     /// Beware that if you "forbid" this lint that existing code may fail in
     /// the future.
     ///
-    /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
     /// [TR39Confusable]: https://www.unicode.org/reports/tr39/#Confusable_Detection
     pub CONFUSABLE_IDENTS,
     Warn,
     /// ### Example
     ///
     /// ```rust
-    /// #![feature(non_ascii_idents)]
-    ///
     /// // The Japanese katakana character エ can be confused with the Han character 工.
     /// const エ: &'static str = "アイウ";
     /// ```
     ///
     /// ### Explanation
     ///
-    /// With the [`non_ascii_idents`] nightly-only feature enabled,
-    /// identifiers are allowed to use non-ASCII characters. This lint warns
-    /// when characters between different scripts may appear visually similar,
-    /// which can cause confusion.
+    /// This lint warns when characters between different scripts may appear
+    /// visually similar, which can cause confusion.
     ///
     /// If the crate contains other identifiers in the same script that have
     /// non-confusable characters, then this lint will *not* be issued. For
     /// Note that the set of confusable characters may change over time.
     /// Beware that if you "forbid" this lint that existing code may fail in
     /// the future.
-    ///
-    /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html
     pub MIXED_SCRIPT_CONFUSABLES,
     Warn,
     "detects Unicode scripts whose mixed script confusables codepoints are solely used",
index ad42366b04d1b7c5b722eb09c01938eeacb02882..04e45e2351b56e416268a89e57a91936726d06a2 100644 (file)
     ///
     /// On x86, `asm!` uses the intel assembly syntax by default. While this
     /// can be switched using assembler directives like `.att_syntax`, using the
-    /// `att_syntax` option is recomended instead because it will also properly
+    /// `att_syntax` option is recommended instead because it will also properly
     /// prefix register placeholders with `%` as required by AT&T syntax.
     pub BAD_ASM_STYLE,
     Warn,
     /// Statics with an uninhabited type can never be initialized, so they are impossible to define.
     /// However, this can be side-stepped with an `extern static`, leading to problems later in the
     /// compiler which assumes that there are no initialized uninhabited places (such as locals or
-    /// statics). This was accientally allowed, but is being phased out.
+    /// statics). This was accidentally allowed, but is being phased out.
     pub UNINHABITED_STATIC,
     Warn,
     "uninhabited static",
     };
 }
 
+declare_lint! {
+    /// The `large_assignments` lint detects when objects of large
+    /// types are being moved around.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (can crash on some platforms)
+    /// let x = [0; 50000];
+    /// let y = x;
+    /// ```
+    ///
+    /// produces:
+    ///
+    /// ```text
+    /// warning: moving a large value
+    ///   --> $DIR/move-large.rs:1:3
+    ///   let y = x;
+    ///           - Copied large value here
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// When using a large type in a plain assignment or in a function
+    /// argument, idiomatic code can be inefficient.
+    /// Ideally appropriate optimizations would resolve this, but such
+    /// optimizations are only done in a best-effort manner.
+    /// This lint will trigger on all sites of large moves and thus allow the
+    /// user to resolve them in code.
+    pub LARGE_ASSIGNMENTS,
+    Warn,
+    "detects large moves or copies",
+}
+
 declare_lint_pass! {
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// that are used by other parts of the compiler.
         LEGACY_DERIVE_HELPERS,
         PROC_MACRO_BACK_COMPAT,
         OR_PATTERNS_BACK_COMPAT,
+        LARGE_ASSIGNMENTS,
     ]
 }
 
index d6db69c748fb568aaa339ae8e01046920d3156b0..2e135fbe2bd8fe7b271c496288c66c730565faf4 100644 (file)
@@ -349,8 +349,10 @@ extern "C" void LLVMRustRemoveFunctionAttributes(LLVMValueRef Fn,
   F->setAttributes(PALNew);
 }
 
-// enable fpmath flag UnsafeAlgebra
-extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) {
+// Enable a fast-math flag
+//
+// https://llvm.org/docs/LangRef.html#fast-math-flags
+extern "C" void LLVMRustSetFastMath(LLVMValueRef V) {
   if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) {
     I->setFast(true);
   }
@@ -1298,9 +1300,19 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) {
 
 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
 
+#if LLVM_VERSION_LT(13, 0)
+using LLVMInlineAsmDiagHandlerTy = LLVMContext::InlineAsmDiagHandlerTy;
+#else
+using LLVMInlineAsmDiagHandlerTy = void*;
+#endif
+
 extern "C" void LLVMRustSetInlineAsmDiagnosticHandler(
-    LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) {
+    LLVMContextRef C, LLVMInlineAsmDiagHandlerTy H, void *CX) {
+  // Diagnostic handlers were unified in LLVM change 5de2d189e6ad, so starting
+  // with LLVM 13 this function is gone.
+#if LLVM_VERSION_LT(13, 0)
   unwrap(C)->setInlineAsmDiagnosticHandler(H, CX);
+#endif
 }
 
 extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef,
index 26db3a5f39d7c80e8ee0aa1b2960a13c5ecfc3b9..e9ae22f8cedbc47926fd0ed4aa39e9ae0469fa10 100644 (file)
@@ -511,8 +511,11 @@ fn resolve_crate<'b>(
         if dep.is_none() {
             self.used_extern_options.insert(name);
         }
-        self.maybe_resolve_crate(name, dep_kind, dep)
-            .unwrap_or_else(|err| err.report(self.sess, span))
+        self.maybe_resolve_crate(name, dep_kind, dep).unwrap_or_else(|err| {
+            let missing_core =
+                self.maybe_resolve_crate(sym::core, CrateDepKind::Explicit, None).is_err();
+            err.report(&self.sess, span, missing_core)
+        })
     }
 
     fn maybe_resolve_crate<'b>(
index 7f6311861c1b28177421870b8be7ef36774d8d53..6e7360950908e9dbd164ff0c7f7ecfebb93f4b91 100644 (file)
@@ -790,7 +790,8 @@ pub fn find_plugin_registrar(
 ) -> (PathBuf, CrateDisambiguator) {
     match find_plugin_registrar_impl(sess, metadata_loader, name) {
         Ok(res) => res,
-        Err(err) => err.report(sess, span),
+        // `core` is always available if we got as far as loading plugins.
+        Err(err) => err.report(sess, span, false),
     }
 }
 
@@ -883,7 +884,7 @@ struct CrateMismatch {
 }
 
 impl CrateError {
-    crate fn report(self, sess: &Session, span: Span) -> ! {
+    crate fn report(self, sess: &Session, span: Span, missing_core: bool) -> ! {
         let mut err = match self {
             CrateError::NonAsciiName(crate_name) => sess.struct_span_err(
                 span,
@@ -1068,7 +1069,37 @@ impl CrateError {
                     if (crate_name == sym::std || crate_name == sym::core)
                         && locator.triple != TargetTriple::from_triple(config::host_triple())
                     {
-                        err.note(&format!("the `{}` target may not be installed", locator.triple));
+                        if missing_core {
+                            err.note(&format!(
+                                "the `{}` target may not be installed",
+                                locator.triple
+                            ));
+                        } else {
+                            err.note(&format!(
+                                "the `{}` target may not support the standard library",
+                                locator.triple
+                            ));
+                        }
+                        if missing_core && std::env::var("RUSTUP_HOME").is_ok() {
+                            err.help(&format!(
+                                "consider downloading the target with `rustup target add {}`",
+                                locator.triple
+                            ));
+                        }
+                        // Suggest using #![no_std]. #[no_core] is unstable and not really supported anyway.
+                        // NOTE: this is a dummy span if `extern crate std` was injected by the compiler.
+                        // If it's not a dummy, that means someone added `extern crate std` explicitly and `#![no_std]` won't help.
+                        if !missing_core && span.is_dummy() {
+                            let current_crate =
+                                sess.opts.crate_name.as_deref().unwrap_or("<unknown>");
+                            err.note(&format!(
+                                "`std` is required by `{}` because it does not declare `#![no_std]`",
+                                current_crate
+                            ));
+                        }
+                        if sess.is_nightly_build() && std::env::var("CARGO").is_ok() {
+                            err.help("consider building the standard library from source with `cargo build -Zbuild-std`");
+                        }
                     } else if crate_name == sym::profiler_builtins {
                         err.note(&"the compiler may have been built without the profiler runtime");
                     }
index 3d0a9d553b0281a4f579397daa5abf199ae0b9f6..19ae5ce69c1365d33062a679284a29772ff23626 100644 (file)
@@ -757,6 +757,7 @@ fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef {
                     data.paren_sugar,
                     data.has_auto_impl,
                     data.is_marker,
+                    data.skip_array_during_method_dispatch,
                     data.specialization_kind,
                     self.def_path_hash(item_id),
                 )
@@ -767,6 +768,7 @@ fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef {
                 false,
                 false,
                 false,
+                false,
                 ty::trait_def::TraitSpecializationKind::None,
                 self.def_path_hash(item_id),
             ),
index a5157854e15c0511749fab3f82903d9b671b73ee..e8f02b8e66f0a9f03caaabd16d90d6c786fba389 100644 (file)
@@ -1422,6 +1422,7 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item<'tcx>) {
                     paren_sugar: trait_def.paren_sugar,
                     has_auto_impl: self.tcx.trait_is_auto(def_id),
                     is_marker: trait_def.is_marker,
+                    skip_array_during_method_dispatch: trait_def.skip_array_during_method_dispatch,
                     specialization_kind: trait_def.specialization_kind,
                 };
 
index 7cfb051e703c59640947e17db82e6dfe1f30d2d2..9f665d5daaa032c9b1aabf4c5eca708c4eda1521 100644 (file)
@@ -385,6 +385,7 @@ struct TraitData {
     paren_sugar: bool,
     has_auto_impl: bool,
     is_marker: bool,
+    skip_array_during_method_dispatch: bool,
     specialization_kind: ty::trait_def::TraitSpecializationKind,
 }
 
index ba9d0a40732e6bec841cc6a239bdd9fa61565618..aa54d1ae7b9d118426d9702dac6384725e0b50c4 100644 (file)
@@ -32,8 +32,8 @@
 //! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro
 //! defines the `DepKind` enum. Each `DepKind` has its own parameters that are
 //! needed at runtime in order to construct a valid `DepNode` fingerprint.
-//! However, only `CompileCodegenUnit` is constructed explicitly (with
-//! `make_compile_codegen_unit`).
+//! However, only `CompileCodegenUnit` and `CompileMonoItem` are constructed
+//! explicitly (with `make_compile_codegen_unit` cq `make_compile_mono_item`).
 //!
 //! Because the macro sees what parameters a given `DepKind` requires, it can
 //! "infer" some properties for each kind of `DepNode`:
 //!   `DefId` it was computed from. In other cases, too much information gets
 //!   lost during fingerprint computation.
 //!
-//! `make_compile_codegen_unit`, together with `DepNode::new()`, ensures that only
-//! valid `DepNode` instances can be constructed. For example, the API does not
-//! allow for constructing parameterless `DepNode`s with anything other
-//! than a zeroed out fingerprint. More generally speaking, it relieves the
-//! user of the `DepNode` API of having to know how to compute the expected
-//! fingerprint for a given set of node parameters.
+//! `make_compile_codegen_unit` and `make_compile_mono_items`, together with
+//! `DepNode::new()`, ensures that only valid `DepNode` instances can be
+//! constructed. For example, the API does not allow for constructing
+//! parameterless `DepNode`s with anything other than a zeroed out fingerprint.
+//! More generally speaking, it relieves the user of the `DepNode` API of
+//! having to know how to compute the expected fingerprint for a given set of
+//! node parameters.
 //!
 //! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html
 
+use crate::mir::mono::MonoItem;
 use crate::ty::TyCtxt;
 
 use rustc_data_structures::fingerprint::Fingerprint;
@@ -175,6 +177,14 @@ pub mod dep_kind {
         can_reconstruct_query_key: || false,
     };
 
+    pub const CompileMonoItem: DepKindStruct = DepKindStruct {
+        has_params: true,
+        is_anon: false,
+        is_eval_always: false,
+
+        can_reconstruct_query_key: || false,
+    };
+
     macro_rules! define_query_dep_kinds {
         ($(
             [$($attrs:tt)*]
@@ -251,6 +261,10 @@ pub mod label_strs {
 
     // WARNING: if `Symbol` is changed, make sure you update `make_compile_codegen_unit` below.
     [] CompileCodegenUnit(Symbol),
+
+    // WARNING: if `MonoItem` is changed, make sure you update `make_compile_mono_item` below.
+    // Only used by rustc_codegen_cranelift
+    [] CompileMonoItem(MonoItem),
 ]);
 
 // WARNING: `construct` is generic and does not know that `CompileCodegenUnit` takes `Symbol`s as keys.
@@ -259,6 +273,12 @@ pub mod label_strs {
     DepNode::construct(tcx, DepKind::CompileCodegenUnit, &name)
 }
 
+// WARNING: `construct` is generic and does not know that `CompileMonoItem` takes `MonoItem`s as keys.
+// Be very careful changing this type signature!
+crate fn make_compile_mono_item(tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>) -> DepNode {
+    DepNode::construct(tcx, DepKind::CompileMonoItem, mono_item)
+}
+
 pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>;
 
 // We keep a lot of `DepNode`s in memory during compilation. It's not
index d2fe9af34fb625a8a88ae9e94cd4f89f661ddaec..31bea8329587d7469350bd4bc8c550f2a9fbf029 100644 (file)
@@ -12,8 +12,8 @@
     SerializedDepNodeIndex, WorkProduct, WorkProductId,
 };
 
-crate use dep_node::make_compile_codegen_unit;
 pub use dep_node::{label_strs, DepKind, DepNode, DepNodeExt};
+crate use dep_node::{make_compile_codegen_unit, make_compile_mono_item};
 
 pub type DepGraph = rustc_query_system::dep_graph::DepGraph<DepKind>;
 pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps<DepKind>;
index 1db03e9165b8825f73c013f452b8acc185f2d470..45ea07a3db6b607960edcba5f96514cbc0d964de 100644 (file)
@@ -29,7 +29,6 @@
 #![feature(bool_to_option)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
-#![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(core_intrinsics)]
 #![feature(discriminant_kind)]
index 7024d9a3d21dbdb63c6042512822dfd009051b9a..93e7aeaffce3771ea62e15a0a0552b8000107fd6 100644 (file)
@@ -89,6 +89,10 @@ pub struct CodegenFnAttrFlags: u32 {
         /// #[cmse_nonsecure_entry]: with a TrustZone-M extension, declare a
         /// function as an entry function from Non-Secure code.
         const CMSE_NONSECURE_ENTRY      = 1 << 14;
+        /// `#[no_coverage]`: indicates that the function should be ignored by
+        /// the MIR `InstrumentCoverage` pass and not added to the coverage map
+        /// during codegen.
+        const NO_COVERAGE               = 1 << 15;
     }
 }
 
index 61f850c2fc166fa86da21a1b1462158ea5372bbf..601198fd0de0443090df30821db510102dee1d82 100644 (file)
@@ -1,4 +1,8 @@
-//! Registering limits, recursion_limit, type_length_limit and const_eval_limit
+//! Registering limits:
+//! * recursion_limit,
+//! * move_size_limit,
+//! * type_length_limit, and
+//! * const_eval_limit
 //!
 //! There are various parts of the compiler that must impose arbitrary limits
 //! on how deeply they recurse to prevent stack overflow. Users can override
 use crate::bug;
 use rustc_ast as ast;
 use rustc_data_structures::sync::OnceCell;
-use rustc_session::{Limit, Session};
+use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
 
 use std::num::IntErrorKind;
 
 pub fn update_limits(sess: &Session, krate: &ast::Crate) {
     update_limit(sess, krate, &sess.recursion_limit, sym::recursion_limit, 128);
+    update_limit(sess, krate, &sess.move_size_limit, sym::move_size_limit, 0);
     update_limit(sess, krate, &sess.type_length_limit, sym::type_length_limit, 1048576);
     update_limit(sess, krate, &sess.const_eval_limit, sym::const_eval_limit, 1_000_000);
 }
@@ -22,7 +27,7 @@ pub fn update_limits(sess: &Session, krate: &ast::Crate) {
 fn update_limit(
     sess: &Session,
     krate: &ast::Crate,
-    limit: &OnceCell<Limit>,
+    limit: &OnceCell<impl From<usize> + std::fmt::Debug>,
     name: Symbol,
     default: usize,
 ) {
@@ -34,7 +39,7 @@ fn update_limit(
         if let Some(s) = attr.value_str() {
             match s.as_str().parse() {
                 Ok(n) => {
-                    limit.set(Limit::new(n)).unwrap();
+                    limit.set(From::from(n)).unwrap();
                     return;
                 }
                 Err(e) => {
@@ -63,5 +68,5 @@ fn update_limit(
             }
         }
     }
-    limit.set(Limit::new(default)).unwrap();
+    limit.set(From::from(default)).unwrap();
 }
index 5440e63543d40a83e9d8973fcb572f486a4ca3b6..f44267a404bf301ae3ea8ebf884f52b91319617d 100644 (file)
@@ -235,18 +235,6 @@ pub struct ScopeTree {
     /// escape into 'static and should have no local cleanup scope.
     rvalue_scopes: FxHashMap<hir::ItemLocalId, Option<Scope>>,
 
-    /// Encodes the hierarchy of fn bodies. Every fn body (including
-    /// closures) forms its own distinct region hierarchy, rooted in
-    /// the block that is the fn body. This map points from the ID of
-    /// that root block to the ID of the root block for the enclosing
-    /// fn, if any. Thus the map structures the fn bodies into a
-    /// hierarchy based on their lexical mapping. This is used to
-    /// handle the relationships between regions in a fn and in a
-    /// closure defined by that fn. See the "Modeling closures"
-    /// section of the README in infer::region_constraints for
-    /// more details.
-    closure_tree: FxHashMap<hir::ItemLocalId, hir::ItemLocalId>,
-
     /// If there are any `yield` nested within a scope, this map
     /// stores the `Span` of the last one and its index in the
     /// postorder of the Visitor traversal on the HIR.
@@ -356,23 +344,6 @@ pub fn opt_destruction_scope(&self, n: hir::ItemLocalId) -> Option<Scope> {
         self.destruction_scopes.get(&n).cloned()
     }
 
-    /// Records that `sub_closure` is defined within `sup_closure`. These IDs
-    /// should be the ID of the block that is the fn body, which is
-    /// also the root of the region hierarchy for that fn.
-    pub fn record_closure_parent(
-        &mut self,
-        sub_closure: hir::ItemLocalId,
-        sup_closure: hir::ItemLocalId,
-    ) {
-        debug!(
-            "record_closure_parent(sub_closure={:?}, sup_closure={:?})",
-            sub_closure, sup_closure
-        );
-        assert!(sub_closure != sup_closure);
-        let previous = self.closure_tree.insert(sub_closure, sup_closure);
-        assert!(previous.is_none());
-    }
-
     pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) {
         debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
         assert!(var != lifetime.item_local_id());
@@ -474,7 +445,6 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
             ref var_map,
             ref destruction_scopes,
             ref rvalue_scopes,
-            ref closure_tree,
             ref yield_in_scope,
         } = *self;
 
@@ -488,7 +458,6 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
         var_map.hash_stable(hcx, hasher);
         destruction_scopes.hash_stable(hcx, hasher);
         rvalue_scopes.hash_stable(hcx, hasher);
-        closure_tree.hash_stable(hcx, hasher);
         yield_in_scope.hash_stable(hcx, hasher);
     }
 }
index 3b8a4adfb930aa3d2ddde93b8de23b1361c31fbe..cc0df127434e50b3967fdc01e9d4fb9611f56540 100644 (file)
@@ -45,7 +45,7 @@ pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'
 
 /// Packages the kind of error we got from the const code interpreter
 /// up with a Rust-level backtrace of where the error occurred.
-/// Thsese should always be constructed by calling `.into()` on
+/// These should always be constructed by calling `.into()` on
 /// a `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
 /// macros for this.
 #[derive(Debug)]
index 89f456400940e12c9e5e11601d310dec31cd097b..e22c0b40d5a53067abb7c1c81ffc7791921e7cb0 100644 (file)
 use crate::ty::subst::{Subst, SubstsRef};
 use crate::ty::{self, List, Ty, TyCtxt};
 use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
-use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc_hir::{self, GeneratorKind};
+use rustc_hir::{self as hir, HirId};
 use rustc_target::abi::{Size, VariantIdx};
 
 use polonius_engine::Atom;
@@ -1521,7 +1521,7 @@ pub enum StatementKind<'tcx> {
     /// Marks the start of a "coverage region", injected with '-Zinstrument-coverage'. A
     /// `Coverage` statement carries metadata about the coverage region, used to inject a coverage
     /// map into the binary. If `Coverage::kind` is a `Counter`, the statement also generates
-    /// executable code, to increment a counter varible at runtime, each time the code region is
+    /// executable code, to increment a counter variable at runtime, each time the code region is
     /// executed.
     Coverage(Box<Coverage>),
 
@@ -1948,6 +1948,29 @@ pub struct SourceScope {
     }
 }
 
+impl SourceScope {
+    /// Finds the original HirId this MIR item came from.
+    /// This is necessary after MIR optimizations, as otherwise we get a HirId
+    /// from the function that was inlined instead of the function call site.
+    pub fn lint_root(
+        self,
+        source_scopes: &IndexVec<SourceScope, SourceScopeData<'tcx>>,
+    ) -> Option<HirId> {
+        let mut data = &source_scopes[self];
+        // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
+        // does not work as I thought it would. Needs more investigation and documentation.
+        while data.inlined.is_some() {
+            trace!(?data);
+            data = &source_scopes[data.parent_scope.unwrap()];
+        }
+        trace!(?data);
+        match &data.local_data {
+            ClearCrossCrate::Set(data) => Some(data.lint_root),
+            ClearCrossCrate::Clear => None,
+        }
+    }
+}
+
 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
 pub struct SourceScopeData<'tcx> {
     pub span: Span,
index 6c2468b9ffe0b1c937d2a1fc53d5fb331d35668d..92a1094bbcdc1bbdfb3bb7b6953bac7d8574c8a1 100644 (file)
@@ -181,6 +181,11 @@ pub fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option<Span> {
         }
         .map(|hir_id| tcx.hir().span(hir_id))
     }
+
+    // Only used by rustc_codegen_cranelift
+    pub fn codegen_dep_node(&self, tcx: TyCtxt<'tcx>) -> DepNode {
+        crate::dep_graph::make_compile_mono_item(tcx, self)
+    }
 }
 
 impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for MonoItem<'tcx> {
index ad3baccf1549616b5ebd694f4728de2255dfb9cb..fdd874c6f68224064e486c8cfe8a3ee9c46f3907 100644 (file)
 
 #[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
 pub enum UnsafetyViolationKind {
-    /// Only permitted in regular `fn`s, prohibited in `const fn`s.
+    /// Unsafe operation outside `unsafe`.
     General,
-    /// Permitted both in `const fn`s and regular `fn`s.
-    GeneralAndConstFn,
     /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block.
     /// Has to be handled as a lint for backwards compatibility.
     UnsafeFn,
index bac69e282a521672ae81467f3ae72581e2aac37c..08fa12aa3718fb8dce5e4f30faeb5744f74328c3 100644 (file)
     }
 
     /// Collects the associated items defined on a trait or impl.
-    query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> {
+    query associated_items(key: DefId) -> ty::AssocItems<'tcx> {
         storage(ArenaCacheSelector<'tcx>)
         desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) }
     }
         desc { "checking if the crate is_compiler_builtins" }
     }
     query has_global_allocator(_: CrateNum) -> bool {
+        // This query depends on untracked global state in CStore
+        eval_always
         fatal_cycle
         desc { "checking if the crate has_global_allocator" }
     }
         desc { "testing if a region is late bound" }
     }
     /// For a given item (like a struct), gets the default lifetimes to be used
-    /// for each paramter if a trait object were to be passed for that parameter.
+    /// for each parameter if a trait object were to be passed for that parameter.
     /// For example, for `struct Foo<'a, T, U>`, this would be `['static, 'static]`.
     /// For `struct Foo<'a, T: 'a, U>`, this would instead be `['a, 'static]`.
     query object_lifetime_defaults_map(_: LocalDefId)
index 00dec3b355f8f840129dfba9bf59f8fb3a218358..c9b73c682098b6fd728e8c388294de4774658beb 100644 (file)
@@ -670,6 +670,9 @@ pub enum ObjectSafetyViolation {
 
     /// Associated const.
     AssocConst(Symbol, Span),
+
+    /// GAT
+    GAT(Symbol, Span),
 }
 
 impl ObjectSafetyViolation {
@@ -715,6 +718,9 @@ pub fn error_msg(&self) -> Cow<'static, str> {
                 format!("it contains associated `const` `{}`", name).into()
             }
             ObjectSafetyViolation::AssocConst(..) => "it contains this associated `const`".into(),
+            ObjectSafetyViolation::GAT(name, _) => {
+                format!("it contains the generic associated type `{}`", name).into()
+            }
         }
     }
 
@@ -773,6 +779,7 @@ trait objects",
                 );
             }
             ObjectSafetyViolation::AssocConst(name, _)
+            | ObjectSafetyViolation::GAT(name, _)
             | ObjectSafetyViolation::Method(name, ..) => {
                 err.help(&format!("consider moving `{}` to another trait", name));
             }
@@ -786,6 +793,7 @@ trait objects",
             ObjectSafetyViolation::SupertraitSelf(spans)
             | ObjectSafetyViolation::SizedSelf(spans) => spans.clone(),
             ObjectSafetyViolation::AssocConst(_, span)
+            | ObjectSafetyViolation::GAT(_, span)
             | ObjectSafetyViolation::Method(_, _, span)
                 if *span != DUMMY_SP =>
             {
index d3770fa416b53e0d51fd2dc7b171a2407aaf0e57..d005f63ed4383b4365d9790cd715a8746000547c 100644 (file)
@@ -96,15 +96,15 @@ pub fn as_def_kind(&self) -> DefKind {
 /// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is
 /// done only on items with the same name.
 #[derive(Debug, Clone, PartialEq, HashStable)]
-pub struct AssociatedItems<'tcx> {
+pub struct AssocItems<'tcx> {
     pub(super) items: SortedIndexMultiMap<u32, Symbol, &'tcx ty::AssocItem>,
 }
 
-impl<'tcx> AssociatedItems<'tcx> {
+impl<'tcx> AssocItems<'tcx> {
     /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order.
     pub fn new(items_in_def_order: impl IntoIterator<Item = &'tcx ty::AssocItem>) -> Self {
         let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect();
-        AssociatedItems { items }
+        AssocItems { items }
     }
 
     /// Returns a slice of associated items in the order they were defined.
index 52cb6b301b070eb715772e6417315f86cdff6c9d..887a5831cd720e3a4971b877d137d1d9ff82369b 100644 (file)
@@ -160,7 +160,7 @@ pub fn get_root_variable(&self) -> hir::HirId {
         }
     }
 
-    /// Returns the `LocalDefId` of the closure that captureed this Place
+    /// Returns the `LocalDefId` of the closure that captured this Place
     pub fn get_closure_local_def_id(&self) -> LocalDefId {
         match self.place.base {
             HirPlaceBase::Upvar(upvar_id) => upvar_id.closure_expr_id,
index d30a8693959f39a17a7c381fb702fa8bcc9792be..c8fdbc30d1591cd2089819c1119da7557937d0e0 100644 (file)
@@ -36,7 +36,7 @@ pub fn to_ord(&self, tcx: TyCtxt<'_>) -> ast::ParamKindOrd {
             GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime,
             GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type,
             GenericParamDefKind::Const { .. } => {
-                ast::ParamKindOrd::Const { unordered: tcx.features().const_generics }
+                ast::ParamKindOrd::Const { unordered: tcx.features().unordered_const_ty_params() }
             }
         }
     }
index 33065bc3a7bd3f29f18fc37480fb0cd73c4abe9f..e9b8883f29a488e578ae43af77264de178b046a0 100644 (file)
@@ -35,6 +35,11 @@ pub struct TraitDef {
     /// and thus `impl`s of it are allowed to overlap.
     pub is_marker: bool,
 
+    /// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]`
+    /// attribute, indicating that editions before 2021 should not consider this trait
+    /// during method dispatch if the receiver is an array.
+    pub skip_array_during_method_dispatch: bool,
+
     /// Used to determine whether the standard library is allowed to specialize
     /// on this trait.
     pub specialization_kind: TraitSpecializationKind,
@@ -82,6 +87,7 @@ pub fn new(
         paren_sugar: bool,
         has_auto_impl: bool,
         is_marker: bool,
+        skip_array_during_method_dispatch: bool,
         specialization_kind: TraitSpecializationKind,
         def_path_hash: DefPathHash,
     ) -> TraitDef {
@@ -91,6 +97,7 @@ pub fn new(
             paren_sugar,
             has_auto_impl,
             is_marker,
+            skip_array_during_method_dispatch,
             specialization_kind,
             def_path_hash,
         }
index 7d09ca5152ffe51220243af0fe977162a9894fc5..e365928c15f918c6e28abcced78c98211a5ca3a7 100644 (file)
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::DUMMY_SP;
 use rustc_target::abi::{Integer, Size, TargetDataLayout};
 use smallvec::SmallVec;
-use std::{cmp, fmt, iter};
+use std::{fmt, iter};
 
 #[derive(Copy, Clone, Debug)]
 pub struct Discr<'tcx> {
@@ -135,21 +135,6 @@ fn disr_incr<'tcx>(&self, tcx: TyCtxt<'tcx>, val: Option<Discr<'tcx>>) -> Option
     }
 }
 
-/// Describes whether a type is representable. For types that are not
-/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to
-/// distinguish between types that are recursive with themselves and types that
-/// contain a different recursive type. These cases can therefore be treated
-/// differently when reporting errors.
-///
-/// The ordering of the cases is significant. They are sorted so that cmp::max
-/// will keep the "more erroneous" of two values.
-#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
-pub enum Representability {
-    Representable,
-    ContainsRecursive,
-    SelfRecursive(Vec<Span>),
-}
-
 impl<'tcx> TyCtxt<'tcx> {
     /// Creates a hash of the type `Ty` which will be the same no matter what crate
     /// context it's calculated within. This is used by the `type_id` intrinsic.
@@ -870,178 +855,6 @@ pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
         }
     }
 
-    /// Check whether a type is representable. This means it cannot contain unboxed
-    /// structural recursion. This check is needed for structs and enums.
-    pub fn is_representable(&'tcx self, tcx: TyCtxt<'tcx>, sp: Span) -> Representability {
-        // Iterate until something non-representable is found
-        fn fold_repr<It: Iterator<Item = Representability>>(iter: It) -> Representability {
-            iter.fold(Representability::Representable, |r1, r2| match (r1, r2) {
-                (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => {
-                    Representability::SelfRecursive(v1.into_iter().chain(v2).collect())
-                }
-                (r1, r2) => cmp::max(r1, r2),
-            })
-        }
-
-        fn are_inner_types_recursive<'tcx>(
-            tcx: TyCtxt<'tcx>,
-            sp: Span,
-            seen: &mut Vec<Ty<'tcx>>,
-            representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
-            ty: Ty<'tcx>,
-        ) -> Representability {
-            match ty.kind() {
-                Tuple(..) => {
-                    // Find non representable
-                    fold_repr(ty.tuple_fields().map(|ty| {
-                        is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty)
-                    }))
-                }
-                // Fixed-length vectors.
-                // FIXME(#11924) Behavior undecided for zero-length vectors.
-                Array(ty, _) => {
-                    is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty)
-                }
-                Adt(def, substs) => {
-                    // Find non representable fields with their spans
-                    fold_repr(def.all_fields().map(|field| {
-                        let ty = field.ty(tcx, substs);
-                        let span = match field
-                            .did
-                            .as_local()
-                            .map(|id| tcx.hir().local_def_id_to_hir_id(id))
-                            .and_then(|id| tcx.hir().find(id))
-                        {
-                            Some(hir::Node::Field(field)) => field.ty.span,
-                            _ => sp,
-                        };
-                        match is_type_structurally_recursive(
-                            tcx,
-                            span,
-                            seen,
-                            representable_cache,
-                            ty,
-                        ) {
-                            Representability::SelfRecursive(_) => {
-                                Representability::SelfRecursive(vec![span])
-                            }
-                            x => x,
-                        }
-                    }))
-                }
-                Closure(..) => {
-                    // this check is run on type definitions, so we don't expect
-                    // to see closure types
-                    bug!("requires check invoked on inapplicable type: {:?}", ty)
-                }
-                _ => Representability::Representable,
-            }
-        }
-
-        fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool {
-            match *ty.kind() {
-                Adt(ty_def, _) => ty_def == def,
-                _ => false,
-            }
-        }
-
-        // Does the type `ty` directly (without indirection through a pointer)
-        // contain any types on stack `seen`?
-        fn is_type_structurally_recursive<'tcx>(
-            tcx: TyCtxt<'tcx>,
-            sp: Span,
-            seen: &mut Vec<Ty<'tcx>>,
-            representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
-            ty: Ty<'tcx>,
-        ) -> Representability {
-            debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp);
-            if let Some(representability) = representable_cache.get(ty) {
-                debug!(
-                    "is_type_structurally_recursive: {:?} {:?} - (cached) {:?}",
-                    ty, sp, representability
-                );
-                return representability.clone();
-            }
-
-            let representability =
-                is_type_structurally_recursive_inner(tcx, sp, seen, representable_cache, ty);
-
-            representable_cache.insert(ty, representability.clone());
-            representability
-        }
-
-        fn is_type_structurally_recursive_inner<'tcx>(
-            tcx: TyCtxt<'tcx>,
-            sp: Span,
-            seen: &mut Vec<Ty<'tcx>>,
-            representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
-            ty: Ty<'tcx>,
-        ) -> Representability {
-            match ty.kind() {
-                Adt(def, _) => {
-                    {
-                        // Iterate through stack of previously seen types.
-                        let mut iter = seen.iter();
-
-                        // The first item in `seen` is the type we are actually curious about.
-                        // We want to return SelfRecursive if this type contains itself.
-                        // It is important that we DON'T take generic parameters into account
-                        // for this check, so that Bar<T> in this example counts as SelfRecursive:
-                        //
-                        // struct Foo;
-                        // struct Bar<T> { x: Bar<Foo> }
-
-                        if let Some(&seen_type) = iter.next() {
-                            if same_struct_or_enum(seen_type, *def) {
-                                debug!("SelfRecursive: {:?} contains {:?}", seen_type, ty);
-                                return Representability::SelfRecursive(vec![sp]);
-                            }
-                        }
-
-                        // We also need to know whether the first item contains other types
-                        // that are structurally recursive. If we don't catch this case, we
-                        // will recurse infinitely for some inputs.
-                        //
-                        // It is important that we DO take generic parameters into account
-                        // here, so that code like this is considered SelfRecursive, not
-                        // ContainsRecursive:
-                        //
-                        // struct Foo { Option<Option<Foo>> }
-
-                        for &seen_type in iter {
-                            if ty::TyS::same_type(ty, seen_type) {
-                                debug!("ContainsRecursive: {:?} contains {:?}", seen_type, ty);
-                                return Representability::ContainsRecursive;
-                            }
-                        }
-                    }
-
-                    // For structs and enums, track all previously seen types by pushing them
-                    // onto the 'seen' stack.
-                    seen.push(ty);
-                    let out = are_inner_types_recursive(tcx, sp, seen, representable_cache, ty);
-                    seen.pop();
-                    out
-                }
-                _ => {
-                    // No need to push in other cases.
-                    are_inner_types_recursive(tcx, sp, seen, representable_cache, ty)
-                }
-            }
-        }
-
-        debug!("is_type_representable: {:?}", self);
-
-        // To avoid a stack overflow when checking an enum variant or struct that
-        // contains a different, structurally recursive type, maintain a stack
-        // of seen types and check recursion for each of them (issues #3008, #3779).
-        let mut seen: Vec<Ty<'_>> = Vec::new();
-        let mut representable_cache = FxHashMap::default();
-        let r = is_type_structurally_recursive(tcx, sp, &mut seen, &mut representable_cache, self);
-        debug!("is_type_representable: {:?} is {:?}", self, r);
-        r
-    }
-
     /// Peel off all reference types in this type until there are none left.
     ///
     /// This method is idempotent, i.e. `ty.peel_refs().peel_refs() == ty.peel_refs()`.
index 5fdf8a8d1ee195d322f79af77cb250f5b6888707..9f19a474ca38bdf9809915f61590294211d4a9cb 100644 (file)
@@ -197,7 +197,11 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                                 );
                             }
                         }
-                        FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
+                        FnSelfUseKind::Normal {
+                            self_arg,
+                            implicit_into_iter,
+                            is_option_or_result,
+                        } => {
                             if implicit_into_iter {
                                 err.span_label(
                                     fn_call_span,
@@ -215,6 +219,14 @@ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
                                     ),
                                 );
                             }
+                            if is_option_or_result {
+                                err.span_suggestion_verbose(
+                                    fn_call_span.shrink_to_lo(),
+                                    "consider calling `.as_ref()` to borrow the type's contents",
+                                    "as_ref().".to_string(),
+                                    Applicability::MachineApplicable,
+                                );
+                            }
                             // Avoid pointing to the same function in multiple different
                             // error messages.
                             if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
@@ -1681,7 +1693,7 @@ pub(in crate::borrow_check) fn report_illegal_reassignment(
                 if decl.can_be_made_mutable() {
                     err.span_suggestion(
                         decl.source_info.span,
-                        "make this binding mutable",
+                        "consider making this binding mutable",
                         format!("mut {}", name),
                         Applicability::MachineApplicable,
                     );
index 577d7d53814ee1d362a084d421919f8ce5c5ff06..aa9f18d99962892334fb16c6bcbcb466b15094b7 100644 (file)
@@ -573,7 +573,13 @@ pub(super) enum UseSpans<'tcx> {
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub(super) enum FnSelfUseKind<'tcx> {
     /// A normal method call of the form `receiver.foo(a, b, c)`
-    Normal { self_arg: Ident, implicit_into_iter: bool },
+    Normal {
+        self_arg: Ident,
+        implicit_into_iter: bool,
+        /// Whether the self type of the method call has an `.as_ref()` method.
+        /// Used for better diagnostics.
+        is_option_or_result: bool,
+    },
     /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
     FnOnceCall,
     /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
@@ -900,7 +906,17 @@ pub(super) fn move_spans(
                     fn_call_span.desugaring_kind(),
                     Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
                 );
-                FnSelfUseKind::Normal { self_arg, implicit_into_iter }
+                let parent_self_ty = parent
+                    .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
+                    .and_then(|did| match tcx.type_of(did).kind() {
+                        ty::Adt(def, ..) => Some(def.did),
+                        _ => None,
+                    });
+                let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
+                    tcx.is_diagnostic_item(sym::option_type, def_id)
+                        || tcx.is_diagnostic_item(sym::result_type, def_id)
+                });
+                FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result }
             });
 
             return FnSelfUse {
index 28f6508cab2dadf449e32e0a5f3380b07a977a2c..d1fb999e518ca59b46dda2ce97d4193e2a51f0a8 100644 (file)
@@ -642,15 +642,18 @@ fn maybe_body_id_of_fn(hir_map: &Map<'tcx>, id: HirId) -> Option<BodyId> {
                                         .starts_with(&original_method_ident.name.to_string())
                             })
                             .map(|ident| format!("{}()", ident))
+                            .peekable()
                     });
 
-                if let Some(suggestions) = opt_suggestions {
-                    err.span_suggestions(
-                        path_segment.ident.span,
-                        &format!("use mutable method"),
-                        suggestions,
-                        Applicability::MaybeIncorrect,
-                    );
+                if let Some(mut suggestions) = opt_suggestions {
+                    if suggestions.peek().is_some() {
+                        err.span_suggestions(
+                            path_segment.ident.span,
+                            &format!("use mutable method"),
+                            suggestions,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
                 }
             }
         };
index 3629c813bc7a05252a0b620a4eb2fb711d6299e9..7dc3434bf333867fd21707db468d8d7ed4869b79 100644 (file)
@@ -1,4 +1,4 @@
-//! Contains utilities for generating suggestions for borrowck errors related to unsatisified
+//! Contains utilities for generating suggestions for borrowck errors related to unsatisfied
 //! outlives constraints.
 
 use std::collections::BTreeMap;
index 8c18dfcb8d0cc72495b6c7a72992609b024c2b21..40419a4d201ac0b198ab799ebf509a020ea23b7e 100644 (file)
@@ -1,4 +1,3 @@
-use rustc_attr as attr;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::hir::map::blocks::FnLikeNode;
@@ -34,54 +33,6 @@ pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
     }
 }
 
-/// Returns `true` if this function must conform to `min_const_fn`
-pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
-    // Bail out if the signature doesn't contain `const`
-    if !tcx.is_const_fn_raw(def_id) {
-        return false;
-    }
-
-    if tcx.features().staged_api {
-        // In order for a libstd function to be considered min_const_fn
-        // it needs to be stable and have no `rustc_const_unstable` attribute.
-        match tcx.lookup_const_stability(def_id) {
-            // `rustc_const_unstable` functions don't need to conform.
-            Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
-            None => {
-                if let Some(stab) = tcx.lookup_stability(def_id) {
-                    if stab.level.is_stable() {
-                        tcx.sess.delay_span_bug(
-                            tcx.def_span(def_id),
-                            "stable const functions must have either `rustc_const_stable` or \
-                             `rustc_const_unstable` attribute",
-                        );
-                        // While we errored above, because we don't know if we need to conform, we
-                        // err on the "safe" side and require min_const_fn.
-                        true
-                    } else {
-                        // Unstable functions need not conform to min_const_fn.
-                        false
-                    }
-                } else {
-                    // Internal functions are forced to conform to min_const_fn.
-                    // Annotate the internal function with a const stability attribute if
-                    // you need to use unstable features.
-                    // Note: this is an arbitrary choice that does not affect stability or const
-                    // safety or anything, it just changes whether we need to annotate some
-                    // internal functions with `rustc_const_stable` or with `rustc_const_unstable`
-                    true
-                }
-            }
-            // Everything else needs to conform, because it would be callable from
-            // other `min_const_fn` functions.
-            _ => true,
-        }
-    } else {
-        // users enabling the `const_fn` feature gate can do what they want
-        !tcx.features().const_fn
-    }
-}
-
 pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
     let parent_id = tcx.hir().get_parent_did(hir_id);
     if !parent_id.is_top_level_module() { is_const_impl_raw(tcx, parent_id) } else { false }
index 61785a52729d5d091b4f98f9d8347cbd4e66c7b3..8e9148f5b6643f7a3830d5c8cea5d17b56646024 100644 (file)
@@ -53,7 +53,7 @@ fn hook_panic_fn(
 /// Extra machine state for CTFE, and the Machine instance
 pub struct CompileTimeInterpreter<'mir, 'tcx> {
     /// For now, the number of terminators that can be evaluated before we throw a resource
-    /// exhuastion error.
+    /// exhaustion error.
     ///
     /// Setting this to `0` disables the limit and allows the interpreter to run forever.
     pub steps_remaining: usize,
index 3f7808c20901b13418db34826d4b698269fecadb..344d7b9becd1955c4c69cb2c83ab970e6e032707 100644 (file)
@@ -510,7 +510,7 @@ fn next_in_backward_order(self) -> Self {
         }
     }
 
-    /// Returns `true` if the effect at `self` should be applied eariler than the effect at `other`
+    /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other`
     /// in forward order.
     fn precedes_in_forward_order(self, other: Self) -> bool {
         let ord = self
index 50a205676fe0c2faaa701aec1288a06777bbf652..e5bc9320260c64b900b25e78f01677fb1fe4a9ca 100644 (file)
@@ -225,7 +225,7 @@ pub fn to_const_int(self) -> ConstInt {
 }
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
-    /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST.
+    /// Normalize `place.ptr` to a `Pointer` if this is a place and not a ZST.
     /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
     #[inline]
     pub fn force_op_ptr(
index b0db4f9e649b31b437a8461b7677540414095381..783aa9465c395a82870c83698de366e1042a2673 100644 (file)
@@ -12,7 +12,6 @@
 #![feature(bool_to_option)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
-#![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(crate_visibility_modifier)]
 #![feature(decl_macro)]
index 1fda71d74bbf5a1a100f8a62527625fe5e8b4c46..e621bc9167d801dc3c4f2587e0ac714e8684a741 100644 (file)
 use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext};
 use rustc_session::config::EntryFnType;
+use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
 use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
+use rustc_target::abi::Size;
 use smallvec::SmallVec;
 use std::iter;
 use std::ops::Range;
@@ -753,6 +755,46 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Loc
         self.super_terminator(terminator, location);
     }
 
+    fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
+        self.super_operand(operand, location);
+        let limit = self.tcx.sess.move_size_limit();
+        if limit == 0 {
+            return;
+        }
+        let limit = Size::from_bytes(limit);
+        let ty = operand.ty(self.body, self.tcx);
+        let ty = self.monomorphize(ty);
+        let layout = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty));
+        if let Ok(layout) = layout {
+            if layout.size > limit {
+                debug!(?layout);
+                let source_info = self.body.source_info(location);
+                debug!(?source_info);
+                let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
+                debug!(?lint_root);
+                let lint_root = match lint_root {
+                    Some(lint_root) => lint_root,
+                    // This happens when the issue is in a function from a foreign crate that
+                    // we monomorphized in the current crate. We can't get a `HirId` for things
+                    // in other crates.
+                    // FIXME: Find out where to report the lint on. Maybe simply crate-level lint root
+                    // but correct span? This would make the lint at least accept crate-level lint attributes.
+                    None => return,
+                };
+                self.tcx.struct_span_lint_hir(
+                    LARGE_ASSIGNMENTS,
+                    lint_root,
+                    source_info.span,
+                    |lint| {
+                        let mut err = lint.build(&format!("moving {} bytes", layout.size.bytes()));
+                        err.span_label(source_info.span, "value moved from here");
+                        err.emit()
+                    },
+                );
+            }
+        }
+    }
+
     fn visit_local(
         &mut self,
         _place_local: &Local,
index a18c1f74524044fb127e2b4fa52790aea4cc3fdb..ffeaaf60a30cf36596fac6c03ed3b60ff387972a 100644 (file)
@@ -540,12 +540,17 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<
 pub struct UnsizingCast;
 impl NonConstOp for UnsizingCast {
     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
-        mcf_status_in_item(ccx)
+        if ccx.const_kind() != hir::ConstContext::ConstFn {
+            Status::Allowed
+        } else {
+            Status::Unstable(sym::const_fn_unsize)
+        }
     }
 
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
-        mcf_build_error(
-            ccx,
+        feature_err(
+            &ccx.tcx.sess.parse_sess,
+            sym::const_fn_unsize,
             span,
             "unsizing casts to types besides slices are not allowed in const fn",
         )
@@ -642,12 +647,17 @@ fn importance(&self) -> DiagnosticImportance {
         }
 
         fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
-            mcf_status_in_item(ccx)
+            if ccx.const_kind() != hir::ConstContext::ConstFn {
+                Status::Allowed
+            } else {
+                Status::Unstable(sym::const_fn_trait_bound)
+            }
         }
 
         fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
-            mcf_build_error(
-                ccx,
+            feature_err(
+                &ccx.tcx.sess.parse_sess,
+                sym::const_fn_trait_bound,
                 span,
                 "trait bounds other than `Sized` on const fn parameters are unstable",
             )
@@ -672,21 +682,3 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<
         }
     }
 }
-
-fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
-    if ccx.const_kind() != hir::ConstContext::ConstFn {
-        Status::Allowed
-    } else {
-        Status::Unstable(sym::const_fn)
-    }
-}
-
-fn mcf_build_error(ccx: &ConstCx<'_, 'tcx>, span: Span, msg: &str) -> DiagnosticBuilder<'tcx> {
-    let mut err = struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg);
-    err.note(
-        "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
-             for more information",
-    );
-    err.help("add `#![feature(const_fn)]` to the crate attributes to enable");
-    err
-}
index 36644ab3c5918e4dda413a17055ef9e884155711..ac8c748ea85710a4dc9d09d01ac75f75ae499472 100644 (file)
@@ -29,11 +29,11 @@ pub fn in_any_value_of_ty(
 /// Normally, we would determine what qualifications apply to each type and error when an illegal
 /// operation is performed on such a type. However, this was found to be too imprecise, especially
 /// in the presence of `enum`s. If only a single variant of an enum has a certain qualification, we
-/// needn't reject code unless it actually constructs and operates on the qualifed variant.
+/// needn't reject code unless it actually constructs and operates on the qualified variant.
 ///
 /// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a
 /// type-based one). Qualifications propagate structurally across variables: If a local (or a
-/// projection of a local) is assigned a qualifed value, that local itself becomes qualifed.
+/// projection of a local) is assigned a qualified value, that local itself becomes qualified.
 pub trait Qualif {
     /// The name of the file used to debug the dataflow analysis that computes this qualif.
     const ANALYSIS_NAME: &'static str;
index ce5e41d3e7c866289896035965f68414bea0f1f6..cf7d404a077896ea711fae6128ed91097c900269 100644 (file)
@@ -1,6 +1,6 @@
 //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
 
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
+use rustc_errors::{Applicability, Diagnostic, ErrorReported};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, HirId, LangItem};
 use rustc_index::bit_set::BitSet;
@@ -234,13 +234,11 @@ pub fn check_body(&mut self) {
             if self.is_const_stable_const_fn() {
                 let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
                 if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
-                    struct_span_err!(
-                        self.ccx.tcx.sess,
-                        self.span,
-                        E0723,
-                        "trait methods cannot be stable const fn"
-                    )
-                    .emit();
+                    self.ccx
+                        .tcx
+                        .sess
+                        .struct_span_err(self.span, "trait methods cannot be stable const fn")
+                        .emit();
                 }
             }
 
index 09da9b2e4d6ffd29a9cd470b06094f8069da3952..955be8cc81e18285502b650f541130e7db5375b6 100644 (file)
 
 use std::ops::Bound;
 
-use crate::const_eval::is_min_const_fn;
-
 pub struct UnsafetyChecker<'a, 'tcx> {
     body: &'a Body<'tcx>,
     body_did: LocalDefId,
     const_context: bool,
-    min_const_fn: bool,
     violations: Vec<UnsafetyViolation>,
     source_info: SourceInfo,
     tcx: TyCtxt<'tcx>,
@@ -34,21 +31,15 @@ pub struct UnsafetyChecker<'a, 'tcx> {
 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
     fn new(
         const_context: bool,
-        min_const_fn: bool,
         body: &'a Body<'tcx>,
         body_did: LocalDefId,
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Self {
-        // sanity check
-        if min_const_fn {
-            assert!(const_context);
-        }
         Self {
             body,
             body_did,
             const_context,
-            min_const_fn,
             violations: vec![],
             source_info: SourceInfo::outermost(body.span),
             tcx,
@@ -84,7 +75,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
                 let sig = func_ty.fn_sig(self.tcx);
                 if let hir::Unsafety::Unsafe = sig.unsafety() {
                     self.require_unsafe(
-                        UnsafetyViolationKind::GeneralAndConstFn,
+                        UnsafetyViolationKind::General,
                         UnsafetyViolationDetails::CallToUnsafeFunction,
                     )
                 }
@@ -134,7 +125,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
                     match self.tcx.layout_scalar_valid_range(def.did) {
                         (Bound::Unbounded, Bound::Unbounded) => {}
                         _ => self.require_unsafe(
-                            UnsafetyViolationKind::GeneralAndConstFn,
+                            UnsafetyViolationKind::General,
                             UnsafetyViolationDetails::InitializingTypeWith,
                         ),
                     }
@@ -213,7 +204,7 @@ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location:
                 let base_ty = base.ty(self.body, self.tcx).ty;
                 if base_ty.is_unsafe_ptr() {
                     self.require_unsafe(
-                        UnsafetyViolationKind::GeneralAndConstFn,
+                        UnsafetyViolationKind::General,
                         UnsafetyViolationDetails::DerefOfRawPointer,
                     )
                 }
@@ -258,7 +249,7 @@ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location:
                         );
                     if !nodrop {
                         self.require_unsafe(
-                            UnsafetyViolationKind::GeneralAndConstFn,
+                            UnsafetyViolationKind::General,
                             UnsafetyViolationDetails::AssignToDroppingUnionField,
                         );
                     } else {
@@ -266,7 +257,7 @@ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location:
                     }
                 } else {
                     self.require_unsafe(
-                        UnsafetyViolationKind::GeneralAndConstFn,
+                        UnsafetyViolationKind::General,
                         UnsafetyViolationDetails::AccessToUnionField,
                     )
                 }
@@ -277,6 +268,9 @@ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location:
 
 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
     fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
+        // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
+        assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
+
         let source_info = self.source_info;
         let lint_root = self.body.source_scopes[self.source_info.scope]
             .local_data
@@ -304,8 +298,7 @@ fn register_violations(
             Safety::Safe => {
                 for violation in violations {
                     match violation.kind {
-                        UnsafetyViolationKind::GeneralAndConstFn
-                        | UnsafetyViolationKind::General => {}
+                        UnsafetyViolationKind::General => {}
                         UnsafetyViolationKind::UnsafeFn => {
                             bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
                         }
@@ -334,29 +327,6 @@ fn register_violations(
                 if !violations.is_empty() {
                     self.used_unsafe.insert(hir_id);
                 }
-                // only some unsafety is allowed in const fn
-                if self.min_const_fn {
-                    for violation in violations {
-                        match violation.kind {
-                            // these unsafe things are stable in const fn
-                            UnsafetyViolationKind::GeneralAndConstFn => {}
-                            // these things are forbidden in const fns
-                            UnsafetyViolationKind::General => {
-                                let mut violation = *violation;
-                                // const fns don't need to be backwards compatible and can
-                                // emit these violations as a hard error instead of a backwards
-                                // compat lint
-                                violation.kind = UnsafetyViolationKind::General;
-                                if !self.violations.contains(&violation) {
-                                    self.violations.push(violation)
-                                }
-                            }
-                            UnsafetyViolationKind::UnsafeFn => bug!(
-                                "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
-                            ),
-                        }
-                    }
-                }
                 true
             }
         };
@@ -394,7 +364,7 @@ fn check_mut_borrowing_layout_constrained_field(
                             } else {
                                 continue;
                             };
-                            self.require_unsafe(UnsafetyViolationKind::GeneralAndConstFn, details);
+                            self.require_unsafe(UnsafetyViolationKind::General, details);
                         }
                     }
                 }
@@ -412,7 +382,7 @@ fn check_target_features(&mut self, func_did: DefId) {
         // Is `callee_features` a subset of `calling_features`?
         if !callee_features.iter().all(|feature| self_features.contains(feature)) {
             self.require_unsafe(
-                UnsafetyViolationKind::GeneralAndConstFn,
+                UnsafetyViolationKind::General,
                 UnsafetyViolationDetails::CallToFunctionWith,
             )
         }
@@ -494,15 +464,12 @@ fn unsafety_check_result<'tcx>(
     let param_env = tcx.param_env(def.did);
 
     let id = tcx.hir().local_def_id_to_hir_id(def.did);
-    let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
-        hir::BodyOwnerKind::Closure => (false, false),
-        hir::BodyOwnerKind::Fn => {
-            (tcx.is_const_fn_raw(def.did.to_def_id()), is_min_const_fn(tcx, def.did.to_def_id()))
-        }
-        hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
+    let const_context = match tcx.hir().body_owner_kind(id) {
+        hir::BodyOwnerKind::Closure => false,
+        hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
+        hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
     };
-    let mut checker =
-        UnsafetyChecker::new(const_context, min_const_fn, body, def.did, tcx, param_env);
+    let mut checker = UnsafetyChecker::new(const_context, body, def.did, tcx, param_env);
     checker.visit_body(&body);
 
     check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
@@ -577,7 +544,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
 
         match kind {
-            UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
+            UnsafetyViolationKind::General => {
                 // once
                 struct_span_err!(
                     tcx.sess,
index 7706316c96516a3e3057837b58a32f31eb6cec9c..5968bbbfca7f3d01046f3fff0edc514f226ec5e8 100644 (file)
@@ -13,9 +13,9 @@
     MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
 };
 use rustc_middle::mir::{
-    AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl,
-    LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData,
-    Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
+    AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
+    Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
+    StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
 };
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
 use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -440,18 +440,7 @@ fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, lo
     }
 
     fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
-        let mut data = &self.source_scopes[source_info.scope];
-        // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
-        // does not work as I thought it would. Needs more investigation and documentation.
-        while data.inlined.is_some() {
-            trace!(?data);
-            data = &self.source_scopes[data.parent_scope.unwrap()];
-        }
-        trace!(?data);
-        match &data.local_data {
-            ClearCrossCrate::Set(data) => Some(data.lint_root),
-            ClearCrossCrate::Clear => None,
-        }
+        source_info.scope.lint_root(&self.source_scopes)
     }
 
     fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
index 0e9728df73ca749520a62b7e873487f15d3fa3f3..48361483099f2f325f1b67e9443a74acddd1d32f 100644 (file)
@@ -816,7 +816,7 @@ fn bcb_to_string_sections(
     sections
 }
 
-/// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any
+/// Returns a simple string representation of a `TerminatorKind` variant, independent of any
 /// values it might hold.
 pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str {
     match kind {
index 93133e9b7f06364b52f3b549b7714fae78d858ea..eaeb44289cfb2097c2c5354cc73ed3eb42d13848 100644 (file)
@@ -23,6 +23,7 @@
 use rustc_middle::hir;
 use rustc_middle::hir::map::blocks::FnLikeNode;
 use rustc_middle::ich::StableHashingContext;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::{
     self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
@@ -87,6 +88,11 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) {
             _ => {}
         }
 
+        let codegen_fn_attrs = tcx.codegen_fn_attrs(mir_source.def_id());
+        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
+            return;
+        }
+
         trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
         Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
         trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
@@ -111,7 +117,8 @@ fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>)
         let body_span = hir_body.value.span;
         let source_file = source_map.lookup_source_file(body_span.lo());
         let fn_sig_span = match some_fn_sig.filter(|fn_sig| {
-            Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.hi()))
+            fn_sig.span.ctxt() == body_span.ctxt()
+                && Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.lo()))
         }) {
             Some(fn_sig) => fn_sig.span.with_hi(body_span.lo()),
             None => body_span.shrink_to_lo(),
index 324d826b375c1976d9a23b3a8e1e1a2e16a42272..2041109eb385fb586f593baa1070e10254b574d2 100644 (file)
@@ -11,7 +11,7 @@
 use rustc_middle::ty::TyCtxt;
 
 use rustc_span::source_map::original_sp;
-use rustc_span::{BytePos, Span, SyntaxContext};
+use rustc_span::{BytePos, Span};
 
 use std::cmp::Ordering;
 
@@ -240,7 +240,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
     /// to be).
     pub(super) fn generate_coverage_spans(
         mir_body: &'a mir::Body<'tcx>,
-        fn_sig_span: Span,
+        fn_sig_span: Span, // Ensured to be same SourceFile and SyntaxContext as `body_span`
         body_span: Span,
         basic_coverage_blocks: &'a CoverageGraph,
     ) -> Vec<CoverageSpan> {
@@ -717,11 +717,21 @@ pub(super) fn filtered_terminator_span(
         | TerminatorKind::FalseEdge { .. }
         | TerminatorKind::Goto { .. } => None,
 
+        // Call `func` operand can have a more specific span when part of a chain of calls
+        | TerminatorKind::Call { ref func, .. } => {
+            let mut span = terminator.source_info.span;
+            if let mir::Operand::Constant(box constant) = func {
+                if constant.span.lo() > span.lo() {
+                    span = span.with_lo(constant.span.lo());
+                }
+            }
+            Some(function_source_span(span, body_span))
+        }
+
         // Retain spans from all other terminators
         TerminatorKind::Resume
         | TerminatorKind::Abort
         | TerminatorKind::Return
-        | TerminatorKind::Call { .. }
         | TerminatorKind::Yield { .. }
         | TerminatorKind::GeneratorDrop
         | TerminatorKind::FalseUnwind { .. }
@@ -733,6 +743,6 @@ pub(super) fn filtered_terminator_span(
 
 #[inline]
 fn function_source_span(span: Span, body_span: Span) -> Span {
-    let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root());
+    let span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
     if body_span.contains(span) { span } else { body_span }
 }
index 7a9bfaad88367528bb9544684c2d51828f80259a..dee112443d337f9952bd538d793f315e9b789a8d 100644 (file)
@@ -17,7 +17,7 @@
 //! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which
 //! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some
 //! basic, coverage-specific features would be impossible to test, but thankfully initializing these
-//! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument
+//! globals is comparatively simpler. The easiest way is to wrap the test in a closure argument
 //! to: `rustc_span::with_default_session_globals(|| { test_here(); })`.
 
 use super::counters;
index ddda98d16162312d55c0f83f13bb65bc24bcf916..33ad128eeeb7503847c1c63974e5ab491de1830e 100644 (file)
@@ -3,7 +3,7 @@
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::def_id::DefId;
 
-/// Checks if the specified `local` is used as the `self` prameter of a method call
+/// Checks if the specified `local` is used as the `self` parameter of a method call
 /// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
 /// returned.
 pub fn find_self_call<'tcx>(
index 589a4467dcae1023d9170f09fe27020e62adf284..1053890e61837bd7efe0955942bc8ab79698531d 100644 (file)
@@ -80,7 +80,7 @@
 /// The projections are truncated to represent a path that might be captured by a
 /// closure/generator. This implies the vector returned from this function doesn't contain
 /// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be
-/// part of a path that is captued by a closure. We stop applying projections once we see the first
+/// part of a path that is captured by a closure. We stop applying projections once we see the first
 /// projection that isn't captured by a closure.
 fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
     mir_projections: &[PlaceElem<'tcx>],
@@ -578,7 +578,7 @@ fn expr_as_place(
 
     /// Lower a captured upvar. Note we might not know the actual capture index,
     /// so we create a place starting from `PlaceBase::Upvar`, which will be resolved
-    /// once all projections that allow us to indentify a capture have been applied.
+    /// once all projections that allow us to identify a capture have been applied.
     fn lower_captured_upvar(
         &mut self,
         block: BasicBlock,
index b637b9b70bdc6d8c9c3461946151392a203f429a..41fc925c0494ed850ee21c84354b1a0b038e8f1f 100644 (file)
@@ -618,6 +618,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
         } else {
             assert!(value.is_none(), "`return` and `break` should have a destination");
+            if self.tcx.sess.instrument_coverage() {
+                // Unlike `break` and `return`, which push an `Assign` statement to MIR, from which
+                // a Coverage code region can be generated, `continue` needs no `Assign`; but
+                // without one, the `InstrumentCoverage` MIR pass cannot generate a code region for
+                // `continue`. Coverage will be missing unless we add a dummy `Assign` to MIR.
+                self.add_dummy_assignment(&span, block, source_info);
+            }
         }
 
         let region_scope = self.scopes.breakable_scopes[break_index].region_scope;
@@ -643,6 +650,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         self.cfg.start_new_block().unit()
     }
 
+    // Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue`
+    // statement.
+    fn add_dummy_assignment(&mut self, span: &Span, block: BasicBlock, source_info: SourceInfo) {
+        let local_decl = LocalDecl::new(self.tcx.mk_unit(), *span).internal();
+        let temp_place = Place::from(self.local_decls.push(local_decl));
+        self.cfg.push_assign_unit(block, source_info, temp_place, self.tcx);
+    }
+
     crate fn exit_top_scope(
         &mut self,
         mut block: BasicBlock,
index 23bc1da09b5554975b1ac01a183f0a69ce621d83..da9a0b08e865b0d59520e3e435f8f2b336ed3169 100644 (file)
@@ -4,7 +4,6 @@
 #![feature(array_windows)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
-#![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(control_flow_enum)]
 #![feature(crate_visibility_modifier)]
index 2b7b58459c0a880cf783a79386622775b58ebbe4..acf3867cf8920608c2ad4b40858c0de037656210 100644 (file)
@@ -1448,7 +1448,7 @@ fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemInfo> {
         Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: false })))
     }
 
-    /// Is this unambiguously the start of a `macro_rules! foo` item defnition?
+    /// Is this unambiguously the start of a `macro_rules! foo` item definition?
     fn is_macro_rules_item(&mut self) -> bool {
         self.check_keyword(kw::MacroRules)
             && self.look_ahead(1, |t| *t == token::Not)
@@ -1478,7 +1478,15 @@ fn complain_if_pub_macro(&self, vis: &Visibility, macro_rules: bool) {
         let vstr = pprust::vis_to_string(vis);
         let vstr = vstr.trim_end();
         if macro_rules {
-            self.sess.gated_spans.gate(sym::pub_macro_rules, vis.span);
+            let msg = format!("can't qualify macro_rules invocation with `{}`", vstr);
+            self.struct_span_err(vis.span, &msg)
+                .span_suggestion(
+                    vis.span,
+                    "try exporting the macro",
+                    "#[macro_export]".to_owned(),
+                    Applicability::MaybeIncorrect, // speculative
+                )
+                .emit();
         } else {
             self.struct_span_err(vis.span, "can't qualify macro invocation with `pub`")
                 .span_suggestion(
index df292b141760dc3d76693e5f8deb7bfe956e5a15..d57cba6420f7b2781b47fb9f90e034f3aad55c2f 100644 (file)
@@ -69,55 +69,48 @@ fn check_attributes(
         let mut is_valid = true;
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
-            is_valid &= if self.tcx.sess.check_name(attr, sym::inline) {
-                self.check_inline(hir_id, attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) {
-                self.check_non_exhaustive(hir_id, attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::marker) {
-                self.check_marker(hir_id, attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::target_feature) {
-                self.check_target_feature(hir_id, attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::track_caller) {
-                self.check_track_caller(hir_id, &attr.span, attrs, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::doc) {
-                self.check_doc_attrs(attr, hir_id, target)
-            } else if self.tcx.sess.check_name(attr, sym::no_link) {
-                self.check_no_link(hir_id, &attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::export_name) {
-                self.check_export_name(hir_id, &attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) {
-                self.check_rustc_args_required_const(&attr, span, target, item)
-            } else if self.tcx.sess.check_name(attr, sym::rustc_layout_scalar_valid_range_start) {
-                self.check_rustc_layout_scalar_valid_range(&attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::rustc_layout_scalar_valid_range_end) {
-                self.check_rustc_layout_scalar_valid_range(&attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::allow_internal_unstable) {
-                self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
-            } else if self.tcx.sess.check_name(attr, sym::rustc_allow_const_fn_unstable) {
-                self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::naked) {
-                self.check_naked(hir_id, attr, span, target)
-            } else if self.tcx.sess.check_name(attr, sym::rustc_legacy_const_generics) {
-                self.check_rustc_legacy_const_generics(&attr, span, target, item)
-            } else if self.tcx.sess.check_name(attr, sym::rustc_clean)
-                || self.tcx.sess.check_name(attr, sym::rustc_dirty)
-                || self.tcx.sess.check_name(attr, sym::rustc_if_this_changed)
-                || self.tcx.sess.check_name(attr, sym::rustc_then_this_would_need)
-            {
-                self.check_rustc_dirty_clean(&attr)
-            } else {
-                // lint-only checks
-                if self.tcx.sess.check_name(attr, sym::cold) {
-                    self.check_cold(hir_id, attr, span, target);
-                } else if self.tcx.sess.check_name(attr, sym::link_name) {
-                    self.check_link_name(hir_id, attr, span, target);
-                } else if self.tcx.sess.check_name(attr, sym::link_section) {
-                    self.check_link_section(hir_id, attr, span, target);
-                } else if self.tcx.sess.check_name(attr, sym::no_mangle) {
-                    self.check_no_mangle(hir_id, attr, span, target);
+            is_valid &= match attr.name_or_empty() {
+                sym::inline => self.check_inline(hir_id, attr, span, target),
+                sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
+                sym::marker => self.check_marker(hir_id, attr, span, target),
+                sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
+                sym::track_caller => {
+                    self.check_track_caller(hir_id, &attr.span, attrs, span, target)
                 }
-                true
+                sym::doc => self.check_doc_attrs(attr, hir_id, target),
+                sym::no_link => self.check_no_link(hir_id, &attr, span, target),
+                sym::export_name => self.check_export_name(hir_id, &attr, span, target),
+                sym::rustc_args_required_const => {
+                    self.check_rustc_args_required_const(&attr, span, target, item)
+                }
+                sym::rustc_layout_scalar_valid_range_start
+                | sym::rustc_layout_scalar_valid_range_end => {
+                    self.check_rustc_layout_scalar_valid_range(&attr, span, target)
+                }
+                sym::allow_internal_unstable => {
+                    self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
+                }
+                sym::rustc_allow_const_fn_unstable => {
+                    self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
+                }
+                sym::naked => self.check_naked(hir_id, attr, span, target),
+                sym::rustc_legacy_const_generics => {
+                    self.check_rustc_legacy_const_generics(&attr, span, target, item)
+                }
+                sym::rustc_clean
+                | sym::rustc_dirty
+                | sym::rustc_if_this_changed
+                | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
+                _ => true,
             };
+            // lint-only checks
+            match attr.name_or_empty() {
+                sym::cold => self.check_cold(hir_id, attr, span, target),
+                sym::link_name => self.check_link_name(hir_id, attr, span, target),
+                sym::link_section => self.check_link_section(hir_id, attr, span, target),
+                sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
+                _ => {}
+            }
         }
 
         if !is_valid {
@@ -1116,7 +1109,7 @@ fn check_repr(
         // ```
         let hints: Vec<_> = attrs
             .iter()
-            .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
+            .filter(|attr| attr.has_name(sym::repr))
             .filter_map(|attr| attr.meta_item_list())
             .flatten()
             .collect();
@@ -1287,7 +1280,7 @@ fn check_repr(
 
     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
         for attr in attrs {
-            if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
+            if attr.has_name(sym::used) && target != Target::Static {
                 self.tcx
                     .sess
                     .span_err(attr.span, "attribute must be applied to a `static` variable");
@@ -1489,7 +1482,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
         sym::path,
         sym::automatically_derived,
         sym::start,
-        sym::main,
+        sym::rustc_main,
     ];
 
     for attr in attrs {
index e53f821e6daff53d683b12cc5832af23320c0cf5..e1b750df33c684961c20db683b958c654b99678e 100644 (file)
@@ -84,7 +84,7 @@ fn entry_point_type(ctxt: &EntryContext<'_, '_>, item: &Item<'_>, at_root: bool)
     let attrs = ctxt.map.attrs(item.hir_id());
     if ctxt.session.contains_name(attrs, sym::start) {
         EntryPointType::Start
-    } else if ctxt.session.contains_name(attrs, sym::main) {
+    } else if ctxt.session.contains_name(attrs, sym::rustc_main) {
         EntryPointType::MainAttr
     } else if item.ident.name == sym::main {
         if at_root {
@@ -111,8 +111,8 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) {
             if let Some(attr) = ctxt.session.find_by_name(attrs, sym::start) {
                 throw_attr_err(&ctxt.session, attr.span, "start");
             }
-            if let Some(attr) = ctxt.session.find_by_name(attrs, sym::main) {
-                throw_attr_err(&ctxt.session, attr.span, "main");
+            if let Some(attr) = ctxt.session.find_by_name(attrs, sym::rustc_main) {
+                throw_attr_err(&ctxt.session, attr.span, "rustc_main");
             }
         }
         EntryPointType::MainNamed => {
@@ -193,10 +193,7 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) {
             err.span_note(span, "here is a function named `main`");
         }
         err.note("you have one or more functions named `main` not defined at the crate level");
-        err.help(
-            "either move the `main` function definitions or attach the `#[main]` attribute \
-                  to one of them",
-        );
+        err.help("consider moving the `main` function definitions");
         // There were some functions named `main` though. Try to give the user a hint.
         format!(
             "the main function must be defined at the crate level{}",
index 933e8ad1d727c4719baf896de0cdd220785d0091..0be7ef7e12a3eec7c9e7f6d8f77ce775d23eb8ed 100644 (file)
@@ -5,7 +5,6 @@
 //! This API is completely unstable and subject to change.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
-#![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(crate_visibility_modifier)]
 #![feature(in_band_lifetimes)]
index e22a108aaf07e47ae191aa568982f90079b117e9..fa930471c21102df0049e850af8cbafcc9c8f253 100644 (file)
@@ -1476,7 +1476,7 @@ fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
         for p in body.params {
             self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
                 if !self.live_on_entry(ln, var) {
-                    self.report_unsed_assign(hir_id, spans, var, |name| {
+                    self.report_unused_assign(hir_id, spans, var, |name| {
                         format!("value passed to `{}` is never read", name)
                     });
                 }
@@ -1615,13 +1615,13 @@ fn report_unused(
 
     fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
         if !self.live_on_exit(ln, var) {
-            self.report_unsed_assign(hir_id, spans, var, |name| {
+            self.report_unused_assign(hir_id, spans, var, |name| {
                 format!("value assigned to `{}` is never read", name)
             });
         }
     }
 
-    fn report_unsed_assign(
+    fn report_unused_assign(
         &self,
         hir_id: HirId,
         spans: Vec<Span>,
index b532021bed2e9ad03b978c222355bbcab0b46319..14a373c59423fbbdeef6940bca513eb8d63fd551 100644 (file)
 
 #[derive(Debug, Copy, Clone)]
 pub struct Context {
-    /// The root of the current region tree. This is typically the id
-    /// of the innermost fn body. Each fn forms its own disjoint tree
-    /// in the region hierarchy. These fn bodies are themselves
-    /// arranged into a tree. See the "Modeling closures" section of
-    /// the README in `rustc_trait_selection::infer::region_constraints`
-    /// for more details.
-    root_id: Option<hir::ItemLocalId>,
-
     /// The scope that contains any new variables declared, plus its depth in
     /// the scope tree.
     var_parent: Option<(Scope, ScopeDepth)>,
@@ -743,11 +735,6 @@ fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
         let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
         self.terminating_scopes.insert(body.value.hir_id.local_id);
 
-        if let Some(root_id) = self.cx.root_id {
-            self.scope_tree.record_closure_parent(body.value.hir_id.local_id, root_id);
-        }
-        self.cx.root_id = Some(body.value.hir_id.local_id);
-
         self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::CallSite });
         self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::Arguments });
 
@@ -824,7 +811,7 @@ fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
             tcx,
             scope_tree: ScopeTree::default(),
             expr_and_pat_count: 0,
-            cx: Context { root_id: None, parent: None, var_parent: None },
+            cx: Context { parent: None, var_parent: None },
             terminating_scopes: Default::default(),
             pessimistic_yield: false,
             fixup_scopes: vec![],
index e092bb845ffacb874fbe6ecc54e7a71d1d278fde..d37a5be2fe515d058c3de9bf910b3b536b5d064c 100644 (file)
@@ -1114,7 +1114,7 @@ fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
 
 ////////////////////////////////////////////////////////////////////////////////////////////
 /// Type privacy visitor, checks types for privacy and reports violations.
-/// Both explicitly written types and inferred types of expressions and patters are checked.
+/// Both explicitly written types and inferred types of expressions and patterns are checked.
 /// Checks are performed on "semantic" types regardless of names and their hygiene.
 ////////////////////////////////////////////////////////////////////////////////////////////
 
index 4194b28dc7d681fc485c0e765a55430a7745f6dc..ee914fa1ba95cfb2cedf993b0a1da6168042dd79 100644 (file)
@@ -438,6 +438,11 @@ pub mod query_callbacks {
                 try_load_from_on_disk_cache: |_, _| {},
             };
 
+            pub const CompileMonoItem: QueryStruct = QueryStruct {
+                force_from_dep_node: |_, _| false,
+                try_load_from_on_disk_cache: |_, _| {},
+            };
+
             $(pub const $name: QueryStruct = {
                 const is_anon: bool = is_anon!([$($modifiers)*]);
 
index 071144f38e702f2459a45f58fd5f10e57b6dff85..be72baefb9edcc356395c428fa8d3b23912d5f27 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(bool_to_option)]
-#![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(core_intrinsics)]
 #![feature(drain_filter)]
index d77022a65e43979d32baa55fd0fa6b3d9b5de9f7..b5c95cfcb29cbe46e57f414ecef95e53bed03674 100644 (file)
@@ -1230,13 +1230,13 @@ fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'a> {
         };
 
         let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id());
-        let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export);
         self.r.macro_map.insert(def_id.to_def_id(), ext);
         self.r.local_macro_def_scopes.insert(def_id, parent_scope.module);
 
-        if macro_rules && matches!(item.vis.kind, ast::VisibilityKind::Inherited) {
+        if macro_rules {
             let ident = ident.normalize_to_macros_2_0();
             self.r.macro_names.insert(ident);
+            let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export);
             let vis = if is_macro_export {
                 ty::Visibility::Public
             } else {
@@ -1261,11 +1261,6 @@ fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'a> {
                 }),
             ))
         } else {
-            if is_macro_export {
-                let what = if macro_rules { "`macro_rules` with `pub`" } else { "`macro` items" };
-                let msg = format!("`#[macro_export]` cannot be used on {what}");
-                self.r.session.span_err(item.span, &msg);
-            }
             let module = parent_scope.module;
             let vis = match item.kind {
                 // Visibilities must not be resolved non-speculatively twice
index 327beca218e1d6a1f62a0f970acf4a08dd032d9a..c5f12c0c691b3c948b1cde39e2fdab2fc1d4907e 100644 (file)
@@ -472,17 +472,6 @@ impl<'a> Resolver<'a> {
                 );
                 err
             }
-            ResolutionError::ParamInAnonConstInTyDefault(name) => {
-                let mut err = self.session.struct_span_err(
-                    span,
-                    "constant values inside of type parameter defaults must not depend on generic parameters",
-                );
-                err.span_label(
-                    span,
-                    format!("the anonymous constant must not depend on the parameter `{}`", name),
-                );
-                err
-            }
             ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => {
                 let mut err = self.session.struct_span_err(
                     span,
@@ -758,17 +747,14 @@ fn lookup_import_candidates_from_module<FilterFn>(
     {
         let mut candidates = Vec::new();
         let mut seen_modules = FxHashSet::default();
-        let not_local_module = crate_name.name != kw::Crate;
-        let mut worklist =
-            vec![(start_module, Vec::<ast::PathSegment>::new(), true, not_local_module)];
+        let mut worklist = vec![(start_module, Vec::<ast::PathSegment>::new(), true)];
         let mut worklist_via_import = vec![];
 
-        while let Some((in_module, path_segments, accessible, in_module_is_extern)) =
-            match worklist.pop() {
-                None => worklist_via_import.pop(),
-                Some(x) => Some(x),
-            }
-        {
+        while let Some((in_module, path_segments, accessible)) = match worklist.pop() {
+            None => worklist_via_import.pop(),
+            Some(x) => Some(x),
+        } {
+            let in_module_is_extern = !in_module.def_id().unwrap().is_local();
             // We have to visit module children in deterministic order to avoid
             // instabilities in reported imports (#43552).
             in_module.for_each_child(self, |this, ident, ns, name_binding| {
@@ -850,11 +836,10 @@ fn lookup_import_candidates_from_module<FilterFn>(
                         name_binding.is_extern_crate() && lookup_ident.span.rust_2018();
 
                     if !is_extern_crate_that_also_appears_in_prelude {
-                        let is_extern = in_module_is_extern || name_binding.is_extern_crate();
                         // add the module to the lookup
                         if seen_modules.insert(module.def_id().unwrap()) {
                             if via_import { &mut worklist_via_import } else { &mut worklist }
-                                .push((module, path_segments, child_accessible, is_extern));
+                                .push((module, path_segments, child_accessible));
                         }
                     }
                 }
index 9321f11f6593380f749d79476205eca86929f173..92f21191de4308a8c12082a128382675f9b16570 100644 (file)
@@ -555,18 +555,23 @@ fn visit_generics(&mut self, generics: &'ast Generics) {
         // provide previous type parameters as they're built. We
         // put all the parameters on the ban list and then remove
         // them one by one as they are processed and become available.
-        let mut default_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
-        let mut found_default = false;
-        default_ban_rib.bindings.extend(generics.params.iter().filter_map(
-            |param| match param.kind {
-                GenericParamKind::Type { default: Some(_), .. }
-                | GenericParamKind::Const { default: Some(_), .. } => {
-                    found_default = true;
-                    Some((Ident::with_dummy_span(param.ident.name), Res::Err))
+        let mut forward_ty_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
+        let mut forward_const_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
+        for param in generics.params.iter() {
+            match param.kind {
+                GenericParamKind::Type { .. } => {
+                    forward_ty_ban_rib
+                        .bindings
+                        .insert(Ident::with_dummy_span(param.ident.name), Res::Err);
                 }
-                _ => None,
-            },
-        ));
+                GenericParamKind::Const { .. } => {
+                    forward_const_ban_rib
+                        .bindings
+                        .insert(Ident::with_dummy_span(param.ident.name), Res::Err);
+                }
+                GenericParamKind::Lifetime => {}
+            }
+        }
 
         // rust-lang/rust#61631: The type `Self` is essentially
         // another type parameter. For ADTs, we consider it
@@ -579,7 +584,7 @@ fn visit_generics(&mut self, generics: &'ast Generics) {
         // such as in the case of `trait Add<Rhs = Self>`.)
         if self.diagnostic_metadata.current_self_item.is_some() {
             // (`Some` if + only if we are in ADT's generics.)
-            default_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
+            forward_ty_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
         }
 
         for param in &generics.params {
@@ -591,32 +596,38 @@ fn visit_generics(&mut self, generics: &'ast Generics) {
                     }
 
                     if let Some(ref ty) = default {
-                        self.ribs[TypeNS].push(default_ban_rib);
-                        self.with_rib(ValueNS, ForwardGenericParamBanRibKind, |this| {
-                            // HACK: We use an empty `ForwardGenericParamBanRibKind` here which
-                            // is only used to forbid the use of const parameters inside of
-                            // type defaults.
-                            //
-                            // While the rib name doesn't really fit here, it does allow us to use the same
-                            // code for both const and type parameters.
-                            this.visit_ty(ty);
-                        });
-                        default_ban_rib = self.ribs[TypeNS].pop().unwrap();
+                        self.ribs[TypeNS].push(forward_ty_ban_rib);
+                        self.ribs[ValueNS].push(forward_const_ban_rib);
+                        self.visit_ty(ty);
+                        forward_const_ban_rib = self.ribs[ValueNS].pop().unwrap();
+                        forward_ty_ban_rib = self.ribs[TypeNS].pop().unwrap();
                     }
 
                     // Allow all following defaults to refer to this type parameter.
-                    default_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name));
+                    forward_ty_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name));
                 }
-                GenericParamKind::Const { ref ty, kw_span: _, default: _ } => {
-                    // FIXME(const_generics_defaults): handle `default` value here
-                    for bound in &param.bounds {
-                        self.visit_param_bound(bound);
-                    }
+                GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
+                    // Const parameters can't have param bounds.
+                    assert!(param.bounds.is_empty());
+
                     self.ribs[TypeNS].push(Rib::new(ConstParamTyRibKind));
                     self.ribs[ValueNS].push(Rib::new(ConstParamTyRibKind));
                     self.visit_ty(ty);
                     self.ribs[TypeNS].pop().unwrap();
                     self.ribs[ValueNS].pop().unwrap();
+
+                    if let Some(ref expr) = default {
+                        self.ribs[TypeNS].push(forward_ty_ban_rib);
+                        self.ribs[ValueNS].push(forward_const_ban_rib);
+                        self.visit_anon_const(expr);
+                        forward_const_ban_rib = self.ribs[ValueNS].pop().unwrap();
+                        forward_ty_ban_rib = self.ribs[TypeNS].pop().unwrap();
+                    }
+
+                    // Allow all following defaults to refer to this const parameter.
+                    forward_const_ban_rib
+                        .bindings
+                        .remove(&Ident::with_dummy_span(param.ident.name));
                 }
             }
         }
index 6fae6921fc9b2342a2e19ed3bbc38c539992b18f..e33c374f562e2452d6d68625d3b93810f1bfbd18 100644 (file)
@@ -930,7 +930,14 @@ fn smart_resolve_context_dependent_help(
                     let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \
                                `type` alias";
                     if let Some(span) = self.def_span(def_id) {
-                        err.span_help(span, msg);
+                        if let Ok(snip) = self.r.session.source_map().span_to_snippet(span) {
+                            // The span contains a type alias so we should be able to
+                            // replace `type` with `trait`.
+                            let snip = snip.replacen("type", "trait", 1);
+                            err.span_suggestion(span, msg, snip, Applicability::MaybeIncorrect);
+                        } else {
+                            err.span_help(span, msg);
+                        }
                     } else {
                         err.help(msg);
                     }
index 91bc8ab5ef41da194936a68aada9b56da6639cf8..174df09cbdbb2b5e4dcea4cb05da17b9f6974f5e 100644 (file)
@@ -165,29 +165,6 @@ struct NamedRegionMap {
     map: &'a mut NamedRegionMap,
     scope: ScopeRef<'a>,
 
-    /// This is slightly complicated. Our representation for poly-trait-refs contains a single
-    /// binder and thus we only allow a single level of quantification. However,
-    /// the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>`
-    /// and `for <'a, 'b> &'b T: Foo<'a>`. In order to get the De Bruijn indices
-    /// correct when representing these constraints, we should only introduce one
-    /// scope. However, we want to support both locations for the quantifier and
-    /// during lifetime resolution we want precise information (so we can't
-    /// desugar in an earlier phase). Moreso, an error here doesn't cause a bail
-    /// from type checking, so we need to be extra careful that we don't lose
-    /// any bound var information.
-    ///
-    /// So, if we encounter a quantifier at the outer scope, we set
-    /// `trait_ref_hack` to the hir id of the bounded type (and introduce a scope).
-    /// Then, if we encounter a quantifier at the inner scope, then we know to
-    /// emit an error. Importantly though, we do have to track the lifetimes
-    /// defined on the outer scope (i.e. the bounded ty), since we continue
-    /// to type check after emitting an error; we therefore assume that the bound
-    /// vars on the inner trait refs come from both quantifiers.
-    ///
-    /// If we encounter a quantifier in the inner scope `trait_ref_hack` being
-    /// `None`, then we just introduce the scope at the inner quantifier as normal.
-    trait_ref_hack: Option<hir::HirId>,
-
     /// Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax.
     is_in_fn_syntax: bool,
 
@@ -244,10 +221,7 @@ enum Scope<'a> {
         /// of the resulting opaque type.
         opaque_type_parent: bool,
 
-        /// True only if this `Binder` scope is from the quantifiers on a
-        /// `PolyTraitRef`. This is necessary for `assocated_type_bounds`, which
-        /// requires binders of nested trait refs to be merged.
-        from_poly_trait_ref: bool,
+        scope_type: BinderScopeType,
 
         /// The late bound vars for a given item are stored by `HirId` to be
         /// queried later. However, if we enter an elision scope, we have to
@@ -282,41 +256,6 @@ enum Scope<'a> {
         s: ScopeRef<'a>,
     },
 
-    /// This is a particularly interesting consequence of how we handle poly
-    /// trait refs. See `trait_ref_hack` for additional info. This bit is
-    /// important w.r.t. querying late-bound vars.
-    ///
-    /// To completely understand why this is necessary, first it's important to
-    /// realize that `T: for<'a> U + for<'a, 'b> V` is actually two separate
-    /// trait refs: `T: for<'a> U` and `T: for<'b> V` and as such, the late
-    /// bound vars on each needs to be tracked separately. Also, in this case,
-    /// are *three* relevant `HirId`s: one for the entire bound and one
-    /// for each separate one.
-    ///
-    /// Next, imagine three different poly trait refs:
-    ///   1) `for<'a, 'b> T: U<'a, 'b>`
-    ///   2) `T: for<'a, 'b> U<'a, 'b>`
-    ///   3) `for<'a> T: for<'b> U<'a, 'b>`
-    ///
-    /// First, note that the third example is semantically invalid and an error,
-    /// but we *must* handle it as valid, since type checking isn't bailed out
-    /// of. Other than that, if ask for bound vars for each, we expect
-    /// `['a, 'b]`. If we *didn't* allow binders before `T`, then we would
-    /// always introduce a binder scope at the inner trait ref. This is great,
-    /// becauase later on during type-checking, we will ask "what are the late
-    /// bound vars on this trait ref". However, because we allow bound vars on
-    /// the bound itself, we have to have some way of keeping track of the fact
-    /// that we actually want to store the late bound vars as being associated
-    /// with the trait ref; this is that.
-    ///
-    /// One alternative way to handle this would be to just introduce a new
-    /// `Binder` scope, but that's semantically a bit different, since bound
-    /// vars from both `for<...>`s *do* share the same binder level.
-    TraitRefHackInner {
-        hir_id: hir::HirId,
-        s: ScopeRef<'a>,
-    },
-
     /// When we have nested trait refs, we concanetate late bound vars for inner
     /// trait refs from outer ones. But we also need to include any HRTB
     /// lifetimes encountered when identifying the trait that an associated type
@@ -333,6 +272,22 @@ enum Scope<'a> {
     Root,
 }
 
+#[derive(Copy, Clone, Debug)]
+enum BinderScopeType {
+    /// Any non-concatenating binder scopes.
+    Normal,
+    /// Within a syntactic trait ref, there may be multiple poly trait refs that
+    /// are nested (under the `associcated_type_bounds` feature). The binders of
+    /// the innner poly trait refs are extended from the outer poly trait refs
+    /// and don't increase the late bound depth. If you had
+    /// `T: for<'a>  Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'b>` scope
+    /// would be `Concatenating`. This also used in trait refs in where clauses
+    /// where we have two binders `for<> T: for<> Foo` (I've intentionally left
+    /// out any lifetimes because they aren't needed to show the two scopes).
+    /// The inner `for<>` has a scope of `Concatenating`.
+    Concatenating,
+}
+
 // A helper struct for debugging scopes without printing parent scopes
 struct TruncatedScopeDebug<'a>(&'a Scope<'a>);
 
@@ -344,7 +299,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 next_early_index,
                 track_lifetime_uses,
                 opaque_type_parent,
-                from_poly_trait_ref,
+                scope_type,
                 hir_id,
                 s: _,
             } => f
@@ -353,7 +308,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 .field("next_early_index", next_early_index)
                 .field("track_lifetime_uses", track_lifetime_uses)
                 .field("opaque_type_parent", opaque_type_parent)
-                .field("from_poly_trait_ref", from_poly_trait_ref)
+                .field("scope_type", scope_type)
                 .field("hir_id", hir_id)
                 .field("s", &"..")
                 .finish(),
@@ -368,11 +323,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 .field("lifetime", lifetime)
                 .field("s", &"..")
                 .finish(),
-            Scope::TraitRefHackInner { hir_id, s: _ } => f
-                .debug_struct("TraitRefHackInner")
-                .field("hir_id", hir_id)
-                .field("s", &"..")
-                .finish(),
             Scope::Supertrait { lifetimes, s: _ } => f
                 .debug_struct("Supertrait")
                 .field("lifetimes", lifetimes)
@@ -495,7 +445,6 @@ fn do_resolve(
         tcx,
         map: &mut named_region_map,
         scope: ROOT_SCOPE,
-        trait_ref_hack: None,
         is_in_fn_syntax: false,
         is_in_const_generic: false,
         trait_definition_only,
@@ -618,6 +567,43 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::
     }
 }
 
+impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
+    /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref.
+    fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) {
+        let mut scope = self.scope;
+        let mut supertrait_lifetimes = vec![];
+        loop {
+            match scope {
+                Scope::Body { .. } | Scope::Root => {
+                    break (vec![], BinderScopeType::Normal);
+                }
+
+                Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
+                    scope = s;
+                }
+
+                Scope::Supertrait { s, lifetimes } => {
+                    supertrait_lifetimes = lifetimes.clone();
+                    scope = s;
+                }
+
+                Scope::TraitRefBoundary { .. } => {
+                    // We should only see super trait lifetimes if there is a `Binder` above
+                    assert!(supertrait_lifetimes.is_empty());
+                    break (vec![], BinderScopeType::Normal);
+                }
+
+                Scope::Binder { hir_id, .. } => {
+                    // Nested poly trait refs have the binders concatenated
+                    let mut full_binders =
+                        self.map.late_bound_vars.entry(*hir_id).or_default().clone();
+                    full_binders.extend(supertrait_lifetimes.into_iter());
+                    break (full_binders, BinderScopeType::Concatenating);
+                }
+            }
+        }
+    }
+}
 impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     type Map = Map<'tcx>;
 
@@ -675,7 +661,7 @@ fn visit_fn(
                     s: self.scope,
                     track_lifetime_uses: true,
                     opaque_type_parent: false,
-                    from_poly_trait_ref: false,
+                    scope_type: BinderScopeType::Normal,
                 };
                 self.with(scope, move |_old_scope, this| {
                     intravisit::walk_fn(this, fk, fd, b, s, hir_id)
@@ -800,12 +786,15 @@ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
                     next_early_index: index + non_lifetime_count,
                     opaque_type_parent: true,
                     track_lifetime_uses,
-                    from_poly_trait_ref: false,
+                    scope_type: BinderScopeType::Normal,
                     s: ROOT_SCOPE,
                 };
                 self.with(scope, |old_scope, this| {
                     this.check_lifetime_params(old_scope, &generics.params);
-                    intravisit::walk_item(this, item);
+                    let scope = Scope::TraitRefBoundary { s: this.scope };
+                    this.with(scope, |_, this| {
+                        intravisit::walk_item(this, item);
+                    });
                 });
                 self.missing_named_lifetime_spots.pop();
             }
@@ -869,7 +858,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
                     next_early_index,
                     track_lifetime_uses: true,
                     opaque_type_parent: false,
-                    from_poly_trait_ref: false,
+                    scope_type: BinderScopeType::Normal,
                 };
                 self.with(scope, |old_scope, this| {
                     // a bare fn has no bounds, so everything
@@ -939,9 +928,12 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
 
                         // Elided lifetimes are not allowed in non-return
                         // position impl Trait
-                        let scope = Scope::Elision { elide: Elide::Forbid, s: self.scope };
+                        let scope = Scope::TraitRefBoundary { s: self.scope };
                         self.with(scope, |_, this| {
-                            intravisit::walk_item(this, opaque_ty);
+                            let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope };
+                            this.with(scope, |_, this| {
+                                intravisit::walk_item(this, opaque_ty);
+                            })
                         });
 
                         return;
@@ -1062,7 +1054,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
                             s: this.scope,
                             track_lifetime_uses: true,
                             opaque_type_parent: false,
-                            from_poly_trait_ref: false,
+                            scope_type: BinderScopeType::Normal,
                         };
                         this.with(scope, |_old_scope, this| {
                             this.visit_generics(generics);
@@ -1082,7 +1074,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
                         s: self.scope,
                         track_lifetime_uses: true,
                         opaque_type_parent: false,
-                        from_poly_trait_ref: false,
+                        scope_type: BinderScopeType::Normal,
                     };
                     self.with(scope, |_old_scope, this| {
                         let scope = Scope::TraitRefBoundary { s: this.scope };
@@ -1141,7 +1133,7 @@ fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
                     s: self.scope,
                     track_lifetime_uses: true,
                     opaque_type_parent: true,
-                    from_poly_trait_ref: false,
+                    scope_type: BinderScopeType::Normal,
                 };
                 self.with(scope, |old_scope, this| {
                     this.check_lifetime_params(old_scope, &generics.params);
@@ -1210,7 +1202,7 @@ fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
                     s: self.scope,
                     track_lifetime_uses: true,
                     opaque_type_parent: true,
-                    from_poly_trait_ref: false,
+                    scope_type: BinderScopeType::Normal,
                 };
                 self.with(scope, |old_scope, this| {
                     this.check_lifetime_params(old_scope, &generics.params);
@@ -1270,98 +1262,102 @@ fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
         if !self.trait_definition_only {
             check_mixed_explicit_and_in_band_defs(self.tcx, &generics.params);
         }
-        for param in generics.params {
-            match param.kind {
-                GenericParamKind::Lifetime { .. } => {}
-                GenericParamKind::Type { ref default, .. } => {
-                    walk_list!(self, visit_param_bound, param.bounds);
-                    if let Some(ref ty) = default {
-                        self.visit_ty(&ty);
+        let scope = Scope::TraitRefBoundary { s: self.scope };
+        self.with(scope, |_, this| {
+            for param in generics.params {
+                match param.kind {
+                    GenericParamKind::Lifetime { .. } => {}
+                    GenericParamKind::Type { ref default, .. } => {
+                        walk_list!(this, visit_param_bound, param.bounds);
+                        if let Some(ref ty) = default {
+                            this.visit_ty(&ty);
+                        }
+                    }
+                    GenericParamKind::Const { ref ty, .. } => {
+                        let was_in_const_generic = this.is_in_const_generic;
+                        this.is_in_const_generic = true;
+                        walk_list!(this, visit_param_bound, param.bounds);
+                        this.visit_ty(&ty);
+                        this.is_in_const_generic = was_in_const_generic;
                     }
                 }
-                GenericParamKind::Const { ref ty, .. } => {
-                    let was_in_const_generic = self.is_in_const_generic;
-                    self.is_in_const_generic = true;
-                    walk_list!(self, visit_param_bound, param.bounds);
-                    self.visit_ty(&ty);
-                    self.is_in_const_generic = was_in_const_generic;
-                }
-            }
-        }
-        for predicate in generics.where_clause.predicates {
-            match predicate {
-                &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
-                    ref bounded_ty,
-                    bounds,
-                    ref bound_generic_params,
-                    ..
-                }) => {
-                    let (lifetimes, binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) =
-                        bound_generic_params
-                            .iter()
-                            .filter_map(|param| match param.kind {
-                                GenericParamKind::Lifetime { .. } => Some(param),
-                                _ => None,
-                            })
-                            .enumerate()
-                            .map(|(late_bound_idx, param)| {
-                                let pair =
-                                    Region::late(late_bound_idx as u32, &self.tcx.hir(), param);
-                                let r = late_region_as_bound_region(self.tcx, &pair.1);
-                                (pair, r)
-                            })
-                            .unzip();
-                    self.map.late_bound_vars.insert(bounded_ty.hir_id, binders.clone());
-                    let scope = Scope::TraitRefBoundary { s: self.scope };
-                    self.with(scope, |_, this| {
-                        if !lifetimes.is_empty() {
-                            let next_early_index = this.next_early_index();
-                            let scope = Scope::Binder {
-                                hir_id: bounded_ty.hir_id,
-                                lifetimes,
-                                s: this.scope,
-                                next_early_index,
-                                track_lifetime_uses: true,
-                                opaque_type_parent: false,
-                                from_poly_trait_ref: true,
-                            };
-                            this.with(scope, |old_scope, this| {
-                                this.check_lifetime_params(old_scope, &bound_generic_params);
-                                this.visit_ty(&bounded_ty);
-                                this.trait_ref_hack = Some(bounded_ty.hir_id);
-                                walk_list!(this, visit_param_bound, bounds);
-                                this.trait_ref_hack = None;
-                            })
-                        } else {
+            }
+            for predicate in generics.where_clause.predicates {
+                match predicate {
+                    &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+                        ref bounded_ty,
+                        bounds,
+                        ref bound_generic_params,
+                        ..
+                    }) => {
+                        let (lifetimes, binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) =
+                            bound_generic_params
+                                .iter()
+                                .filter_map(|param| match param.kind {
+                                    GenericParamKind::Lifetime { .. } => Some(param),
+                                    _ => None,
+                                })
+                                .enumerate()
+                                .map(|(late_bound_idx, param)| {
+                                    let pair =
+                                        Region::late(late_bound_idx as u32, &this.tcx.hir(), param);
+                                    let r = late_region_as_bound_region(this.tcx, &pair.1);
+                                    (pair, r)
+                                })
+                                .unzip();
+                        this.map.late_bound_vars.insert(bounded_ty.hir_id, binders.clone());
+                        let next_early_index = this.next_early_index();
+                        // Even if there are no lifetimes defined here, we still wrap it in a binder
+                        // scope. If there happens to be a nested poly trait ref (an error), that
+                        // will be `Concatenating` anyways, so we don't have to worry about the depth
+                        // being wrong.
+                        let scope = Scope::Binder {
+                            hir_id: bounded_ty.hir_id,
+                            lifetimes,
+                            s: this.scope,
+                            next_early_index,
+                            track_lifetime_uses: true,
+                            opaque_type_parent: false,
+                            scope_type: BinderScopeType::Normal,
+                        };
+                        this.with(scope, |old_scope, this| {
+                            this.check_lifetime_params(old_scope, &bound_generic_params);
                             this.visit_ty(&bounded_ty);
                             walk_list!(this, visit_param_bound, bounds);
-                        }
-                    })
-                }
-                &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
-                    ref lifetime,
-                    bounds,
-                    ..
-                }) => {
-                    self.visit_lifetime(lifetime);
-                    walk_list!(self, visit_param_bound, bounds);
-                }
-                &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
-                    ref lhs_ty,
-                    ref rhs_ty,
-                    ..
-                }) => {
-                    self.visit_ty(lhs_ty);
-                    self.visit_ty(rhs_ty);
+                        })
+                    }
+                    &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+                        ref lifetime,
+                        bounds,
+                        ..
+                    }) => {
+                        this.visit_lifetime(lifetime);
+                        walk_list!(this, visit_param_bound, bounds);
+                    }
+                    &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
+                        ref lhs_ty,
+                        ref rhs_ty,
+                        ..
+                    }) => {
+                        this.visit_ty(lhs_ty);
+                        this.visit_ty(rhs_ty);
+                    }
                 }
             }
-        }
+        })
     }
 
     fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) {
         match bound {
-            hir::GenericBound::LangItemTrait(_, _, hir_id, _) if self.trait_ref_hack.is_none() => {
-                self.map.late_bound_vars.insert(*hir_id, vec![]);
+            hir::GenericBound::LangItemTrait(_, _, hir_id, _) => {
+                // FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go
+                // through the regular poly trait ref code, so we don't get another
+                // chance to introduce a binder. For now, I'm keeping the existing logic
+                // of "if there isn't a Binder scope above us, add one", but I
+                // imagine there's a better way to go about this.
+                let (binders, scope_type) = self.poly_trait_ref_binder_info();
+
+                self.map.late_bound_vars.insert(*hir_id, binders);
                 let scope = Scope::Binder {
                     hir_id: *hir_id,
                     lifetimes: FxHashMap::default(),
@@ -1369,7 +1365,7 @@ fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) {
                     next_early_index: self.next_early_index(),
                     track_lifetime_uses: true,
                     opaque_type_parent: false,
-                    from_poly_trait_ref: false,
+                    scope_type,
                 };
                 self.with(scope, |_, this| {
                     intravisit::walk_param_bound(this, bound);
@@ -1388,148 +1384,53 @@ fn visit_poly_trait_ref(
 
         let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref);
 
-        let trait_ref_hack = self.trait_ref_hack.take();
         let next_early_index = self.next_early_index();
-        // See note on `trait_ref_hack`. If `for<..>` has been defined in both
-        // the outer and inner part of the trait ref, emit an error.
-        let has_lifetimes = trait_ref.bound_generic_params.iter().any(|param| match param.kind {
-            GenericParamKind::Lifetime { .. } => true,
-            _ => false,
-        });
-        if trait_ref_hack.is_some() && has_lifetimes {
-            struct_span_err!(
-                self.tcx.sess,
-                trait_ref.span,
-                E0316,
-                "nested quantification of lifetimes"
-            )
-            .emit();
-        }
-
-        let (binders, lifetimes) = if let Some(hir_id) = trait_ref_hack {
-            let mut binders = self.map.late_bound_vars.entry(hir_id).or_default().clone();
-            let initial_bound_vars = binders.len() as u32;
-            let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default();
-            let binders_iter = trait_ref
-                .bound_generic_params
-                .iter()
-                .filter_map(|param| match param.kind {
-                    GenericParamKind::Lifetime { .. } => Some(param),
-                    _ => None,
-                })
-                .enumerate()
-                .map(|(late_bound_idx, param)| {
-                    let pair = Region::late(
-                        initial_bound_vars + late_bound_idx as u32,
-                        &self.tcx.hir(),
-                        param,
-                    );
-                    let r = late_region_as_bound_region(self.tcx, &pair.1);
-                    lifetimes.insert(pair.0, pair.1);
-                    r
-                });
-            binders.extend(binders_iter);
-
-            (binders, lifetimes)
-        } else {
-            let mut supertrait_lifetimes = vec![];
-            let mut scope = self.scope;
-            let mut outer_binders = loop {
-                match scope {
-                    Scope::Body { .. } | Scope::Root => {
-                        break vec![];
-                    }
+        let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
 
-                    Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
-                        scope = s;
-                    }
-
-                    Scope::TraitRefHackInner { hir_id, .. } => {
-                        // Nested poly trait refs have the binders concatenated
-                        // If we reach `TraitRefHackInner`, then there is only one more `Binder` above us,
-                        // over all the bounds. We don't want this, since all the lifetimes we care about
-                        // are here anyways.
-                        let mut full_binders =
-                            self.map.late_bound_vars.entry(*hir_id).or_default().clone();
-                        full_binders.extend(supertrait_lifetimes.into_iter());
-                        break full_binders;
-                    }
-
-                    Scope::Supertrait { s, lifetimes } => {
-                        supertrait_lifetimes = lifetimes.clone();
-                        scope = s;
-                    }
-
-                    Scope::TraitRefBoundary { .. } => {
-                        // We should only see super trait lifetimes if there is a `Binder` above
-                        assert!(supertrait_lifetimes.is_empty());
-                        break vec![];
-                    }
-
-                    Scope::Binder { hir_id, from_poly_trait_ref, .. } => {
-                        if !from_poly_trait_ref {
-                            // We should only see super trait lifetimes if there is a `Binder` above
-                            assert!(supertrait_lifetimes.is_empty());
-                            break vec![];
-                        }
-                        // Nested poly trait refs have the binders concatenated
-                        let mut full_binders =
-                            self.map.late_bound_vars.entry(*hir_id).or_default().clone();
-                        full_binders.extend(supertrait_lifetimes.into_iter());
-                        break full_binders;
-                    }
-                }
-            };
-            let (lifetimes, local_binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) = trait_ref
-                .bound_generic_params
-                .iter()
-                .filter_map(|param| match param.kind {
-                    GenericParamKind::Lifetime { .. } => Some(param),
-                    _ => None,
-                })
-                .enumerate()
-                .map(|(late_bound_idx, param)| {
-                    let pair = Region::late(
-                        outer_binders.len() as u32 + late_bound_idx as u32,
-                        &self.tcx.hir(),
-                        param,
-                    );
-                    let r = late_region_as_bound_region(self.tcx, &pair.1);
-                    (pair, r)
-                })
-                .unzip();
-
-            outer_binders.extend(local_binders.into_iter());
-
-            (outer_binders, lifetimes)
-        };
+        let initial_bound_vars = binders.len() as u32;
+        let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default();
+        let binders_iter = trait_ref
+            .bound_generic_params
+            .iter()
+            .filter_map(|param| match param.kind {
+                GenericParamKind::Lifetime { .. } => Some(param),
+                _ => None,
+            })
+            .enumerate()
+            .map(|(late_bound_idx, param)| {
+                let pair = Region::late(
+                    initial_bound_vars + late_bound_idx as u32,
+                    &self.tcx.hir(),
+                    param,
+                );
+                let r = late_region_as_bound_region(self.tcx, &pair.1);
+                lifetimes.insert(pair.0, pair.1);
+                r
+            });
+        binders.extend(binders_iter);
 
         debug!(?binders);
         self.map.late_bound_vars.insert(trait_ref.trait_ref.hir_ref_id, binders);
 
-        if trait_ref_hack.is_none() || has_lifetimes {
-            let scope = Scope::Binder {
-                hir_id: trait_ref.trait_ref.hir_ref_id,
-                lifetimes,
-                s: self.scope,
-                next_early_index,
-                track_lifetime_uses: true,
-                opaque_type_parent: false,
-                from_poly_trait_ref: true,
-            };
-            self.with(scope, |old_scope, this| {
-                this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
-                walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
-                this.visit_trait_ref(&trait_ref.trait_ref);
-            });
-        } else {
-            let scope =
-                Scope::TraitRefHackInner { hir_id: trait_ref.trait_ref.hir_ref_id, s: self.scope };
-            self.with(scope, |_old_scope, this| {
-                this.visit_trait_ref(&trait_ref.trait_ref);
-            });
-        }
-        self.trait_ref_hack = trait_ref_hack;
+        // Always introduce a scope here, even if this is in a where clause and
+        // we introduced the binders around the bounded Ty. In that case, we
+        // just reuse the concatenation functionality also present in nested trait
+        // refs.
+        let scope = Scope::Binder {
+            hir_id: trait_ref.trait_ref.hir_ref_id,
+            lifetimes,
+            s: self.scope,
+            next_early_index,
+            track_lifetime_uses: true,
+            opaque_type_parent: false,
+            scope_type,
+        };
+        self.with(scope, |old_scope, this| {
+            this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
+            walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
+            this.visit_trait_ref(&trait_ref.trait_ref);
+        });
+
         if should_pop_missing_lt {
             self.missing_named_lifetime_spots.pop();
         }
@@ -1680,7 +1581,6 @@ fn check_if_label_shadows_lifetime(tcx: TyCtxt<'_>, mut scope: ScopeRef<'_>, lab
                 Scope::Body { s, .. }
                 | Scope::Elision { s, .. }
                 | Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::TraitRefHackInner { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. } => {
                     scope = s;
@@ -1871,12 +1771,10 @@ fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F)
         let labels_in_fn = take(&mut self.labels_in_fn);
         let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
         let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots);
-        let trait_ref_hack = take(&mut self.trait_ref_hack);
         let mut this = LifetimeContext {
             tcx: *tcx,
             map,
             scope: &wrap_scope,
-            trait_ref_hack,
             is_in_fn_syntax: self.is_in_fn_syntax,
             is_in_const_generic: self.is_in_const_generic,
             trait_definition_only: self.trait_definition_only,
@@ -1896,7 +1794,6 @@ fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F)
         self.labels_in_fn = this.labels_in_fn;
         self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
         self.missing_named_lifetime_spots = this.missing_named_lifetime_spots;
-        self.trait_ref_hack = this.trait_ref_hack;
     }
 
     /// helper method to determine the span to remove when suggesting the
@@ -2265,7 +2162,7 @@ fn visit_early_late<F>(
             s: self.scope,
             opaque_type_parent: true,
             track_lifetime_uses: false,
-            from_poly_trait_ref: false,
+            scope_type: BinderScopeType::Normal,
         };
         self.with(scope, move |old_scope, this| {
             this.check_lifetime_params(old_scope, &generics.params);
@@ -2289,7 +2186,6 @@ fn next_early_index_helper(&self, only_opaque_type_parent: bool) -> u32 {
                 | Scope::Body { s, .. }
                 | Scope::Elision { s, .. }
                 | Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::TraitRefHackInner { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. } => scope = s,
             }
@@ -2323,7 +2219,6 @@ fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
         // given name or we run out of scopes.
         // search.
         let mut late_depth = 0;
-        let mut in_poly_trait_ref = false;
         let mut scope = self.scope;
         let mut outermost_body = None;
         let result = loop {
@@ -2341,25 +2236,7 @@ fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
                     break None;
                 }
 
-                Scope::TraitRefBoundary { s, .. } => {
-                    // We've exited nested poly trait refs; mark that we are no longer in nested trait refs.
-                    // We don't increase the late depth because this isn't a `Binder` scope.
-                    //
-                    // This came up in #83737, which boiled down to a case like this:
-                    //
-                    // ```
-                    // F: for<> Fn(&()) -> Box<dyn for<> Future<Output = ()> + Unpin>,
-                    //                         //  ^^^^^
-
-                    // ```
-                    //
-                    // Here, as we traverse upwards from the `dyn for<>` binder, we want to reset `in_poly_trait_ref`
-                    // to false, so that we avoid excess contaenation when we encounter the outer `for<>`  binder.
-                    in_poly_trait_ref = false;
-                    scope = s;
-                }
-
-                Scope::Binder { ref lifetimes, from_poly_trait_ref, s, .. } => {
+                Scope::Binder { ref lifetimes, scope_type, s, .. } => {
                     match lifetime_ref.name {
                         LifetimeName::Param(param_name) => {
                             if let Some(&def) = lifetimes.get(&param_name.normalize_to_macros_2_0())
@@ -2369,47 +2246,17 @@ fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
                         }
                         _ => bug!("expected LifetimeName::Param"),
                     }
-
-                    match (from_poly_trait_ref, in_poly_trait_ref) {
-                        // This is the first binder we see that is a poly trait ref; add one to the
-                        // late depth and mark that we're potentially in nested trait refs.
-                        (true, false) => {
-                            in_poly_trait_ref = true;
-                            late_depth += 1;
-                        }
-                        // We've already seen a binder that is a poly trait ref and this one is too,
-                        // that means that they are nested and we are concatenating the bound vars;
-                        // don't increase the late depth.
-                        //
-                        // This happens specifically with associated trait bounds like the following:
-                        //
-                        // ```
-                        // for<'a> T: Iterator<Item: for<'b> Foo<'a, 'b>>
-                        // ```
-                        //
-                        // In this case, as we traverse `for<'b>`, we would increment `late_depth` but
-                        // set `in_poly_trait_ref` to true. Then when we traverse `for<'a>`, we would
-                        // not increment `late_depth` again. (NB: Niko thinks this logic is actually
-                        // wrong.)
-                        (true, true) => {}
-                        // We've exited nested poly trait refs; add one to the late depth and mark
-                        // that we are no longer in nested trait refs
-                        (false, true) => {
-                            in_poly_trait_ref = false;
-                            late_depth += 1;
-                        }
-                        // Any other kind of nested binders: just increase late depth.
-                        (false, false) => {
-                            late_depth += 1;
-                        }
+                    match scope_type {
+                        BinderScopeType::Normal => late_depth += 1,
+                        BinderScopeType::Concatenating => {}
                     }
                     scope = s;
                 }
 
                 Scope::Elision { s, .. }
                 | Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::TraitRefHackInner { s, .. }
-                | Scope::Supertrait { s, .. } => {
+                | Scope::Supertrait { s, .. }
+                | Scope::TraitRefBoundary { s, .. } => {
                     scope = s;
                 }
             }
@@ -2562,7 +2409,6 @@ fn visit_segment_args(
                         Scope::Binder { s, .. }
                         | Scope::Elision { s, .. }
                         | Scope::ObjectLifetimeDefault { s, .. }
-                        | Scope::TraitRefHackInner { s, .. }
                         | Scope::Supertrait { s, .. }
                         | Scope::TraitRefBoundary { s, .. } => {
                             scope = s;
@@ -2719,6 +2565,13 @@ fn supertrait_hrtb_lifetimes(
                 Some(next) => next,
                 None => break None,
             };
+            // See issue #83753. If someone writes an associated type on a non-trait, just treat it as
+            // there being no supertrait HRTBs.
+            match tcx.def_kind(def_id) {
+                DefKind::Trait | DefKind::TraitAlias | DefKind::Impl => {}
+                _ => break None,
+            }
+
             if trait_defines_associated_type_named(def_id) {
                 break Some(bound_vars.into_iter().collect());
             }
@@ -2754,7 +2607,7 @@ fn visit_fn_like_elision(
         let mut scope = &*self.scope;
         let hir_id = loop {
             match scope {
-                Scope::Binder { hir_id, .. } | Scope::TraitRefHackInner { hir_id, .. } => {
+                Scope::Binder { hir_id, .. } => {
                     break *hir_id;
                 }
                 Scope::Body { id, .. } => break id.hir_id,
@@ -2764,7 +2617,14 @@ fn visit_fn_like_elision(
                 | Scope::TraitRefBoundary { ref s, .. } => {
                     scope = *s;
                 }
-                Scope::Root => bug!("In fn_like_elision without appropriate scope above"),
+                Scope::Root => {
+                    // See issue #83907. Just bail out from looking inside.
+                    self.tcx.sess.delay_span_bug(
+                        rustc_span::DUMMY_SP,
+                        "In fn_like_elision without appropriate scope above",
+                    );
+                    return;
+                }
             }
         };
         // While not strictly necessary, we gather anon lifetimes *before* actually
@@ -3098,7 +2958,6 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
 
         let span = lifetime_refs[0].span;
         let mut late_depth = 0;
-        let mut in_poly_trait_ref = false;
         let mut scope = self.scope;
         let mut lifetime_names = FxHashSet::default();
         let mut lifetime_spans = vec![];
@@ -3109,14 +2968,7 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
 
                 Scope::Root => break None,
 
-                Scope::TraitRefBoundary { s, .. } => {
-                    // We've exited nested poly trait refs; mark that we are no longer in nested trait refs.
-                    // We don't increase the late depth because this isn't a `Binder` scope
-                    in_poly_trait_ref = false;
-                    scope = s;
-                }
-
-                Scope::Binder { s, ref lifetimes, from_poly_trait_ref, .. } => {
+                Scope::Binder { s, ref lifetimes, scope_type, .. } => {
                     // collect named lifetimes for suggestions
                     for name in lifetimes.keys() {
                         if let hir::ParamName::Plain(name) = name {
@@ -3124,20 +2976,9 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
                             lifetime_spans.push(name.span);
                         }
                     }
-                    // See comments in `resolve_lifetime_ref`
-                    match (from_poly_trait_ref, in_poly_trait_ref) {
-                        (true, false) => {
-                            in_poly_trait_ref = true;
-                            late_depth += 1;
-                        }
-                        (true, true) => {}
-                        (false, true) => {
-                            in_poly_trait_ref = false;
-                            late_depth += 1;
-                        }
-                        (false, false) => {
-                            late_depth += 1;
-                        }
+                    match scope_type {
+                        BinderScopeType::Normal => late_depth += 1,
+                        BinderScopeType::Concatenating => {}
                     }
                     scope = s;
                 }
@@ -3187,8 +3028,8 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
                 }
 
                 Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::TraitRefHackInner { s, .. }
-                | Scope::Supertrait { s, .. } => {
+                | Scope::Supertrait { s, .. }
+                | Scope::TraitRefBoundary { s, .. } => {
                     scope = s;
                 }
             }
@@ -3294,31 +3135,13 @@ fn report_elision_failure(
     fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
         debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref);
         let mut late_depth = 0;
-        let mut in_poly_trait_ref = false;
         let mut scope = self.scope;
         let lifetime = loop {
             match *scope {
-                Scope::TraitRefBoundary { s, .. } => {
-                    // We've exited nested poly trait refs; mark that we are no longer in nested trait refs.
-                    // We don't increase the late depth because this isn't a `Binder` scope
-                    in_poly_trait_ref = false;
-                    scope = s;
-                }
-
-                Scope::Binder { s, from_poly_trait_ref, .. } => {
-                    match (from_poly_trait_ref, in_poly_trait_ref) {
-                        (true, false) => {
-                            in_poly_trait_ref = true;
-                            late_depth += 1;
-                        }
-                        (true, true) => {}
-                        (false, true) => {
-                            in_poly_trait_ref = false;
-                            late_depth += 1;
-                        }
-                        (false, false) => {
-                            late_depth += 1;
-                        }
+                Scope::Binder { s, scope_type, .. } => {
+                    match scope_type {
+                        BinderScopeType::Normal => late_depth += 1,
+                        BinderScopeType::Concatenating => {}
                     }
                     scope = s;
                 }
@@ -3329,7 +3152,7 @@ fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime)
 
                 Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l,
 
-                Scope::TraitRefHackInner { s, .. } | Scope::Supertrait { s, .. } => {
+                Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => {
                     scope = s;
                 }
             }
@@ -3456,7 +3279,6 @@ fn check_lifetime_param_for_shadowing(
                 Scope::Body { s, .. }
                 | Scope::Elision { s, .. }
                 | Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::TraitRefHackInner { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. } => {
                     old_scope = s;
@@ -3515,7 +3337,6 @@ fn track_lifetime_uses(&self) -> bool {
                 } => break false,
 
                 Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::TraitRefHackInner { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. } => scope = s,
             }
index 129954381c9baf497321a6e1aea4350cfef88ce6..1d1969f7e78abeba3a9ce9b6aed0237280510b3e 100644 (file)
@@ -239,8 +239,6 @@ enum ResolutionError<'a> {
     ForwardDeclaredTyParam, // FIXME(const_generics_defaults)
     /// ERROR E0770: the type of const parameters must not depend on other generic parameters.
     ParamInTyOfConstParam(Symbol),
-    /// constant values inside of type parameter defaults must not depend on generic parameters.
-    ParamInAnonConstInTyDefault(Symbol),
     /// generic parameters must not be used inside const evaluations.
     ///
     /// This error is only emitted when using `min_const_generics`.
@@ -2672,26 +2670,18 @@ fn validate_res_from_ribs(
                 }
             }
             Res::Def(DefKind::TyParam, _) | Res::SelfTy(..) => {
-                let mut in_ty_param_default = false;
                 for rib in ribs {
-                    let has_generic_params = match rib.kind {
+                    let has_generic_params: HasGenericParams = match rib.kind {
                         NormalRibKind
                         | ClosureOrAsyncRibKind
                         | AssocItemRibKind
                         | ModuleRibKind(..)
-                        | MacroDefinition(..) => {
+                        | MacroDefinition(..)
+                        | ForwardGenericParamBanRibKind => {
                             // Nothing to do. Continue.
                             continue;
                         }
 
-                        // We only forbid constant items if we are inside of type defaults,
-                        // for example `struct Foo<T, U = [u8; std::mem::size_of::<T>()]>`
-                        ForwardGenericParamBanRibKind => {
-                            // FIXME(const_generic_defaults): we may need to distinguish between
-                            // being in type parameter defaults and const parameter defaults
-                            in_ty_param_default = true;
-                            continue;
-                        }
                         ConstantItemRibKind(trivial, _) => {
                             let features = self.session.features_untracked();
                             // HACK(min_const_generics): We currently only allow `N` or `{ N }`.
@@ -2720,19 +2710,7 @@ fn validate_res_from_ribs(
                                 }
                             }
 
-                            if in_ty_param_default {
-                                if record_used {
-                                    self.report_error(
-                                        span,
-                                        ResolutionError::ParamInAnonConstInTyDefault(
-                                            rib_ident.name,
-                                        ),
-                                    );
-                                }
-                                return Res::Err;
-                            } else {
-                                continue;
-                            }
+                            continue;
                         }
 
                         // This was an attempt to use a type parameter outside its scope.
@@ -2770,23 +2748,15 @@ fn validate_res_from_ribs(
                     ribs.next();
                 }
 
-                let mut in_ty_param_default = false;
                 for rib in ribs {
                     let has_generic_params = match rib.kind {
                         NormalRibKind
                         | ClosureOrAsyncRibKind
                         | AssocItemRibKind
                         | ModuleRibKind(..)
-                        | MacroDefinition(..) => continue,
-
-                        // We only forbid constant items if we are inside of type defaults,
-                        // for example `struct Foo<T, U = [u8; std::mem::size_of::<T>()]>`
-                        ForwardGenericParamBanRibKind => {
-                            // FIXME(const_generic_defaults): we may need to distinguish between
-                            // being in type parameter defaults and const parameter defaults
-                            in_ty_param_default = true;
-                            continue;
-                        }
+                        | MacroDefinition(..)
+                        | ForwardGenericParamBanRibKind => continue,
+
                         ConstantItemRibKind(trivial, _) => {
                             let features = self.session.features_untracked();
                             // HACK(min_const_generics): We currently only allow `N` or `{ N }`.
@@ -2808,19 +2778,7 @@ fn validate_res_from_ribs(
                                 return Res::Err;
                             }
 
-                            if in_ty_param_default {
-                                if record_used {
-                                    self.report_error(
-                                        span,
-                                        ResolutionError::ParamInAnonConstInTyDefault(
-                                            rib_ident.name,
-                                        ),
-                                    );
-                                }
-                                return Res::Err;
-                            } else {
-                                continue;
-                            }
+                            continue;
                         }
 
                         ItemRibKind(has_generic_params) => has_generic_params,
index b6b349e4a803d2ab79fd6946f00a5249e36e8807..52a6e4ff924f45c61069c83a19670b318d09f03f 100644 (file)
@@ -75,19 +75,21 @@ pub enum OptLevel {
 
 /// This is what the `LtoCli` values get mapped to after resolving defaults and
 /// and taking other command line options into account.
+///
+/// Note that linker plugin-based LTO is a different mechanism entirely.
 #[derive(Clone, PartialEq)]
 pub enum Lto {
-    /// Don't do any LTO whatsoever
+    /// Don't do any LTO whatsoever.
     No,
 
-    /// Do a full crate graph LTO with ThinLTO
+    /// Do a full-crate-graph (inter-crate) LTO with ThinLTO.
     Thin,
 
-    /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen
-    /// units).
+    /// Do a local ThinLTO (intra-crate, over the CodeGen Units of the local crate only). This is
+    /// only relevant if multiple CGUs are used.
     ThinLocal,
 
-    /// Do a full crate graph LTO with "fat" LTO
+    /// Do a full-crate-graph (inter-crate) LTO with "fat" LTO.
     Fat,
 }
 
@@ -795,7 +797,7 @@ pub const fn default_lib_output() -> CrateType {
     CrateType::Rlib
 }
 
-pub fn default_configuration(sess: &Session) -> CrateConfig {
+fn default_configuration(sess: &Session) -> CrateConfig {
     let end = &sess.target.endian;
     let arch = &sess.target.arch;
     let wordsz = sess.target.pointer_width.to_string();
@@ -890,7 +892,7 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo
     user_cfg
 }
 
-pub fn build_target_config(opts: &Options, target_override: Option<Target>) -> Target {
+pub(super) fn build_target_config(opts: &Options, target_override: Option<Target>) -> Target {
     let target_result = target_override.map_or_else(|| Target::search(&opts.target_triple), Ok);
     let target = target_result.unwrap_or_else(|e| {
         early_error(
index a184608ed29bfb8d7fa2607addac75dba7dabac3..fd26f50da5a8ef3e0b2a348c1ff9c08cffc98920 100644 (file)
@@ -814,7 +814,7 @@ fn parse_split_debuginfo(slot: &mut Option<SplitDebuginfo>, v: Option<&str>) ->
         "a single extra argument to append to the linker invocation (can be used several times)"),
     link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
         "extra arguments to append to the linker invocation (space separated)"),
-    link_dead_code: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
+    link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "keep dead code at link time (useful for code coverage) (default: no)"),
     link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
         "control whether to link Rust provided C objects/libraries or rely
index cc2583be94474ea208f77f4c1a9fe446e45f4549..7bff634fb2dd09baef261966758669e942e8fec9 100644 (file)
@@ -83,6 +83,12 @@ pub fn value_within_limit(&self, value: usize) -> bool {
     }
 }
 
+impl From<usize> for Limit {
+    fn from(value: usize) -> Self {
+        Self::new(value)
+    }
+}
+
 impl fmt::Display for Limit {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{}", self.0)
@@ -143,6 +149,10 @@ pub struct Session {
     /// operations such as auto-dereference and monomorphization.
     pub recursion_limit: OnceCell<Limit>,
 
+    /// The size at which the `large_assignments` lint starts
+    /// being emitted.
+    pub move_size_limit: OnceCell<usize>,
+
     /// The maximum length of types during monomorphization.
     pub type_length_limit: OnceCell<Limit>,
 
@@ -352,6 +362,11 @@ pub fn recursion_limit(&self) -> Limit {
         self.recursion_limit.get().copied().unwrap()
     }
 
+    #[inline]
+    pub fn move_size_limit(&self) -> usize {
+        self.move_size_limit.get().copied().unwrap()
+    }
+
     #[inline]
     pub fn type_length_limit(&self) -> Limit {
         self.type_length_limit.get().copied().unwrap()
@@ -1414,6 +1429,7 @@ pub fn build_session(
         features: OnceCell::new(),
         lint_store: OnceCell::new(),
         recursion_limit: OnceCell::new(),
+        move_size_limit: OnceCell::new(),
         type_length_limit: OnceCell::new(),
         const_eval_limit: OnceCell::new(),
         incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
index 6805d4f289077420d16ea58c8f3b76d895dc8c40..f75fe22767f47008499b1938c22cb9fb7cdfdf1d 100644 (file)
@@ -748,7 +748,7 @@ pub struct ExpnData {
 
     /// Used to force two `ExpnData`s to have different `Fingerprint`s.
     /// Due to macro expansion, it's possible to end up with two `ExpnId`s
-    /// that have identical `ExpnData`s. This violates the constract of `HashStable`
+    /// that have identical `ExpnData`s. This violates the contract of `HashStable`
     /// - the two `ExpnId`s are not equal, but their `Fingerprint`s are equal
     /// (since the numerical `ExpnId` value is not considered by the `HashStable`
     /// implementation).
index 6f6ff37c525a2659a286441d46610101f7d2619e..d30236ec3eccca8371bdf4f4fe6fb812e8098831 100644 (file)
@@ -16,7 +16,6 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(array_windows)]
 #![feature(crate_visibility_modifier)]
-#![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(negative_impls)]
 #![feature(nll)]
index a7f493569d32aee5198f5dc6e1f40ccf9b0c3321..ca3df07571ada052b696394a6068974e02dfdd9f 100644 (file)
         const_fn,
         const_fn_floating_point_arithmetic,
         const_fn_fn_ptr_basics,
+        const_fn_trait_bound,
         const_fn_transmute,
         const_fn_union,
+        const_fn_unsize,
         const_generic_defaults,
         const_generics,
         const_generics_defaults,
         label_break_value,
         lang,
         lang_items,
+        large_assignments,
         lateout,
         lazy_normalization_consts,
         le,
         more_struct_aliases,
         movbe_target_feature,
         move_ref_pattern,
+        move_size_limit,
         mul,
         mul_assign,
         mul_with_overflow,
         no,
         no_builtins,
         no_core,
+        no_coverage,
         no_crate_inject,
         no_debug,
         no_default_passes,
         non_modrs_mods,
         none_error,
         nontemporal_store,
-        nontrapping_dash_fptoint: "nontrapping-fptoint",
         noop_method_borrow,
         noop_method_clone,
         noop_method_deref,
         rustc_layout_scalar_valid_range_start,
         rustc_legacy_const_generics,
         rustc_macro_transparency,
+        rustc_main,
         rustc_mir,
         rustc_nonnull_optimization_guaranteed,
         rustc_object_lifetime_default,
         rustc_regions,
         rustc_reservation_impl,
         rustc_serialize,
+        rustc_skip_array_during_method_dispatch,
         rustc_specialization_trait,
         rustc_stable,
         rustc_std_internal_symbol,
         simd_reduce_or,
         simd_reduce_xor,
         simd_rem,
+        simd_round,
         simd_saturating_add,
         simd_saturating_sub,
         simd_scatter,
         simd_shl,
         simd_shr,
         simd_sub,
+        simd_trunc,
         simd_xor,
         since,
         sinf32,
index d9d7d467d92943956f0d3c3ab028caf08ffbcfda..0cf2441d84e04c5655a2fedf31208e08691d696c 100644 (file)
@@ -73,8 +73,8 @@ pub struct ArgAttribute: u16 {
 }
 
 /// Sometimes an ABI requires small integers to be extended to a full or partial register. This enum
-/// defines if this extension should be zero-extension or sign-extension when necssary. When it is
-/// not necesary to extend the argument, this enum is ignored.
+/// defines if this extension should be zero-extension or sign-extension when necessary. When it is
+/// not necessary to extend the argument, this enum is ignored.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum ArgExtension {
     None,
index b54764b9e323915915c4fac6eac59d542f7d8185..6702538874705df0ffe83ed820bf5453b2bc1223 100644 (file)
@@ -9,7 +9,6 @@
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(bool_to_option)]
-#![feature(const_fn)]
 #![feature(const_panic)]
 #![feature(nll)]
 #![feature(never_type)]
index da79bc2f338031237f6a29f2407035820e2493eb..1e2e5766a311bcf8cc6fb67894538ec424a98945 100644 (file)
@@ -1,4 +1,4 @@
-use crate::spec::{LinkerFlavor, StackProbeType, Target};
+use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target};
 
 pub fn target() -> Target {
     let mut base = super::linux_musl_base::opts();
@@ -7,6 +7,8 @@ pub fn target() -> Target {
     base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string());
     base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
     base.static_position_independent_executables = true;
+    base.supported_sanitizers =
+        SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD;
 
     Target {
         llvm_target: "x86_64-unknown-linux-musl".to_string(),
index 15a3d3ddd8d857531e9374f5971940407a8d5fc0..da5a1af7f770e929e49f5e9a6259bac5522ff4a7 100644 (file)
@@ -503,14 +503,14 @@ fn report_selection_error(
                             let unit_obligation =
                                 obligation.with(predicate.without_const().to_predicate(tcx));
                             if self.predicate_may_hold(&unit_obligation) {
+                                err.note("this trait is implemented for `()`.");
                                 err.note(
-                                    "the trait is implemented for `()`. \
-                                     Possibly this error has been caused by changes to \
-                                     Rust's type-inference algorithm (see issue #48950 \
-                                     <https://github.com/rust-lang/rust/issues/48950> \
-                                     for more information). Consider whether you meant to use \
-                                     the type `()` here instead.",
+                                    "this error might have been caused by changes to \
+                                    Rust's type-inference algorithm (see issue #48950 \
+                                    <https://github.com/rust-lang/rust/issues/48950> \
+                                    for more information).",
                                 );
+                                err.help("did you intend to use the type `()` here instead?");
                             }
                         }
 
@@ -1954,7 +1954,7 @@ pub enum ArgKind {
     Arg(String, String),
 
     /// An argument of tuple type. For a "found" argument, the span is
-    /// the locationo in the source of the pattern. For a "expected"
+    /// the location in the source of the pattern. For a "expected"
     /// argument, it will be None. The vector is a list of (name, ty)
     /// strings for the components of the tuple.
     Tuple(Option<Span>, Vec<(String, String)>),
index b5a458db6075fd72b88230d9f34b650b8e982bb1..d5e1bd3f9ea2eec0cdb652acc564b69c047f8c4a 100644 (file)
@@ -132,6 +132,14 @@ fn object_safety_violations_for_trait(
             .map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)),
     );
 
+    violations.extend(
+        tcx.associated_items(trait_def_id)
+            .in_definition_order()
+            .filter(|item| item.kind == ty::AssocKind::Type)
+            .filter(|item| !tcx.generics_of(item.def_id).params.is_empty())
+            .map(|item| ObjectSafetyViolation::GAT(item.ident.name, item.ident.span)),
+    );
+
     debug!(
         "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
         trait_def_id, violations
@@ -439,8 +447,7 @@ fn virtual_call_violation_for_method<'tcx>(
         return Some(MethodViolationCode::WhereClauseReferencesSelf);
     }
 
-    let receiver_ty =
-        tcx.liberate_late_bound_regions(method.def_id, sig.map_bound(|sig| sig.inputs()[0]));
+    let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0));
 
     // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on.
     // However, this is already considered object-safe. We allow it as a special case here.
index 31685a012ca2f03dd290c9be9c93b1a6d524a9a4..e338a21b60308d43748a5a764cc92f592e33b72d 100644 (file)
@@ -462,12 +462,11 @@ fn confirm_object_candidate(
 
         for assoc_type in assoc_types {
             if !tcx.generics_of(assoc_type).params.is_empty() {
-                // FIXME(generic_associated_types) generate placeholders to
-                // extend the trait substs.
-                tcx.sess.span_fatal(
+                tcx.sess.delay_span_bug(
                     obligation.cause.span,
-                    "generic associated types in trait objects are not supported yet",
+                    "GATs in trait object shouldn't have been considered",
                 );
+                return Err(SelectionError::Unimplemented);
             }
             // This maybe belongs in wf, but that can't (doesn't) handle
             // higher-ranked things.
index f441246909b45a80da6a41a978a76350eb34f22a..e4aabbdb7ede7be92a145e5850c5e4967db34243 100644 (file)
@@ -944,7 +944,7 @@ fn insert_evaluation_cache(
     /// subobligations without taking in a 'parent' depth, causing the
     /// generated subobligations to have a `recursion_depth` of `0`.
     ///
-    /// To ensure that obligation_depth never decreasees, we force all subobligations
+    /// To ensure that obligation_depth never decreases, we force all subobligations
     /// to have at least the depth of the original obligation.
     fn add_depth<T: 'cx, I: Iterator<Item = &'cx mut Obligation<'tcx, T>>>(
         &self,
index aa5d3388401ae7651283547e0da3d52108c6e0db..67a692eceacc4e99175899ae305f6be39a865709 100644 (file)
@@ -20,6 +20,7 @@
 mod common_traits;
 pub mod instance;
 mod needs_drop;
+pub mod representability;
 mod ty;
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs
new file mode 100644 (file)
index 0000000..ca00163
--- /dev/null
@@ -0,0 +1,186 @@
+//! Check whether a type is representable.
+use rustc_data_structures::stable_map::FxHashMap;
+use rustc_hir as hir;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
+use std::cmp;
+
+/// Describes whether a type is representable. For types that are not
+/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to
+/// distinguish between types that are recursive with themselves and types that
+/// contain a different recursive type. These cases can therefore be treated
+/// differently when reporting errors.
+///
+/// The ordering of the cases is significant. They are sorted so that cmp::max
+/// will keep the "more erroneous" of two values.
+#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
+pub enum Representability {
+    Representable,
+    ContainsRecursive,
+    SelfRecursive(Vec<Span>),
+}
+
+/// Check whether a type is representable. This means it cannot contain unboxed
+/// structural recursion. This check is needed for structs and enums.
+pub fn ty_is_representable<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, sp: Span) -> Representability {
+    debug!("is_type_representable: {:?}", ty);
+    // To avoid a stack overflow when checking an enum variant or struct that
+    // contains a different, structurally recursive type, maintain a stack
+    // of seen types and check recursion for each of them (issues #3008, #3779).
+    let mut seen: Vec<Ty<'_>> = Vec::new();
+    let mut representable_cache = FxHashMap::default();
+    let r = is_type_structurally_recursive(tcx, sp, &mut seen, &mut representable_cache, ty);
+    debug!("is_type_representable: {:?} is {:?}", ty, r);
+    r
+}
+
+// Iterate until something non-representable is found
+fn fold_repr<It: Iterator<Item = Representability>>(iter: It) -> Representability {
+    iter.fold(Representability::Representable, |r1, r2| match (r1, r2) {
+        (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => {
+            Representability::SelfRecursive(v1.into_iter().chain(v2).collect())
+        }
+        (r1, r2) => cmp::max(r1, r2),
+    })
+}
+
+fn are_inner_types_recursive<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sp: Span,
+    seen: &mut Vec<Ty<'tcx>>,
+    representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+    ty: Ty<'tcx>,
+) -> Representability {
+    match ty.kind() {
+        ty::Tuple(..) => {
+            // Find non representable
+            fold_repr(
+                ty.tuple_fields().map(|ty| {
+                    is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty)
+                }),
+            )
+        }
+        // Fixed-length vectors.
+        // FIXME(#11924) Behavior undecided for zero-length vectors.
+        ty::Array(ty, _) => is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty),
+        ty::Adt(def, substs) => {
+            // Find non representable fields with their spans
+            fold_repr(def.all_fields().map(|field| {
+                let ty = field.ty(tcx, substs);
+                let span = match field
+                    .did
+                    .as_local()
+                    .map(|id| tcx.hir().local_def_id_to_hir_id(id))
+                    .and_then(|id| tcx.hir().find(id))
+                {
+                    Some(hir::Node::Field(field)) => field.ty.span,
+                    _ => sp,
+                };
+                match is_type_structurally_recursive(tcx, span, seen, representable_cache, ty) {
+                    Representability::SelfRecursive(_) => {
+                        Representability::SelfRecursive(vec![span])
+                    }
+                    x => x,
+                }
+            }))
+        }
+        ty::Closure(..) => {
+            // this check is run on type definitions, so we don't expect
+            // to see closure types
+            bug!("requires check invoked on inapplicable type: {:?}", ty)
+        }
+        _ => Representability::Representable,
+    }
+}
+
+fn same_adt<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool {
+    match *ty.kind() {
+        ty::Adt(ty_def, _) => ty_def == def,
+        _ => false,
+    }
+}
+
+// Does the type `ty` directly (without indirection through a pointer)
+// contain any types on stack `seen`?
+fn is_type_structurally_recursive<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sp: Span,
+    seen: &mut Vec<Ty<'tcx>>,
+    representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+    ty: Ty<'tcx>,
+) -> Representability {
+    debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp);
+    if let Some(representability) = representable_cache.get(ty) {
+        debug!(
+            "is_type_structurally_recursive: {:?} {:?} - (cached) {:?}",
+            ty, sp, representability
+        );
+        return representability.clone();
+    }
+
+    let representability =
+        is_type_structurally_recursive_inner(tcx, sp, seen, representable_cache, ty);
+
+    representable_cache.insert(ty, representability.clone());
+    representability
+}
+
+fn is_type_structurally_recursive_inner<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sp: Span,
+    seen: &mut Vec<Ty<'tcx>>,
+    representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+    ty: Ty<'tcx>,
+) -> Representability {
+    match ty.kind() {
+        ty::Adt(def, _) => {
+            {
+                // Iterate through stack of previously seen types.
+                let mut iter = seen.iter();
+
+                // The first item in `seen` is the type we are actually curious about.
+                // We want to return SelfRecursive if this type contains itself.
+                // It is important that we DON'T take generic parameters into account
+                // for this check, so that Bar<T> in this example counts as SelfRecursive:
+                //
+                // struct Foo;
+                // struct Bar<T> { x: Bar<Foo> }
+
+                if let Some(&seen_adt) = iter.next() {
+                    if same_adt(seen_adt, *def) {
+                        debug!("SelfRecursive: {:?} contains {:?}", seen_adt, ty);
+                        return Representability::SelfRecursive(vec![sp]);
+                    }
+                }
+
+                // We also need to know whether the first item contains other types
+                // that are structurally recursive. If we don't catch this case, we
+                // will recurse infinitely for some inputs.
+                //
+                // It is important that we DO take generic parameters into account
+                // here, so that code like this is considered SelfRecursive, not
+                // ContainsRecursive:
+                //
+                // struct Foo { Option<Option<Foo>> }
+
+                for &seen_adt in iter {
+                    if ty::TyS::same_type(ty, seen_adt) {
+                        debug!("ContainsRecursive: {:?} contains {:?}", seen_adt, ty);
+                        return Representability::ContainsRecursive;
+                    }
+                }
+            }
+
+            // For structs and enums, track all previously seen types by pushing them
+            // onto the 'seen' stack.
+            seen.push(ty);
+            let out = are_inner_types_recursive(tcx, sp, seen, representable_cache, ty);
+            seen.pop();
+            out
+        }
+        _ => {
+            // No need to push in other cases.
+            are_inner_types_recursive(tcx, sp, seen, representable_cache, ty)
+        }
+    }
+}
index 29f1761b84d2b3e54b4bf5d4340535cf452ab0a4..38e5ce6fd831c686c56856e10a35b6e02b66d863 100644 (file)
@@ -210,9 +210,9 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
     }
 }
 
-fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssociatedItems<'_> {
+fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> {
     let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did));
-    ty::AssociatedItems::new(items)
+    ty::AssocItems::new(items)
 }
 
 fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
index d92d317e34ad68e89e5dc4fc9e6697ac78ee9192..eb55a8a23ad02b80f31929bab9a49281f76c9058 100644 (file)
@@ -26,3 +26,4 @@ rustc_span = { path = "../rustc_span" }
 rustc_index = { path = "../rustc_index" }
 rustc_infer = { path = "../rustc_infer" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
+rustc_ty_utils = { path = "../rustc_ty_utils" }
index 7a297f2c65f13fb68df8c90867fbaea8c079fecf..a3804e468da2da74b0e159948fe23ca8f74cc180 100644 (file)
@@ -109,6 +109,20 @@ fn generic_arg_mismatch_err(
                     );
                 }
             }
+            (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => {
+                let body = tcx.hir().body(cnst.value.body);
+                if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) =
+                    body.value.kind
+                {
+                    if let Res::Def(DefKind::Fn { .. }, id) = path.res {
+                        err.help(&format!(
+                            "`{}` is a function item, not a type",
+                            tcx.item_name(id)
+                        ));
+                        err.help("function item types cannot be named directly");
+                    }
+                }
+            }
             _ => {}
         }
 
@@ -286,7 +300,7 @@ pub fn create_substs_for_generic_args<'a>(
                                                         ParamKindOrd::Const {
                                                             unordered: tcx
                                                                 .features()
-                                                                .const_generics,
+                                                                .unordered_const_ty_params(),
                                                         }
                                                     }
                                                 },
@@ -309,7 +323,9 @@ pub fn create_substs_for_generic_args<'a>(
                                             GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
                                             GenericArg::Type(_) => ParamKindOrd::Type,
                                             GenericArg::Const(_) => ParamKindOrd::Const {
-                                                unordered: tcx.features().const_generics,
+                                                unordered: tcx
+                                                    .features()
+                                                    .unordered_const_ty_params(),
                                             },
                                         }),
                                         Some(&format!(
index b6de491911ab7c260c5d48bb40fd0dbd21e1c257..2f2e90e4bd66fcf465c165478ddc1e29f67f8a8f 100644 (file)
@@ -513,7 +513,9 @@ fn inferred_kind(
                     GenericParamDefKind::Const { has_default } => {
                         let ty = tcx.at(self.span).type_of(param.def_id);
                         if !infer_args && has_default {
-                            tcx.const_param_default(param.def_id).into()
+                            tcx.const_param_default(param.def_id)
+                                .subst_spanned(tcx, substs.unwrap(), Some(self.span))
+                                .into()
                         } else {
                             if infer_args {
                                 self.astconv.ct_infer(ty, Some(param), self.span).into()
@@ -2277,9 +2279,9 @@ fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool) -> Ty<'tcx> {
                 let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(&ty), length));
                 self.normalize_ty(ast_ty.span, array_ty)
             }
-            hir::TyKind::Typeof(ref _e) => {
+            hir::TyKind::Typeof(ref e) => {
                 tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span });
-                tcx.ty_error()
+                tcx.type_of(tcx.hir().local_def_id(e.hir_id))
             }
             hir::TyKind::Infer => {
                 // Infer also appears as the type of arguments or return
index 16c344e8e2b9e2bc893bdf6051b112bf13292c90..b760a54f08c76f1e2f979468ca87f0ca993cd987 100644 (file)
@@ -359,6 +359,21 @@ fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
                     {
                         sugg = Some(format!("&{}", mutbl.prefix_str()));
                     }
+                } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind() {
+                    if fcx
+                        .try_coerce(
+                            self.expr,
+                            fcx.tcx.mk_ref(
+                                &ty::RegionKind::ReErased,
+                                TypeAndMut { ty: self.expr_ty, mutbl },
+                            ),
+                            self.cast_ty,
+                            AllowTwoPhase::No,
+                        )
+                        .is_ok()
+                    {
+                        sugg = Some(format!("&{}", mutbl.prefix_str()));
+                    }
                 }
                 if let Some(sugg) = sugg {
                     err.span_label(self.span, "invalid cast");
index 892abb5a344659ff7107a84568e7fdffd41b6556..72c633dcb20c4119db20a04ed5173f0c695ff3ca 100644 (file)
@@ -15,7 +15,7 @@
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::layout::MAX_SIMD_LANES;
 use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
+use rustc_middle::ty::util::{Discr, IntTypeExt};
 use rustc_middle::ty::{self, ParamEnv, RegionKind, ToPredicate, Ty, TyCtxt};
 use rustc_session::config::EntryFnType;
 use rustc_session::lint::builtin::UNINHABITED_STATIC;
@@ -25,6 +25,7 @@
 use rustc_trait_selection::opaque_types::InferCtxtExt as _;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCauseCode};
+use rustc_ty_utils::representability::{self, Representability};
 
 use std::iter;
 use std::ops::ControlFlow;
@@ -1188,7 +1189,7 @@ pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalD
     // recursive type. It is only necessary to throw an error on those that
     // contain themselves. For case 2, there must be an inner type that will be
     // caught by case 1.
-    match rty.is_representable(tcx, sp) {
+    match representability::ty_is_representable(tcx, rty, sp) {
         Representability::SelfRecursive(spans) => {
             recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans);
             return false;
index 37538267b866a23b13fc0cc28e687105b5ec310d..236fec94bdba79fc0e28bd9d3adc9fcade141fb0 100644 (file)
@@ -973,6 +973,14 @@ fn try_find_coercion_lub<E>(
             }
         };
         if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
+            // Intrinsics are not coercible to function pointers.
+            if a_sig.abi() == Abi::RustIntrinsic
+                || a_sig.abi() == Abi::PlatformIntrinsic
+                || b_sig.abi() == Abi::RustIntrinsic
+                || b_sig.abi() == Abi::PlatformIntrinsic
+            {
+                return Err(TypeError::IntrinsicCast);
+            }
             // The signature must match.
             let a_sig = self.normalize_associated_types_in(new.span, a_sig);
             let b_sig = self.normalize_associated_types_in(new.span, b_sig);
@@ -1486,34 +1494,14 @@ fn report_return_mismatched_types<'a>(
         if let (Some((expr, _)), Some((fn_decl, _, _))) =
             (expression, fcx.get_node_fn_decl(parent_item))
         {
-            fcx.suggest_missing_return_expr(&mut err, expr, fn_decl, expected, found, parent_id);
+            fcx.suggest_missing_break_or_return_expr(
+                &mut err, expr, fn_decl, expected, found, id, parent_id,
+            );
         }
 
         if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) {
             self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output);
         }
-
-        if let Some(sp) = fcx.ret_coercion_span.get() {
-            // If the closure has an explicit return type annotation,
-            // then a type error may occur at the first return expression we
-            // see in the closure (if it conflicts with the declared
-            // return type). Skip adding a note in this case, since it
-            // would be incorrect.
-            if !err.span.primary_spans().iter().any(|&span| span == sp) {
-                let hir = fcx.tcx.hir();
-                let body_owner = hir.body_owned_by(hir.enclosing_body_owner(fcx.body_id));
-                if fcx.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) {
-                    err.span_note(
-                        sp,
-                        &format!(
-                            "return type inferred to be `{}` here",
-                            fcx.resolve_vars_if_possible(expected)
-                        ),
-                    );
-                }
-            }
-        }
-
         err
     }
 
index d879b6e97dcfb884098a9915144e3167bd50c90b..e5fcdcfa74315e02ffcefa64eaff73977e777f8e 100644 (file)
@@ -37,6 +37,7 @@ pub fn emit_coerce_suggestions(
         self.suggest_missing_parentheses(err, expr);
         self.note_need_for_fn_pointer(err, expected, expr_ty);
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
+        self.report_closure_infered_return_type(err, expected)
     }
 
     // Requires that the two types unify, and prints an error message if
@@ -1061,4 +1062,32 @@ pub fn check_for_cast(
             _ => false,
         }
     }
+
+    // Report the type inferred by the return statement.
+    fn report_closure_infered_return_type(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expected: Ty<'tcx>,
+    ) {
+        if let Some(sp) = self.ret_coercion_span.get() {
+            // If the closure has an explicit return type annotation,
+            // then a type error may occur at the first return expression we
+            // see in the closure (if it conflicts with the declared
+            // return type). Skip adding a note in this case, since it
+            // would be incorrect.
+            if !err.span.primary_spans().iter().any(|&span| span == sp) {
+                let hir = self.tcx.hir();
+                let body_owner = hir.body_owned_by(hir.enclosing_body_owner(self.body_id));
+                if self.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) {
+                    err.span_note(
+                        sp,
+                        &format!(
+                            "return type inferred to be `{}` here",
+                            self.resolve_vars_if_possible(expected)
+                        ),
+                    );
+                }
+            }
+        }
+    }
 }
index 9ace455042103559b738507ab3009ada6ebe0143..a50f8e1c655996fef3a7f50d24e7509314ac67c8 100644 (file)
@@ -1446,7 +1446,9 @@ fn inferred_kind(
                     }
                     GenericParamDefKind::Const { has_default, .. } => {
                         if !infer_args && has_default {
-                            tcx.const_param_default(param.def_id).into()
+                            tcx.const_param_default(param.def_id)
+                                .subst_spanned(tcx, substs.unwrap(), Some(self.span))
+                                .into()
                         } else {
                             self.fcx.var_for_def(self.span, param)
                         }
index b758334484535713211619e513ea0623457d840f..d6b1e56316b3751d20cf1e5b07a5b33d208c9493 100644 (file)
@@ -8,7 +8,7 @@
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, ItemKind, Node};
+use rustc_hir::{Expr, ExprKind, ItemKind, Node, Stmt, StmtKind};
 use rustc_infer::infer;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Binder, Ty};
@@ -55,7 +55,9 @@ pub fn suggest_mismatched_types_on_tail(
             pointing_at_return_type =
                 self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
             let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
-            self.suggest_missing_return_expr(err, expr, &fn_decl, expected, found, fn_id);
+            self.suggest_missing_break_or_return_expr(
+                err, expr, &fn_decl, expected, found, blk_id, fn_id,
+            );
         }
         pointing_at_return_type
     }
@@ -472,7 +474,7 @@ pub(in super::super) fn suggest_missing_return_type(
         }
     }
 
-    pub(in super::super) fn suggest_missing_return_expr(
+    pub(in super::super) fn suggest_missing_break_or_return_expr(
         &self,
         err: &mut DiagnosticBuilder<'_>,
         expr: &'tcx hir::Expr<'tcx>,
@@ -480,14 +482,38 @@ pub(in super::super) fn suggest_missing_return_expr(
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
         id: hir::HirId,
+        fn_id: hir::HirId,
     ) {
         if !expected.is_unit() {
             return;
         }
         let found = self.resolve_vars_with_obligations(found);
+
+        let in_loop = self.is_loop(id)
+            || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
+
+        let in_local_statement = self.is_local_statement(id)
+            || self
+                .tcx
+                .hir()
+                .parent_iter(id)
+                .any(|(parent_id, _)| self.is_local_statement(parent_id));
+
+        if in_loop && in_local_statement {
+            err.multipart_suggestion(
+                "you might have meant to break the loop with this value",
+                vec![
+                    (expr.span.shrink_to_lo(), "break ".to_string()),
+                    (expr.span.shrink_to_hi(), ";".to_string()),
+                ],
+                Applicability::MaybeIncorrect,
+            );
+            return;
+        }
+
         if let hir::FnRetTy::Return(ty) = fn_decl.output {
             let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
-            let bound_vars = self.tcx.late_bound_vars(id);
+            let bound_vars = self.tcx.late_bound_vars(fn_id);
             let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
             let ty = self.normalize_associated_types_in(expr.span, ty);
             if self.can_coerce(found, ty) {
@@ -514,4 +540,14 @@ pub(in super::super) fn suggest_missing_parentheses(
             self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
         }
     }
+
+    fn is_loop(&self, id: hir::HirId) -> bool {
+        let node = self.tcx.hir().get(id);
+        matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
+    }
+
+    fn is_local_statement(&self, id: hir::HirId) -> bool {
+        let node = self.tcx.hir().get(id);
+        matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
+    }
 }
index 4c5d16d0b70c0a415f535ea187309394a64d65db..2683e886eeb0f1ebb6664bed44fca837cdb3d6d7 100644 (file)
@@ -6,7 +6,6 @@
 use rustc_middle::ty::Ty;
 use rustc_span::{sym, Span};
 use rustc_trait_selection::traits;
-use std::mem;
 
 pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
     fcx: &'a FnCtxt<'a, 'tcx>,
@@ -14,12 +13,12 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
     // parameters are special cases of patterns, but we want to handle them as
     // *distinct* cases. so track when we are hitting a pattern *within* an fn
     // parameter.
-    outermost_fn_param_pat: bool,
+    outermost_fn_param_pat: Option<Span>,
 }
 
 impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
     pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>, parent_id: hir::HirId) -> Self {
-        Self { fcx, parent_id, outermost_fn_param_pat: false }
+        Self { fcx, parent_id, outermost_fn_param_pat: None }
     }
 
     fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
@@ -92,7 +91,7 @@ fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
     }
 
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
-        let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, true);
+        let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span);
         intravisit::walk_param(self, param);
         self.outermost_fn_param_pat = old_outermost_fn_param_pat;
     }
@@ -102,12 +101,12 @@ fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
         if let PatKind::Binding(_, _, ident, _) = p.kind {
             let var_ty = self.assign(p.span, p.hir_id, None);
 
-            if self.outermost_fn_param_pat {
+            if let Some(ty_span) = self.outermost_fn_param_pat {
                 if !self.fcx.tcx.features().unsized_fn_params {
                     self.fcx.require_type_is_sized(
                         var_ty,
                         p.span,
-                        traits::SizedArgumentType(Some(p.span)),
+                        traits::SizedArgumentType(Some(ty_span)),
                     );
                 }
             } else {
@@ -123,7 +122,7 @@ fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
                 var_ty
             );
         }
-        let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, false);
+        let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take();
         intravisit::walk_pat(self, p);
         self.outermost_fn_param_pat = old_outermost_fn_param_pat;
     }
index 303a77507cf7bc84a3fc1e1a341e0e1d828e44c2..5741b6824b5d70662efa746791b3f043a05aae0b 100644 (file)
@@ -407,8 +407,8 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
         | sym::simd_fpow
         | sym::simd_saturating_add
         | sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)),
-        sym::simd_neg => (1, vec![param(0)], param(0)),
-        sym::simd_fsqrt
+        sym::simd_neg
+        sym::simd_fsqrt
         | sym::simd_fsin
         | sym::simd_fcos
         | sym::simd_fexp
@@ -417,8 +417,10 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
         | sym::simd_flog10
         | sym::simd_flog
         | sym::simd_fabs
+        | sym::simd_ceil
         | sym::simd_floor
-        | sym::simd_ceil => (1, vec![param(0)], param(0)),
+        | sym::simd_round
+        | sym::simd_trunc => (1, vec![param(0)], param(0)),
         sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)),
         sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)),
         sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)),
index 15342235d488a438b282535bcae2709a43f7d4ed..c79743f2d7363d48c862c624409f825327fabd05 100644 (file)
@@ -158,7 +158,7 @@ enum ProbeResult {
 
 /// When adjusting a receiver we often want to do one of
 ///
-/// - Add a `&` (or `&mut`), converting the recevier from `T` to `&T` (or `&mut T`)
+/// - Add a `&` (or `&mut`), converting the receiver from `T` to `&T` (or `&mut T`)
 /// - If the receiver has type `*mut T`, convert it to `*const T`
 ///
 /// This type tells us which one to do.
@@ -1461,6 +1461,16 @@ fn consider_probe(
                 }
 
                 TraitCandidate(trait_ref) => {
+                    if let Some(method_name) = self.method_name {
+                        // Some trait methods are excluded for arrays before 2021.
+                        // (`array.into_iter()` wants a slice iterator for compatibility.)
+                        if self_ty.is_array() && !method_name.span.rust_2021() {
+                            let trait_def = self.tcx.trait_def(trait_ref.def_id);
+                            if trait_def.skip_array_during_method_dispatch {
+                                return ProbeResult::NoMatch;
+                            }
+                        }
+                    }
                     let predicate = trait_ref.without_const().to_predicate(self.tcx);
                     let obligation = traits::Obligation::new(cause, self.param_env, predicate);
                     if !self.predicate_may_hold(&obligation) {
index 02fe8312c4c1fed6a777d771762a01c7af3450a6..73e35f0171aa773b21358c50652487073c8887e4 100644 (file)
@@ -988,6 +988,12 @@ fn suggest_traits_to_import(
         let mut alt_rcvr_sugg = false;
         if let SelfSource::MethodCall(rcvr) = source {
             debug!(?span, ?item_name, ?rcvr_ty, ?rcvr);
+            let skippable = [
+                self.tcx.lang_items().clone_trait(),
+                self.tcx.lang_items().deref_trait(),
+                self.tcx.lang_items().deref_mut_trait(),
+                self.tcx.lang_items().drop_trait(),
+            ];
             // 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.
@@ -996,6 +1002,27 @@ fn suggest_traits_to_import(
                 (self.tcx.mk_mut_ref(&ty::ReErased, rcvr_ty), "&mut "),
                 (self.tcx.mk_imm_ref(&ty::ReErased, rcvr_ty), "&"),
             ] {
+                if let Ok(pick) = self.lookup_probe(
+                    span,
+                    item_name,
+                    rcvr_ty,
+                    rcvr,
+                    crate::check::method::probe::ProbeScope::AllTraits,
+                ) {
+                    // If the method is defined for the receiver we have, it likely wasn't `use`d.
+                    // We point at the method, but we just skip the rest of the check for arbitrary
+                    // self types and rely on the suggestion to `use` the trait from
+                    // `suggest_valid_traits`.
+                    let did = Some(pick.item.container.id());
+                    let skip = skippable.contains(&did);
+                    if pick.autoderefs == 0 && !skip {
+                        err.span_label(
+                            pick.item.ident.span,
+                            &format!("the method is available for `{}` here", rcvr_ty),
+                        );
+                    }
+                    break;
+                }
                 for (rcvr_ty, pre) in &[
                     (self.tcx.mk_lang_item(rcvr_ty, LangItem::OwnedBox), "Box::new"),
                     (self.tcx.mk_lang_item(rcvr_ty, LangItem::Pin), "Pin::new"),
@@ -1015,13 +1042,7 @@ fn suggest_traits_to_import(
                             // We don't want to suggest a container type when the missing
                             // method is `.clone()` or `.deref()` otherwise we'd suggest
                             // `Arc::new(foo).clone()`, which is far from what the user wants.
-                            let skip = [
-                                self.tcx.lang_items().clone_trait(),
-                                self.tcx.lang_items().deref_trait(),
-                                self.tcx.lang_items().deref_mut_trait(),
-                                self.tcx.lang_items().drop_trait(),
-                            ]
-                            .contains(&did);
+                            let skip = skippable.contains(&did);
                             // Make sure the method is defined for the *actual* receiver: we don't
                             // want to treat `Box<Self>` as a receiver if it only works because of
                             // an autoderef to `&self`
@@ -1047,7 +1068,7 @@ fn suggest_traits_to_import(
                 }
             }
         }
-        if !alt_rcvr_sugg && self.suggest_valid_traits(err, valid_out_of_scope_traits) {
+        if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
             return;
         }
 
index 80e173de6b6e565c354a843299158c1a4e66592c..cb4257e05347ae2550195d5f28fb779b9d29083e 100644 (file)
@@ -540,6 +540,12 @@ fn typeck_with_fallback<'tcx>(
                             kind: TypeVariableOriginKind::TypeInference,
                             span,
                         }),
+                        Node::Ty(&hir::Ty {
+                            kind: hir::TyKind::Typeof(ref anon_const), ..
+                        }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::TypeInference,
+                            span,
+                        }),
                         Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(ia), .. })
                             if ia.operands.iter().any(|(op, _op_sp)| match op {
                                 hir::InlineAsmOperand::Const { anon_const } => {
index 6f8dd39958c04f020153fc8c0653a7efa3711f55..751eebb9f95644ed2eaa0b0271f2122f6a475ab8 100644 (file)
@@ -1588,7 +1588,7 @@ fn migration_suggestion_for_2229(
 /// If both the CaptureKind and Expression are considered to be equivalent,
 /// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize
 /// expressions reported back to the user as part of diagnostics based on which appears earlier
-/// in the closure. This can be acheived simply by calling
+/// in the closure. This can be achieved simply by calling
 /// `determine_capture_info(existing_info, current_info)`. This works out because the
 /// expressions that occur earlier in the closure body than the current expression are processed before.
 /// Consider the following example
index 887cc42a1dd272bb058f5c5eac78be86cc141a1b..26871d6f0285cecbf1449e44e3cdec0c1c0133e6 100644 (file)
@@ -728,20 +728,36 @@ fn check_where_clauses<'tcx, 'fcx>(
     //
     // Here, the default `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold.
     for param in &generics.params {
-        if let GenericParamDefKind::Type { .. } = param.kind {
-            if is_our_default(&param) {
-                let ty = fcx.tcx.type_of(param.def_id);
-                // Ignore dependent defaults -- that is, where the default of one type
-                // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
-                // be sure if it will error or not as user might always specify the other.
-                if !ty.needs_subst() {
+        match param.kind {
+            GenericParamDefKind::Type { .. } => {
+                if is_our_default(&param) {
+                    let ty = fcx.tcx.type_of(param.def_id);
+                    // Ignore dependent defaults -- that is, where the default of one type
+                    // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
+                    // be sure if it will error or not as user might always specify the other.
+                    if !ty.needs_subst() {
+                        fcx.register_wf_obligation(
+                            ty.into(),
+                            fcx.tcx.def_span(param.def_id),
+                            ObligationCauseCode::MiscObligation,
+                        );
+                    }
+                }
+            }
+            GenericParamDefKind::Const { .. } => {
+                // FIXME(const_generics_defaults): Figure out if this
+                // is the behavior we want, see the comment further below.
+                if is_our_default(&param) {
+                    let default_ct = tcx.const_param_default(param.def_id);
                     fcx.register_wf_obligation(
-                        ty.into(),
+                        default_ct.into(),
                         fcx.tcx.def_span(param.def_id),
                         ObligationCauseCode::MiscObligation,
                     );
                 }
             }
+            // Doesn't have defaults.
+            GenericParamDefKind::Lifetime => {}
         }
     }
 
@@ -774,14 +790,25 @@ fn check_where_clauses<'tcx, 'fcx>(
                 fcx.tcx.mk_param_from_def(param)
             }
             GenericParamDefKind::Const { .. } => {
+                // FIXME(const_generics_defaults): I(@lcnr) feel like always
+                // using the const parameter is the right choice here, even
+                // if it needs substs.
+                //
+                // Before stabilizing this we probably want to get some tests
+                // where this makes a difference and figure out what's the exact
+                // behavior we want here.
+
+                // If the param has a default, ...
                 if is_our_default(param) {
                     let default_ct = tcx.const_param_default(param.def_id);
-                    // Const params currently have to be concrete.
-                    assert!(!default_ct.needs_subst());
-                    default_ct.into()
-                } else {
-                    fcx.tcx.mk_param_from_def(param)
+                    // ... and it's not a dependent default, ...
+                    if !default_ct.needs_subst() {
+                        // ... then substitute it with the default.
+                        return default_ct.into();
+                    }
                 }
+
+                fcx.tcx.mk_param_from_def(param)
             }
         }
     });
index 29654099992029e45ce61ca8bd4ea1286c8ee9fd..c69389e7b432b1578de4879b47add8b147c64bb1 100644 (file)
@@ -24,8 +24,8 @@ impl InherentOverlapChecker<'tcx> {
     /// namespace.
     fn impls_have_common_items(
         &self,
-        impl_items1: &ty::AssociatedItems<'_>,
-        impl_items2: &ty::AssociatedItems<'_>,
+        impl_items1: &ty::AssocItems<'_>,
+        impl_items2: &ty::AssocItems<'_>,
     ) -> bool {
         let mut impl_items1 = &impl_items1;
         let mut impl_items2 = &impl_items2;
index 1477418d5d8cf484d63eaa924b29e72226b8c4d1..190c9d35934f93219450ec50d3ded0a41850f282 100644 (file)
@@ -1191,6 +1191,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
     }
 
     let is_marker = tcx.has_attr(def_id, sym::marker);
+    let skip_array_during_method_dispatch =
+        tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
     let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
         ty::trait_def::TraitSpecializationKind::Marker
     } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
@@ -1199,7 +1201,16 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
         ty::trait_def::TraitSpecializationKind::None
     };
     let def_path_hash = tcx.def_path_hash(def_id);
-    ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, spec_kind, def_path_hash)
+    ty::TraitDef::new(
+        def_id,
+        unsafety,
+        paren_sugar,
+        is_auto,
+        is_marker,
+        skip_array_during_method_dispatch,
+        spec_kind,
+        def_path_hash,
+    )
 }
 
 fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {
@@ -1316,13 +1327,13 @@ fn has_late_bound_regions<'tcx>(
     }
 }
 
-struct AnonConstInParamListDetector {
-    in_param_list: bool,
-    found_anon_const_in_list: bool,
+struct AnonConstInParamTyDetector {
+    in_param_ty: bool,
+    found_anon_const_in_param_ty: bool,
     ct: HirId,
 }
 
-impl<'v> Visitor<'v> for AnonConstInParamListDetector {
+impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
     type Map = intravisit::ErasedMap<'v>;
 
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -1330,15 +1341,17 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
     }
 
     fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
-        let prev = self.in_param_list;
-        self.in_param_list = true;
-        intravisit::walk_generic_param(self, p);
-        self.in_param_list = prev;
+        if let GenericParamKind::Const { ref ty, default: _ } = p.kind {
+            let prev = self.in_param_ty;
+            self.in_param_ty = true;
+            self.visit_ty(ty);
+            self.in_param_ty = prev;
+        }
     }
 
     fn visit_anon_const(&mut self, c: &'v hir::AnonConst) {
-        if self.in_param_list && self.ct == c.hir_id {
-            self.found_anon_const_in_list = true;
+        if self.in_param_ty && self.ct == c.hir_id {
+            self.found_anon_const_in_param_ty = true;
         } else {
             intravisit::walk_anon_const(self, c)
         }
@@ -1366,27 +1379,24 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
             let parent_id = tcx.hir().get_parent_item(hir_id);
             let parent_def_id = tcx.hir().local_def_id(parent_id);
 
-            let mut in_param_list = false;
+            let mut in_param_ty = false;
             for (_parent, node) in tcx.hir().parent_iter(hir_id) {
                 if let Some(generics) = node.generics() {
-                    let mut visitor = AnonConstInParamListDetector {
-                        in_param_list: false,
-                        found_anon_const_in_list: false,
+                    let mut visitor = AnonConstInParamTyDetector {
+                        in_param_ty: false,
+                        found_anon_const_in_param_ty: false,
                         ct: hir_id,
                     };
 
                     visitor.visit_generics(generics);
-                    in_param_list = visitor.found_anon_const_in_list;
+                    in_param_ty = visitor.found_anon_const_in_param_ty;
                     break;
                 }
             }
 
-            if in_param_list {
+            if in_param_ty {
                 // We do not allow generic parameters in anon consts if we are inside
-                // of a param list.
-                //
-                // This affects both default type bindings, e.g. `struct<T, U = [u8; std::mem::size_of::<T>()]>(T, U)`,
-                // and the types of const parameters, e.g. `struct V<const N: usize, const M: [u8; N]>();`.
+                // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
                 None
             } else if tcx.lazy_normalization() {
                 // HACK(eddyb) this provides the correct generics when
@@ -2651,6 +2661,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
     let mut inline_span = None;
     let mut link_ordinal_span = None;
     let mut no_sanitize_span = None;
+    let mut no_coverage_feature_enabled = false;
+    let mut no_coverage_attr = None;
     for attr in attrs.iter() {
         if tcx.sess.check_name(attr, sym::cold) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
@@ -2714,6 +2726,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
         } else if tcx.sess.check_name(attr, sym::no_mangle) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
+        } else if attr.has_name(sym::feature) {
+            if let Some(list) = attr.meta_item_list() {
+                if list.iter().any(|nested_meta_item| nested_meta_item.has_name(sym::no_coverage)) {
+                    tcx.sess.mark_attr_used(attr);
+                    no_coverage_feature_enabled = true;
+                }
+            }
+        } else if tcx.sess.check_name(attr, sym::no_coverage) {
+            no_coverage_attr = Some(attr);
         } else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
         } else if tcx.sess.check_name(attr, sym::used) {
@@ -2924,6 +2945,23 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
         }
     }
 
+    if let Some(no_coverage_attr) = no_coverage_attr {
+        if tcx.sess.features_untracked().no_coverage || no_coverage_feature_enabled {
+            codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE
+        } else {
+            let mut err = feature_err(
+                &tcx.sess.parse_sess,
+                sym::no_coverage,
+                no_coverage_attr.span,
+                "the `#[no_coverage]` attribute is an experimental feature",
+            );
+            if tcx.sess.parse_sess.unstable_features.is_nightly_build() {
+                err.help("or, alternatively, add `#[feature(no_coverage)]` to the function");
+            }
+            err.emit();
+        }
+    }
+
     codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
         if !attr.has_name(sym::inline) {
             return ia;
index d8eea1ad80b0bcc78a0621de3481c4c091ffaf81..51d5f4ebe2bd2a16141bccaa0ee00fab8b846cbf 100644 (file)
@@ -417,12 +417,14 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
             let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
             match parent_node {
                 Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. })
-                | Node::Ty(&Ty { kind: TyKind::Typeof(ref constant), .. })
                 | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
                     if constant.hir_id == hir_id =>
                 {
                     tcx.types.usize
                 }
+                Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => {
+                    tcx.typeck(def_id).node_type(e.hir_id)
+                }
 
                 Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. })
                     if anon_const.hir_id == hir_id =>
index 837fd3447ea062e2ac84ad485e78d2afc440fd73..532ee00daf8a0a306aa62ab1d79455a2287371b1 100644 (file)
@@ -671,7 +671,7 @@ fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
     /// In the following example the closures `c` only captures `p.x`` even though `incr`
     /// is a capture of the nested closure
     ///
-    /// ```rust,ignore(cannot-test-this-because-pseduo-code)
+    /// ```rust,ignore(cannot-test-this-because-pseudo-code)
     /// let p = ..;
     /// let c = || {
     ///    let incr = 10;
@@ -715,7 +715,7 @@ fn upvar_is_local_variable(
                             // The only places we want to fake read before creating the parent closure are the ones that
                             // are not local to it/ defined by it.
                             //
-                            // ```rust,ignore(cannot-test-this-because-pseduo-code)
+                            // ```rust,ignore(cannot-test-this-because-pseudo-code)
                             // let v1 = (0, 1);
                             // let c = || { // fake reads: v1
                             //    let v2 = (0, 1);
index bf9f7432fb5367ad306b743a6b6f2d7ce96b92d7..a201af0103070afa544b539b16b50ceb5fd90353 100644 (file)
@@ -652,6 +652,43 @@ unsafe fn sift_down_to_bottom(&mut self, mut pos: usize) {
         unsafe { self.sift_up(start, pos) };
     }
 
+    /// Rebuild assuming data[0..start] is still a proper heap.
+    fn rebuild_tail(&mut self, start: usize) {
+        if start == self.len() {
+            return;
+        }
+
+        let tail_len = self.len() - start;
+
+        #[inline(always)]
+        fn log2_fast(x: usize) -> usize {
+            (usize::BITS - x.leading_zeros() - 1) as usize
+        }
+
+        // `rebuild` takes O(self.len()) operations
+        // and about 2 * self.len() comparisons in the worst case
+        // while repeating `sift_up` takes O(tail_len * log(start)) operations
+        // and about 1 * tail_len * log_2(start) comparisons in the worst case,
+        // assuming start >= tail_len. For larger heaps, the crossover point
+        // no longer follows this reasoning and was determined empirically.
+        let better_to_rebuild = if start < tail_len {
+            true
+        } else if self.len() <= 2048 {
+            2 * self.len() < tail_len * log2_fast(start)
+        } else {
+            2 * self.len() < tail_len * 11
+        };
+
+        if better_to_rebuild {
+            self.rebuild();
+        } else {
+            for i in start..self.len() {
+                // SAFETY: The index `i` is always less than self.len().
+                unsafe { self.sift_up(0, i) };
+            }
+        }
+    }
+
     fn rebuild(&mut self) {
         let mut n = self.len() / 2;
         while n > 0 {
@@ -689,37 +726,11 @@ pub fn append(&mut self, other: &mut Self) {
             swap(self, other);
         }
 
-        if other.is_empty() {
-            return;
-        }
-
-        #[inline(always)]
-        fn log2_fast(x: usize) -> usize {
-            (usize::BITS - x.leading_zeros() - 1) as usize
-        }
+        let start = self.data.len();
 
-        // `rebuild` takes O(len1 + len2) operations
-        // and about 2 * (len1 + len2) comparisons in the worst case
-        // while `extend` takes O(len2 * log(len1)) operations
-        // and about 1 * len2 * log_2(len1) comparisons in the worst case,
-        // assuming len1 >= len2. For larger heaps, the crossover point
-        // no longer follows this reasoning and was determined empirically.
-        #[inline]
-        fn better_to_rebuild(len1: usize, len2: usize) -> bool {
-            let tot_len = len1 + len2;
-            if tot_len <= 2048 {
-                2 * tot_len < len2 * log2_fast(len1)
-            } else {
-                2 * tot_len < len2 * 11
-            }
-        }
+        self.data.append(&mut other.data);
 
-        if better_to_rebuild(self.len(), other.len()) {
-            self.data.append(&mut other.data);
-            self.rebuild();
-        } else {
-            self.extend(other.drain());
-        }
+        self.rebuild_tail(start);
     }
 
     /// Returns an iterator which retrieves elements in heap order.
@@ -770,12 +781,22 @@ pub fn drain_sorted(&mut self) -> DrainSorted<'_, T> {
     /// assert_eq!(heap.into_sorted_vec(), [-10, 2, 4])
     /// ```
     #[unstable(feature = "binary_heap_retain", issue = "71503")]
-    pub fn retain<F>(&mut self, f: F)
+    pub fn retain<F>(&mut self, mut f: F)
     where
         F: FnMut(&T) -> bool,
     {
-        self.data.retain(f);
-        self.rebuild();
+        let mut first_removed = self.len();
+        let mut i = 0;
+        self.data.retain(|e| {
+            let keep = f(e);
+            if !keep && i < first_removed {
+                first_removed = i;
+            }
+            i += 1;
+            keep
+        });
+        // data[0..first_removed] is untouched, so we only need to rebuild the tail:
+        self.rebuild_tail(first_removed);
     }
 }
 
index d3e70991ad5187bcee4500d008fe65bd3d56386a..7d6fbf1c438bfe19e5632657b0a5b0fedc14805d 100644 (file)
@@ -2403,6 +2403,12 @@ unsafe fn rotate_right_inner(&mut self, k: usize) {
     /// [`Result::Err`] is returned, containing the index where a matching
     /// element could be inserted while maintaining sorted order.
     ///
+    /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`].
+    ///
+    /// [`binary_search_by`]: VecDeque::binary_search_by
+    /// [`binary_search_by_key`]: VecDeque::binary_search_by_key
+    /// [`partition_point`]: VecDeque::partition_point
+    ///
     /// # Examples
     ///
     /// Looks up a series of four elements. The first is found, with a
@@ -2457,6 +2463,12 @@ pub fn binary_search(&self, x: &T) -> Result<usize, usize>
     /// [`Result::Err`] is returned, containing the index where a matching
     /// element could be inserted while maintaining sorted order.
     ///
+    /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`].
+    ///
+    /// [`binary_search`]: VecDeque::binary_search
+    /// [`binary_search_by_key`]: VecDeque::binary_search_by_key
+    /// [`partition_point`]: VecDeque::partition_point
+    ///
     /// # Examples
     ///
     /// Looks up a series of four elements. The first is found, with a
@@ -2481,8 +2493,11 @@ pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
         F: FnMut(&'a T) -> Ordering,
     {
         let (front, back) = self.as_slices();
+        let cmp_back = back.first().map(|elem| f(elem));
 
-        if let Some(Ordering::Less | Ordering::Equal) = back.first().map(|elem| f(elem)) {
+        if let Some(Ordering::Equal) = cmp_back {
+            Ok(front.len())
+        } else if let Some(Ordering::Less) = cmp_back {
             back.binary_search_by(f).map(|idx| idx + front.len()).map_err(|idx| idx + front.len())
         } else {
             front.binary_search_by(f)
@@ -2492,8 +2507,7 @@ pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
     /// Binary searches this sorted `VecDeque` with a key extraction function.
     ///
     /// Assumes that the `VecDeque` is sorted by the key, for instance with
-    /// [`make_contiguous().sort_by_key()`](#method.make_contiguous) using the same
-    /// key extraction function.
+    /// [`make_contiguous().sort_by_key()`] using the same key extraction function.
     ///
     /// If the value is found then [`Result::Ok`] is returned, containing the
     /// index of the matching element. If there are multiple matches, then any
@@ -2501,6 +2515,13 @@ pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
     /// [`Result::Err`] is returned, containing the index where a matching
     /// element could be inserted while maintaining sorted order.
     ///
+    /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`].
+    ///
+    /// [`make_contiguous().sort_by_key()`]: VecDeque::make_contiguous
+    /// [`binary_search`]: VecDeque::binary_search
+    /// [`binary_search_by`]: VecDeque::binary_search_by
+    /// [`partition_point`]: VecDeque::partition_point
+    ///
     /// # Examples
     ///
     /// Looks up a series of four elements in a slice of pairs sorted by
@@ -2531,6 +2552,51 @@ pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize
     {
         self.binary_search_by(|k| f(k).cmp(b))
     }
+
+    /// Returns the index of the partition point according to the given predicate
+    /// (the index of the first element of the second partition).
+    ///
+    /// The deque is assumed to be partitioned according to the given predicate.
+    /// This means that all elements for which the predicate returns true are at the start of the deque
+    /// and all elements for which the predicate returns false are at the end.
+    /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0
+    /// (all odd numbers are at the start, all even at the end).
+    ///
+    /// If this deque is not partitioned, the returned result is unspecified and meaningless,
+    /// as this method performs a kind of binary search.
+    ///
+    /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`].
+    ///
+    /// [`binary_search`]: VecDeque::binary_search
+    /// [`binary_search_by`]: VecDeque::binary_search_by
+    /// [`binary_search_by_key`]: VecDeque::binary_search_by_key
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(vecdeque_binary_search)]
+    /// use std::collections::VecDeque;
+    ///
+    /// let deque: VecDeque<_> = vec![1, 2, 3, 3, 5, 6, 7].into();
+    /// let i = deque.partition_point(|&x| x < 5);
+    ///
+    /// assert_eq!(i, 4);
+    /// assert!(deque.iter().take(i).all(|&x| x < 5));
+    /// assert!(deque.iter().skip(i).all(|&x| !(x < 5)));
+    /// ```
+    #[unstable(feature = "vecdeque_binary_search", issue = "78021")]
+    pub fn partition_point<P>(&self, mut pred: P) -> usize
+    where
+        P: FnMut(&T) -> bool,
+    {
+        let (front, back) = self.as_slices();
+
+        if let Some(true) = back.first().map(|v| pred(v)) {
+            back.partition_point(pred) + front.len()
+        } else {
+            front.partition_point(pred)
+        }
+    }
 }
 
 impl<T: Clone> VecDeque<T> {
index 14cb1d3b405c2df1f05b6ad71eb8b603de82e2ad..15308a4469bf059dda2f06ad3e23d89f25800b68 100644 (file)
@@ -76,7 +76,6 @@
 #![cfg_attr(test, feature(test))]
 #![cfg_attr(test, feature(new_uninit))]
 #![feature(allocator_api)]
-#![feature(vec_extend_from_within)]
 #![feature(array_chunks)]
 #![feature(array_methods)]
 #![feature(array_windows)]
@@ -89,7 +88,8 @@
 #![feature(cfg_target_has_atomic)]
 #![feature(coerce_unsized)]
 #![feature(const_btree_new)]
-#![feature(const_fn)]
+#![cfg_attr(bootstrap, feature(const_fn))]
+#![cfg_attr(not(bootstrap), feature(const_fn_trait_bound))]
 #![feature(cow_is_borrowed)]
 #![feature(const_cow_is_borrowed)]
 #![feature(destructuring_assignment)]
index dc02c9c883ea0ae1cad39ef99a5db3e5bb3e852c..fe87a97bac1284bc690c9879fcd0474200cbf70d 100644 (file)
@@ -53,17 +53,11 @@ pub struct RawVec<T, A: Allocator = Global> {
 }
 
 impl<T> RawVec<T, Global> {
-    /// HACK(Centril): This exists because `#[unstable]` `const fn`s needn't conform
-    /// to `min_const_fn` and so they cannot be called in `min_const_fn`s either.
+    /// HACK(Centril): This exists because stable `const fn` can only call stable `const fn`, so
+    /// they cannot call `Self::new()`.
     ///
-    /// If you change `RawVec<T>::new` or dependencies, please take care to not
-    /// introduce anything that would truly violate `min_const_fn`.
-    ///
-    /// NOTE: We could avoid this hack and check conformance with some
-    /// `#[rustc_force_min_const_fn]` attribute which requires conformance
-    /// with `min_const_fn` but does not necessarily allow calling it in
-    /// `stable(...) const fn` / user code not enabling `foo` when
-    /// `#[rustc_const_unstable(feature = "foo", issue = "01234")]` is present.
+    /// If you change `RawVec<T>::new` or dependencies, please take care to not introduce anything
+    /// that would truly const-call something unstable.
     pub const NEW: Self = Self::new();
 
     /// Creates the biggest possible `RawVec` (on the system heap)
index c81ababf1519d290a2512a881df3d91c31595e53..cb4af7c5cd15172fea0772967482c7256f59835d 100644 (file)
@@ -2330,8 +2330,8 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 #[stable(feature = "downgraded_weak", since = "1.10.0")]
 impl<T> Default for Weak<T> {
-    /// Constructs a new `Weak<T>`, allocating memory for `T` without initializing
-    /// it. Calling [`upgrade`] on the return value always gives [`None`].
+    /// Constructs a new `Weak<T>`, without allocating any memory.
+    /// Calling [`upgrade`] on the return value always gives [`None`].
     ///
     /// [`None`]: Option
     /// [`upgrade`]: Weak::upgrade
index 036b84bb1d55f21c2423c18f2ede6a4890d683ae..f5d0e911b601b1f9ac0091f021d8558fbf6a8509 100644 (file)
@@ -222,7 +222,6 @@ fn to_vec<A: Allocator>(s: &[Self], alloc: A) -> Vec<Self, A> {
 }
 
 #[lang = "slice_alloc"]
-#[cfg_attr(not(test), rustc_diagnostic_item = "slice")]
 #[cfg(not(test))]
 impl<T> [T] {
     /// Sorts the slice.
index 58a9ae77244fb803a56794fb0cc6a9fc35152822..528ee4ff1542cf5a54cda4d4011847f4244e634c 100644 (file)
@@ -87,6 +87,9 @@ fn wake_by_ref(self: &Arc<Self>) {
 
 #[stable(feature = "wake_trait", since = "1.51.0")]
 impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for Waker {
+    /// Use a `Wake`-able type as a `Waker`.
+    ///
+    /// No heap allocations or atomic operations are used for this conversion.
     fn from(waker: Arc<W>) -> Waker {
         // SAFETY: This is safe because raw_waker safely constructs
         // a RawWaker from Arc<W>.
@@ -96,6 +99,9 @@ fn from(waker: Arc<W>) -> Waker {
 
 #[stable(feature = "wake_trait", since = "1.51.0")]
 impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for RawWaker {
+    /// Use a `Wake`-able type as a `RawWaker`.
+    ///
+    /// No heap allocations or atomic operations are used for this conversion.
     fn from(waker: Arc<W>) -> RawWaker {
         raw_waker(waker)
     }
index bddaab0c761887a80e99eca5a2bb4e6f94a6acd7..b4741c35c583fa3157e44ef5175c00397329cf39 100644 (file)
@@ -49,17 +49,17 @@ fn test_show() {
     let b = Box::new(Test) as Box<dyn Any>;
     let a_str = format!("{:?}", a);
     let b_str = format!("{:?}", b);
-    assert_eq!(a_str, "Any");
-    assert_eq!(b_str, "Any");
+    assert_eq!(a_str, "Any { .. }");
+    assert_eq!(b_str, "Any { .. }");
 
     static EIGHT: usize = 8;
     static TEST: Test = Test;
     let a = &EIGHT as &dyn Any;
     let b = &TEST as &dyn Any;
     let s = format!("{:?}", a);
-    assert_eq!(s, "Any");
+    assert_eq!(s, "Any { .. }");
     let s = format!("{:?}", b);
-    assert_eq!(s, "Any");
+    assert_eq!(s, "Any { .. }");
 }
 
 #[test]
index 0dab0358d6e3d7dfef36b43a9ac1f3bf434f3ac7..85c9446689e6719e4430f1e09c31d86733b7b5d7 100644 (file)
@@ -2041,7 +2041,7 @@ pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]) {
 
     /// Safety: changing returned .2 (&mut usize) is considered the same as calling `.set_len(_)`.
     ///
-    /// This method is used to have unique access to all vec parts at once in `extend_from_within`.
+    /// This method provides unique access to all vec parts at once in `extend_from_within`.
     unsafe fn split_at_spare_mut_with_len(
         &mut self,
     ) -> (&mut [T], &mut [MaybeUninit<T>], &mut usize) {
@@ -2124,8 +2124,6 @@ pub fn extend_from_slice(&mut self, other: &[T]) {
     /// ## Examples
     ///
     /// ```
-    /// #![feature(vec_extend_from_within)]
-    ///
     /// let mut vec = vec![0, 1, 2, 3, 4];
     ///
     /// vec.extend_from_within(2..);
@@ -2137,7 +2135,7 @@ pub fn extend_from_slice(&mut self, other: &[T]) {
     /// vec.extend_from_within(4..8);
     /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]);
     /// ```
-    #[unstable(feature = "vec_extend_from_within", issue = "81656")]
+    #[stable(feature = "vec_extend_from_within", since = "1.53.0")]
     pub fn extend_from_within<R>(&mut self, src: R)
     where
         R: RangeBounds<usize>,
@@ -2279,7 +2277,7 @@ impl<T: Clone, A: Allocator> ExtendFromWithinSpec for Vec<T, A> {
         iter::zip(to_clone, spare)
             .map(|(src, dst)| dst.write(src.clone()))
             // Note:
-            // - Element was just initialized with `MaybeUninit::write`, so it's ok to increace len
+            // - Element was just initialized with `MaybeUninit::write`, so it's ok to increase len
             // - len is increased after each element to prevent leaks (see issue #82533)
             .for_each(|_| *len += 1);
     }
@@ -2810,8 +2808,7 @@ impl<T, A: Allocator> From<Box<[T], A>> for Vec<T, A> {
     /// assert_eq!(Vec::from(b), vec![1, 2, 3]);
     /// ```
     fn from(s: Box<[T], A>) -> Self {
-        let len = s.len();
-        Self { buf: RawVec::from_box(s), len }
+        s.into_vec()
     }
 }
 
index ce794a9a4afa2dd0c89b167efc33a3ff76edfdd1..a7913dcd28740bb88f343909828f47e078b0a71d 100644 (file)
@@ -386,10 +386,23 @@ fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> {
 
 #[test]
 fn test_retain() {
-    let mut a = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]);
-    a.retain(|x| x % 2 == 0);
+    let mut a = BinaryHeap::from(vec![100, 10, 50, 1, 2, 20, 30]);
+    a.retain(|&x| x != 2);
 
-    assert_eq!(a.into_sorted_vec(), [-10, 2, 4])
+    // Check that 20 moved into 10's place.
+    assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
+
+    a.retain(|_| true);
+
+    assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
+
+    a.retain(|&x| x < 50);
+
+    assert_eq!(a.clone().into_vec(), [30, 20, 10, 1]);
+
+    a.retain(|_| false);
+
+    assert!(a.is_empty());
 }
 
 // old binaryheap failed this test
index 7e1194cc4aa20e3e66efa7d6d09f29155207b924..25a83a0b01438ba52fcb077f87a757d81d4754a7 100644 (file)
@@ -20,7 +20,6 @@
 #![feature(vecdeque_binary_search)]
 #![feature(slice_group_by)]
 #![feature(slice_partition_dedup)]
-#![feature(vec_extend_from_within)]
 #![feature(vec_spare_capacity)]
 #![feature(string_remove_matches)]
 
index 0919b1325bceba6b4609589fb41d17c9fba4eac7..d7140cf97593cbd8f751c2fac4e022f18900c697 100644 (file)
@@ -1699,6 +1699,24 @@ fn test_binary_search_by_key() {
     assert_eq!(deque.binary_search_by_key(&4, |&(v,)| v), Err(3));
 }
 
+#[test]
+fn test_partition_point() {
+    // Contiguous (front only) search:
+    let deque: VecDeque<_> = vec![1, 2, 3, 5, 6].into();
+    assert!(deque.as_slices().1.is_empty());
+    assert_eq!(deque.partition_point(|&v| v <= 3), 3);
+
+    // Split search (both front & back non-empty):
+    let mut deque: VecDeque<_> = vec![5, 6].into();
+    deque.push_front(3);
+    deque.push_front(2);
+    deque.push_front(1);
+    deque.push_back(10);
+    assert!(!deque.as_slices().0.is_empty());
+    assert!(!deque.as_slices().1.is_empty());
+    assert_eq!(deque.partition_point(|&v| v <= 5), 4);
+}
+
 #[test]
 fn test_zero_sized_push() {
     const N: usize = 8;
index 710fc18ddcb6c7677b3c96359abb35da37f2a488..221483ebaf45df5c956adffee2d4024e7c3b96b8 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 710fc18ddcb6c7677b3c96359abb35da37f2a488
+Subproject commit 221483ebaf45df5c956adffee2d4024e7c3b96b8
index 2792181acc352ca8a37e713dd52ab8375fb2d06a..9df66263459b155eacffef46c40a9ec271da5dd7 100644 (file)
@@ -112,7 +112,7 @@ fn write_str_macro_debug(bh: &mut Bencher) {
 #[bench]
 fn write_u128_max(bh: &mut Bencher) {
     bh.iter(|| {
-        std::hint::black_box(format!("{}", u128::MAX));
+        test::black_box(format!("{}", u128::MAX));
     });
 }
 
@@ -120,20 +120,20 @@ fn write_u128_max(bh: &mut Bencher) {
 fn write_u128_min(bh: &mut Bencher) {
     bh.iter(|| {
         let s = format!("{}", 0u128);
-        std::hint::black_box(s);
+        test::black_box(s);
     });
 }
 
 #[bench]
 fn write_u64_max(bh: &mut Bencher) {
     bh.iter(|| {
-        std::hint::black_box(format!("{}", u64::MAX));
+        test::black_box(format!("{}", u64::MAX));
     });
 }
 
 #[bench]
 fn write_u64_min(bh: &mut Bencher) {
     bh.iter(|| {
-        std::hint::black_box(format!("{}", 0u64));
+        test::black_box(format!("{}", 0u64));
     });
 }
index 98c34f344251e54f8bbd02eccff48ea409e66b0d..5e1725cfc7a6324b178740ac72a47dd9c6adca60 100644 (file)
@@ -141,7 +141,7 @@ fn type_id(&self) -> TypeId {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Debug for dyn Any {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Any")
+        f.debug_struct("Any").finish_non_exhaustive()
     }
 }
 
@@ -151,14 +151,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Debug for dyn Any + Send {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Any")
+        f.debug_struct("Any").finish_non_exhaustive()
     }
 }
 
 #[stable(feature = "any_send_sync_methods", since = "1.28.0")]
 impl fmt::Debug for dyn Any + Send + Sync {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Any")
+        f.debug_struct("Any").finish_non_exhaustive()
     }
 }
 
index b6ce825e2477a53eed05f3787137e61781d9ebd3..8b56c9560aacf62af42326b0bf9681d35b987346 100644 (file)
 pub use iter::IntoIter;
 
 /// Converts a reference to `T` into a reference to an array of length 1 (without copying).
-#[unstable(feature = "array_from_ref", issue = "77101")]
+#[stable(feature = "array_from_ref", since = "1.53.0")]
 pub fn from_ref<T>(s: &T) -> &[T; 1] {
     // SAFETY: Converting `&T` to `&[T; 1]` is sound.
     unsafe { &*(s as *const T).cast::<[T; 1]>() }
 }
 
 /// Converts a mutable reference to `T` into a mutable reference to an array of length 1 (without copying).
-#[unstable(feature = "array_from_ref", issue = "77101")]
+#[stable(feature = "array_from_ref", since = "1.53.0")]
 pub fn from_mut<T>(s: &mut T) -> &mut [T; 1] {
     // SAFETY: Converting `&mut T` to `&mut [T; 1]` is sound.
     unsafe { &mut *(s as *mut T).cast::<[T; 1]>() }
@@ -155,6 +155,28 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
+// Note: the `#[rustc_skip_array_during_method_dispatch]` on `trait IntoIterator`
+// hides this implementation from explicit `.into_iter()` calls on editions < 2021,
+// so those calls will still resolve to the slice implementation, by reference.
+#[cfg(not(bootstrap))]
+#[stable(feature = "array_into_iter_impl", since = "1.53.0")]
+impl<T, const N: usize> IntoIterator for [T; N] {
+    type Item = T;
+    type IntoIter = IntoIter<T, N>;
+
+    /// Creates a consuming iterator, that is, one that moves each value out of
+    /// the array (from start to end). The array cannot be used after calling
+    /// this unless `T` implements `Copy`, so the whole array is copied.
+    ///
+    /// Arrays have special behavior when calling `.into_iter()` prior to the
+    /// 2021 edition -- see the [array] Editions section for more information.
+    ///
+    /// [array]: prim@array
+    fn into_iter(self) -> Self::IntoIter {
+        IntoIter::new(self)
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, T, const N: usize> IntoIterator for &'a [T; N] {
     type Item = &'a T;
index a8a25f927163cd33a63ef4c1ba9194570febc65c..4780d8dc7883ff0b17ddf2bb32faf32c3aaf230b 100644 (file)
@@ -145,6 +145,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for EscapeDefault {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("EscapeDefault { .. }")
+        f.debug_struct("EscapeDefault").finish_non_exhaustive()
     }
 }
index 67dd1d83415bda73e6cd05dd223da69649153889..0a3e5789e8bedc4dc7def36dfe27763d5d3d6ce2 100644 (file)
@@ -274,6 +274,8 @@ pub trait Eq: PartialEq<Self> {
     //
     // This should never be implemented by hand.
     #[doc(hidden)]
+    #[cfg_attr(not(bootstrap), feature(no_coverage))]
+    #[cfg_attr(not(bootstrap), no_coverage)]
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn assert_receiver_is_total_eq(&self) {}
@@ -334,7 +336,6 @@ impl Ordering {
     /// # Examples
     ///
     /// ```
-    /// #![feature(ordering_helpers)]
     /// use std::cmp::Ordering;
     ///
     /// assert_eq!(Ordering::Less.is_eq(), false);
@@ -343,7 +344,8 @@ impl Ordering {
     /// ```
     #[inline]
     #[must_use]
-    #[unstable(feature = "ordering_helpers", issue = "79885")]
+    #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+    #[stable(feature = "ordering_helpers", since = "1.53.0")]
     pub const fn is_eq(self) -> bool {
         matches!(self, Equal)
     }
@@ -353,7 +355,6 @@ pub const fn is_eq(self) -> bool {
     /// # Examples
     ///
     /// ```
-    /// #![feature(ordering_helpers)]
     /// use std::cmp::Ordering;
     ///
     /// assert_eq!(Ordering::Less.is_ne(), true);
@@ -362,7 +363,8 @@ pub const fn is_eq(self) -> bool {
     /// ```
     #[inline]
     #[must_use]
-    #[unstable(feature = "ordering_helpers", issue = "79885")]
+    #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+    #[stable(feature = "ordering_helpers", since = "1.53.0")]
     pub const fn is_ne(self) -> bool {
         !matches!(self, Equal)
     }
@@ -372,7 +374,6 @@ pub const fn is_ne(self) -> bool {
     /// # Examples
     ///
     /// ```
-    /// #![feature(ordering_helpers)]
     /// use std::cmp::Ordering;
     ///
     /// assert_eq!(Ordering::Less.is_lt(), true);
@@ -381,7 +382,8 @@ pub const fn is_ne(self) -> bool {
     /// ```
     #[inline]
     #[must_use]
-    #[unstable(feature = "ordering_helpers", issue = "79885")]
+    #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+    #[stable(feature = "ordering_helpers", since = "1.53.0")]
     pub const fn is_lt(self) -> bool {
         matches!(self, Less)
     }
@@ -391,7 +393,6 @@ pub const fn is_lt(self) -> bool {
     /// # Examples
     ///
     /// ```
-    /// #![feature(ordering_helpers)]
     /// use std::cmp::Ordering;
     ///
     /// assert_eq!(Ordering::Less.is_gt(), false);
@@ -400,7 +401,8 @@ pub const fn is_lt(self) -> bool {
     /// ```
     #[inline]
     #[must_use]
-    #[unstable(feature = "ordering_helpers", issue = "79885")]
+    #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+    #[stable(feature = "ordering_helpers", since = "1.53.0")]
     pub const fn is_gt(self) -> bool {
         matches!(self, Greater)
     }
@@ -410,7 +412,6 @@ pub const fn is_gt(self) -> bool {
     /// # Examples
     ///
     /// ```
-    /// #![feature(ordering_helpers)]
     /// use std::cmp::Ordering;
     ///
     /// assert_eq!(Ordering::Less.is_le(), true);
@@ -419,7 +420,8 @@ pub const fn is_gt(self) -> bool {
     /// ```
     #[inline]
     #[must_use]
-    #[unstable(feature = "ordering_helpers", issue = "79885")]
+    #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+    #[stable(feature = "ordering_helpers", since = "1.53.0")]
     pub const fn is_le(self) -> bool {
         !matches!(self, Greater)
     }
@@ -429,7 +431,6 @@ pub const fn is_le(self) -> bool {
     /// # Examples
     ///
     /// ```
-    /// #![feature(ordering_helpers)]
     /// use std::cmp::Ordering;
     ///
     /// assert_eq!(Ordering::Less.is_ge(), false);
@@ -438,7 +439,8 @@ pub const fn is_le(self) -> bool {
     /// ```
     #[inline]
     #[must_use]
-    #[unstable(feature = "ordering_helpers", issue = "79885")]
+    #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")]
+    #[stable(feature = "ordering_helpers", since = "1.53.0")]
     pub const fn is_ge(self) -> bool {
         !matches!(self, Less)
     }
index 5b113610a5d3fd5a2cfdde42b53d16efeb6aa54f..a522b7da3bd1c64df67e67a9e29557d5e06c85c5 100644 (file)
@@ -45,8 +45,10 @@ unsafe fn to_int_unchecked(self) -> $Int {
 macro_rules! impl_from {
     ($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => {
         #[$attr]
-        #[doc = $doc]
         impl From<$Small> for $Large {
+            // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
+            // Rustdocs on functions do not.
+            #[doc = $doc]
             #[inline]
             fn from(small: $Small) -> Self {
                 small as Self
@@ -383,8 +385,10 @@ mod ptr_try_from_impls {
 macro_rules! nzint_impl_from {
     ($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => {
         #[$attr]
-        #[doc = $doc]
         impl From<$Small> for $Large {
+            // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
+            // Rustdocs on functions do not.
+            #[doc = $doc]
             #[inline]
             fn from(small: $Small) -> Self {
                 // SAFETY: input type guarantees the value is non-zero
@@ -450,10 +454,12 @@ fn from(small: $Small) -> Self {
 macro_rules! nzint_impl_try_from_int {
     ($Int: ty, $NonZeroInt: ty, #[$attr:meta], $doc: expr) => {
         #[$attr]
-        #[doc = $doc]
         impl TryFrom<$Int> for $NonZeroInt {
             type Error = TryFromIntError;
 
+            // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
+            // Rustdocs on functions do not.
+            #[doc = $doc]
             #[inline]
             fn try_from(value: $Int) -> Result<Self, Self::Error> {
                 Self::new(value).ok_or(TryFromIntError(()))
@@ -489,10 +495,12 @@ fn try_from(value: $Int) -> Result<Self, Self::Error> {
 macro_rules! nzint_impl_try_from_nzint {
     ($From:ty => $To:ty, $doc: expr) => {
         #[stable(feature = "nzint_try_from_nzint_conv", since = "1.49.0")]
-        #[doc = $doc]
         impl TryFrom<$From> for $To {
             type Error = TryFromIntError;
 
+            // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
+            // Rustdocs on functions do not.
+            #[doc = $doc]
             #[inline]
             fn try_from(value: $From) -> Result<Self, Self::Error> {
                 TryFrom::try_from(value.get()).map(|v| {
index 9302baa823bc5f29cde200cfccbcb2ead7e64099..b208ddd4b272fbd0f1c5bbda2c86a9ce310bbf4a 100644 (file)
@@ -53,7 +53,7 @@ pub enum c_void {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for c_void {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("c_void")
+        f.debug_struct("c_void").finish()
     }
 }
 
index 475ebcf07d5edbadb275f0a271a2f985eb8c9dbf..b660788c0515fb5b6d1d36f63b3da858b555788b 100644 (file)
@@ -188,28 +188,19 @@ pub fn field(&mut self, name: &str, value: &dyn fmt::Debug) -> &mut Self {
     #[stable(feature = "debug_non_exhaustive", since = "1.53.0")]
     pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
         self.result = self.result.and_then(|_| {
-            // Draw non-exhaustive dots (`..`), and open brace if necessary (no fields).
-            if self.is_pretty() {
-                if !self.has_fields {
-                    self.fmt.write_str(" {\n")?;
-                }
-                let mut slot = None;
-                let mut state = Default::default();
-                let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state);
-                writer.write_str("..\n")?;
-            } else {
-                if self.has_fields {
-                    self.fmt.write_str(", ..")?;
+            if self.has_fields {
+                if self.is_pretty() {
+                    let mut slot = None;
+                    let mut state = Default::default();
+                    let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state);
+                    writer.write_str("..\n")?;
+                    self.fmt.write_str("}")
                 } else {
-                    self.fmt.write_str(" { ..")?;
+                    self.fmt.write_str(", .. }")
                 }
-            }
-            if self.is_pretty() {
-                self.fmt.write_str("}")?
             } else {
-                self.fmt.write_str(" }")?;
+                self.fmt.write_str(" { .. }")
             }
-            Ok(())
         });
         self.result
     }
index 59493bb0425f3534d1c5e1902637b1dbce6d6d32..87042d95fbef06ac0720bd7ad48985e682e71b2a 100644 (file)
@@ -2220,7 +2220,7 @@ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized> Debug for PhantomData<T> {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
-        f.pad("PhantomData")
+        f.debug_struct("PhantomData").finish()
     }
 }
 
@@ -2270,7 +2270,7 @@ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
 #[stable(feature = "core_impl_debug", since = "1.9.0")]
 impl<T: ?Sized> Debug for UnsafeCell<T> {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
-        f.pad("UnsafeCell")
+        f.debug_struct("UnsafeCell").finish_non_exhaustive()
     }
 }
 
index 0c3303cc2109402c1cfe115a7583bdd390f531fd..77d3a35b268226696a3fb42ad4289b85118308c8 100644 (file)
@@ -175,6 +175,21 @@ pub trait Hash {
 
     /// Feeds a slice of this type into the given [`Hasher`].
     ///
+    /// This method is meant as a convenience, but its implementation is
+    /// also explicitly left unspecified. It isn't guaranteed to be
+    /// equivalent to repeated calls of [`hash`] and implementations of
+    /// [`Hash`] should keep that in mind and call [`hash`] themselves
+    /// if the slice isn't treated as a whole unit in the [`PartialEq`]
+    /// implementation.
+    ///
+    /// For example, a [`VecDeque`] implementation might naïvely call
+    /// [`as_slices`] and then [`hash_slice`] on each slice, but this
+    /// is wrong since the two slices can change with a call to
+    /// [`make_contiguous`] without affecting the [`PartialEq`]
+    /// result. Since these slices aren't treated as singular
+    /// units, and instead part of a larger deque, this method cannot
+    /// be used.
+    ///
     /// # Examples
     ///
     /// ```
@@ -186,6 +201,12 @@ pub trait Hash {
     /// Hash::hash_slice(&numbers, &mut hasher);
     /// println!("Hash is {:x}!", hasher.finish());
     /// ```
+    ///
+    /// [`VecDeque`]: ../../std/collections/struct.VecDeque.html
+    /// [`as_slices`]: ../../std/collections/struct.VecDeque.html#method.as_slices
+    /// [`make_contiguous`]: ../../std/collections/struct.VecDeque.html#method.make_contiguous
+    /// [`hash`]: Hash::hash
+    /// [`hash_slice`]: Hash::hash_slice
     #[stable(feature = "hash_slice", since = "1.3.0")]
     fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
     where
@@ -221,6 +242,11 @@ pub(crate) mod macros {
 /// instance (with [`write`] and [`write_u8`] etc.). Most of the time, `Hasher`
 /// instances are used in conjunction with the [`Hash`] trait.
 ///
+/// This trait makes no assumptions about how the various `write_*` methods are
+/// defined and implementations of [`Hash`] should not assume that they work one
+/// way or another. You cannot assume, for example, that a [`write_u32`] call is
+/// equivalent to four calls of [`write_u8`].
+///
 /// # Examples
 ///
 /// ```
@@ -240,6 +266,7 @@ pub(crate) mod macros {
 /// [`finish`]: Hasher::finish
 /// [`write`]: Hasher::write
 /// [`write_u8`]: Hasher::write_u8
+/// [`write_u32`]: Hasher::write_u32
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Hasher {
     /// Returns the hash value for the values written so far.
@@ -507,7 +534,7 @@ pub trait BuildHasher {
 #[stable(since = "1.9.0", feature = "core_impl_debug")]
 impl<H> fmt::Debug for BuildHasherDefault<H> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("BuildHasherDefault")
+        f.debug_struct("BuildHasherDefault").finish()
     }
 }
 
index 313729581acd945b790c2f69aa03512c825766a1..f7aec73644921ab14cc7a158b9bc9e2a9c9e9b71 100644 (file)
@@ -154,7 +154,7 @@ pub fn spin_loop() {
 /// [`std::convert::identity`]: crate::convert::identity
 #[cfg_attr(not(miri), inline)]
 #[cfg_attr(miri, inline(never))]
-#[unstable(feature = "test", issue = "50297")]
+#[unstable(feature = "bench_black_box", issue = "64102")]
 #[cfg_attr(miri, allow(unused_mut))]
 pub fn black_box<T>(mut dummy: T) -> T {
     // We need to "use" the argument in some way LLVM can't introspect, and on
index 9efc7a480aeb4a4f7001ee4fdcc69a8fad060075..54a47f1323ebf3f17d03e50318410013e58c54c2 100644 (file)
@@ -1,5 +1,8 @@
 use crate::cmp;
-use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen};
+use crate::iter::{
+    adapters::zip::try_get_unchecked, adapters::SourceIter, FusedIterator, InPlaceIterable,
+    TrustedLen, TrustedRandomAccess,
+};
 use crate::ops::{ControlFlow, Try};
 
 /// An iterator that only iterates over the first `n` iterations of `iter`.
@@ -111,6 +114,15 @@ fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
 
         self.try_fold(init, ok(fold)).unwrap()
     }
+
+    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> <I as Iterator>::Item
+    where
+        Self: TrustedRandomAccess,
+    {
+        // SAFETY: the caller must uphold the contract for
+        // `Iterator::__iterator_get_unchecked`.
+        unsafe { try_get_unchecked(&mut self.iter, idx) }
+    }
 }
 
 #[unstable(issue = "none", feature = "inplace_iteration")]
@@ -207,3 +219,12 @@ impl<I> FusedIterator for Take<I> where I: FusedIterator {}
 
 #[unstable(feature = "trusted_len", issue = "37572")]
 unsafe impl<I: TrustedLen> TrustedLen for Take<I> {}
+
+#[doc(hidden)]
+#[unstable(feature = "trusted_random_access", issue = "none")]
+unsafe impl<I> TrustedRandomAccess for Take<I>
+where
+    I: TrustedRandomAccess,
+{
+    const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT;
+}
index 5d4a9fe8c6cc0dde45f7609b323fbe4b40ec5a6c..919c564f2872a8d95020e1e27f6de07052f7935d 100644 (file)
@@ -36,7 +36,7 @@ unsafe impl<T> Sync for Empty<T> {}
 #[stable(feature = "core_impl_debug", since = "1.9.0")]
 impl<T> fmt::Debug for Empty<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Empty")
+        f.debug_struct("Empty").finish()
     }
 }
 
index 1ae6d15c12dd97a4407a1366dc4f34449ff0ac5e..13a2e24cadd10454ec7a0d9f1efbd968695edc43 100644 (file)
@@ -198,6 +198,7 @@ pub trait FromIterator<A>: Sized {
 /// }
 /// ```
 #[rustc_diagnostic_item = "IntoIterator"]
+#[cfg_attr(not(bootstrap), rustc_skip_array_during_method_dispatch)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait IntoIterator {
     /// The type of the elements being iterated over.
index abd44b47f98865ee3e7b70c623357cbaef361c4e..7977d599ae725a0b88c0d8f478fe6bc5218da415 100644 (file)
@@ -1495,7 +1495,12 @@ fn flatten(self) -> Flatten<Self>
     /// [`Some(T)`] again. `fuse()` adapts an iterator, ensuring that after a
     /// [`None`] is given, it will always return [`None`] forever.
     ///
+    /// Note that the [`Fuse`] wrapper is a no-op on iterators that implement
+    /// the [`FusedIterator`] trait. `fuse()` may therefore behave incorrectly
+    /// if the [`FusedIterator`] trait is improperly implemented.
+    ///
     /// [`Some(T)`]: Some
+    /// [`FusedIterator`]: crate::iter::FusedIterator
     ///
     /// # Examples
     ///
@@ -1646,31 +1651,16 @@ fn inspect<F>(self, f: F) -> Inspect<Self, F>
     /// Basic usage:
     ///
     /// ```
-    /// let a = [1, 2, 3];
-    ///
-    /// let iter = a.iter();
+    /// let mut words = vec!["hello", "world", "of", "Rust"].into_iter();
     ///
-    /// let sum: i32 = iter.take(5).fold(0, |acc, i| acc + i);
+    /// // Take the first two words.
+    /// let hello_world: Vec<_> = words.by_ref().take(2).collect();
+    /// assert_eq!(hello_world, vec!["hello", "world"]);
     ///
-    /// assert_eq!(sum, 6);
-    ///
-    /// // if we try to use iter again, it won't work. The following line
-    /// // gives "error: use of moved value: `iter`
-    /// // assert_eq!(iter.next(), None);
-    ///
-    /// // let's try that again
-    /// let a = [1, 2, 3];
-    ///
-    /// let mut iter = a.iter();
-    ///
-    /// // instead, we add in a .by_ref()
-    /// let sum: i32 = iter.by_ref().take(2).fold(0, |acc, i| acc + i);
-    ///
-    /// assert_eq!(sum, 3);
-    ///
-    /// // now this is just fine:
-    /// assert_eq!(iter.next(), Some(&3));
-    /// assert_eq!(iter.next(), None);
+    /// // Collect the rest of the words.
+    /// // We can only do this because we used `by_ref` earlier.
+    /// let of_rust: Vec<_> = words.collect();
+    /// assert_eq!(of_rust, vec!["of", "Rust"]);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     fn by_ref(&mut self) -> &mut Self
index d0c52a445919011f0de0f42eff678082a1a7f38c..0e2c140c367a92ce6a89879fb5ed26fa2d7bd52c 100644 (file)
 #![feature(const_refs_to_cell)]
 #![feature(const_panic)]
 #![feature(const_pin)]
-#![feature(const_fn)]
+#![cfg_attr(bootstrap, feature(const_fn))]
 #![feature(const_fn_union)]
 #![feature(const_impl_trait)]
 #![feature(const_fn_floating_point_arithmetic)]
 #![feature(const_fn_fn_ptr_basics)]
+#![cfg_attr(not(bootstrap), feature(const_fn_trait_bound))]
 #![feature(const_option)]
 #![feature(const_precise_live_drops)]
 #![feature(const_ptr_offset)]
 #![feature(stmt_expr_attributes)]
 #![feature(str_split_as_str)]
 #![feature(str_split_inclusive_as_str)]
+#![feature(char_indices_offset)]
 #![feature(trait_alias)]
 #![feature(transparent_unions)]
 #![feature(try_blocks)]
index 446d72f1d32e4e68399d021b9172ec4f81750f4c..5bf47c3951da22ff93397bf17e8abf315bf089c2 100644 (file)
@@ -886,7 +886,6 @@ pub const fn replace<T>(dest: &mut T, src: T) -> T {
 /// ```
 ///
 /// [`RefCell`]: crate::cell::RefCell
-#[doc(alias = "delete")]
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn drop<T>(_x: T) {}
index 9632e64f180b0743b3f6e93325ea8dd913e62a4f..4b341132e31eca21058cef4eaef116fb647e9fec 100644 (file)
@@ -79,7 +79,7 @@ pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[doc(alias = "popcount")]
         #[doc(alias = "popcnt")]
-        #[inline]
+        #[inline(always)]
         pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() }
 
         /// Returns the number of zeros in the binary representation of `self`.
@@ -93,7 +93,7 @@ pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() }
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn count_zeros(self) -> u32 {
             (!self).count_ones()
         }
@@ -111,7 +111,7 @@ pub const fn count_zeros(self) -> u32 {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn leading_zeros(self) -> u32 {
             (self as $UnsignedT).leading_zeros()
         }
@@ -129,7 +129,7 @@ pub const fn leading_zeros(self) -> u32 {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn trailing_zeros(self) -> u32 {
             (self as $UnsignedT).trailing_zeros()
         }
@@ -147,7 +147,7 @@ pub const fn trailing_zeros(self) -> u32 {
         /// ```
         #[stable(feature = "leading_trailing_ones", since = "1.46.0")]
         #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn leading_ones(self) -> u32 {
             (self as $UnsignedT).leading_ones()
         }
@@ -165,7 +165,7 @@ pub const fn leading_ones(self) -> u32 {
         /// ```
         #[stable(feature = "leading_trailing_ones", since = "1.46.0")]
         #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn trailing_ones(self) -> u32 {
             (self as $UnsignedT).trailing_ones()
         }
@@ -189,7 +189,7 @@ pub const fn trailing_ones(self) -> u32 {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn rotate_left(self, n: u32) -> Self {
             (self as $UnsignedT).rotate_left(n) as Self
         }
@@ -214,7 +214,7 @@ pub const fn rotate_left(self, n: u32) -> Self {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn rotate_right(self, n: u32) -> Self {
             (self as $UnsignedT).rotate_right(n) as Self
         }
@@ -234,7 +234,7 @@ pub const fn rotate_right(self, n: u32) -> Self {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn swap_bytes(self) -> Self {
             (self as $UnsignedT).swap_bytes() as Self
         }
@@ -254,8 +254,8 @@ pub const fn swap_bytes(self) -> Self {
         #[doc = concat!("assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits());")]
         /// ```
         #[stable(feature = "reverse_bits", since = "1.37.0")]
-        #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
-        #[inline]
+        #[rustc_const_stable(feature = "const_int_methods", since = "1.37.0")]
+        #[inline(always)]
         #[must_use]
         pub const fn reverse_bits(self) -> Self {
             (self as $UnsignedT).reverse_bits() as Self
@@ -416,7 +416,7 @@ pub const fn checked_add(self, rhs: Self) -> Option<Self> {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub unsafe fn unchecked_add(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_add`.
@@ -454,7 +454,7 @@ pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub unsafe fn unchecked_sub(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_sub`.
@@ -492,7 +492,7 @@ pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub unsafe fn unchecked_mul(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_mul`.
@@ -741,7 +741,7 @@ pub const fn checked_pow(self, mut exp: u32) -> Option<Self> {
         #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn saturating_add(self, rhs: Self) -> Self {
             intrinsics::saturating_add(self, rhs)
         }
@@ -762,7 +762,7 @@ pub const fn saturating_add(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn saturating_sub(self, rhs: Self) -> Self {
             intrinsics::saturating_sub(self, rhs)
         }
@@ -783,7 +783,7 @@ pub const fn saturating_sub(self, rhs: Self) -> Self {
 
         #[stable(feature = "saturating_neg", since = "1.45.0")]
         #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn saturating_neg(self) -> Self {
             intrinsics::saturating_sub(0, self)
         }
@@ -883,7 +883,7 @@ pub const fn saturating_pow(self, exp: u32) -> Self {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_add(self, rhs: Self) -> Self {
             intrinsics::wrapping_add(self, rhs)
         }
@@ -903,7 +903,7 @@ pub const fn wrapping_add(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_sub(self, rhs: Self) -> Self {
             intrinsics::wrapping_sub(self, rhs)
         }
@@ -923,7 +923,7 @@ pub const fn wrapping_sub(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_mul(self, rhs: Self) -> Self {
             intrinsics::wrapping_mul(self, rhs)
         }
@@ -1081,7 +1081,7 @@ pub const fn wrapping_neg(self) -> Self {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_shl(self, rhs: u32) -> Self {
             // SAFETY: the masking by the bitsize of the type ensures that we do not shift
             // out of bounds
@@ -1110,7 +1110,7 @@ pub const fn wrapping_shl(self, rhs: u32) -> Self {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_shr(self, rhs: u32) -> Self {
             // SAFETY: the masking by the bitsize of the type ensures that we do not shift
             // out of bounds
@@ -1225,7 +1225,7 @@ pub const fn wrapping_pow(self, mut exp: u32) -> Self {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
             let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT);
             (a as Self, b)
@@ -1249,7 +1249,7 @@ pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
             let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT);
             (a as Self, b)
@@ -1272,7 +1272,7 @@ pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
             let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT);
             (a as Self, b)
@@ -1725,7 +1725,7 @@ pub const fn abs(self) -> Self {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn signum(self) -> Self {
             match self {
                 n if n > 0 =>  1,
@@ -1747,7 +1747,7 @@ pub const fn signum(self) -> Self {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn is_positive(self) -> bool { self > 0 }
 
         /// Returns `true` if `self` is negative and `false` if the number is zero or
@@ -1763,7 +1763,7 @@ pub const fn is_positive(self) -> bool { self > 0 }
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn is_negative(self) -> bool { self < 0 }
 
         /// Return the memory representation of this integer as a byte array in
index 81262a2f91839a65208ee73e05c584fbcdfd8313..6b9b435d47fe9994d5998b228983d050a17a0b96 100644 (file)
@@ -23,7 +23,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 }
 
 macro_rules! nonzero_integers {
-    ( $( #[$stability: meta] $Ty: ident($Int: ty); )+ ) => {
+    ( $( #[$stability: meta] #[$const_new_unchecked_stability: meta] $Ty: ident($Int: ty); )+ ) => {
         $(
             /// An integer that is known not to equal zero.
             ///
@@ -48,7 +48,7 @@ impl $Ty {
                 ///
                 /// The value must not be zero.
                 #[$stability]
-                #[rustc_const_stable(feature = "nonzero", since = "1.34.0")]
+                #[$const_new_unchecked_stability]
                 #[inline]
                 pub const unsafe fn new_unchecked(n: $Int) -> Self {
                     // SAFETY: this is guaranteed to be safe by the caller.
@@ -146,18 +146,18 @@ fn bitor_assign(&mut self, rhs: $Int) {
 }
 
 nonzero_integers! {
-    #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8);
-    #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16);
-    #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32);
-    #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64);
-    #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128);
-    #[stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize);
-    #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8);
-    #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16);
-    #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32);
-    #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64);
-    #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128);
-    #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize);
+    #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8);
+    #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16);
+    #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32);
+    #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64);
+    #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128);
+    #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize);
+    #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8);
+    #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16);
+    #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32);
+    #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64);
+    #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128);
+    #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize);
 }
 
 macro_rules! from_str_radix_nzint_impl {
index 62d539b96c301ef90c937b198dc1bedf4130e0c1..08d9161eff112851bccfbc2e281d4b8e13677fbf 100644 (file)
@@ -79,7 +79,7 @@ pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
         #[doc(alias = "popcount")]
         #[doc(alias = "popcnt")]
-        #[inline]
+        #[inline(always)]
         pub const fn count_ones(self) -> u32 {
             intrinsics::ctpop(self as $ActualT) as u32
         }
@@ -95,7 +95,7 @@ pub const fn count_ones(self) -> u32 {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn count_zeros(self) -> u32 {
             (!self).count_ones()
         }
@@ -113,7 +113,7 @@ pub const fn count_zeros(self) -> u32 {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn leading_zeros(self) -> u32 {
             intrinsics::ctlz(self as $ActualT) as u32
         }
@@ -132,7 +132,7 @@ pub const fn leading_zeros(self) -> u32 {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn trailing_zeros(self) -> u32 {
             intrinsics::cttz(self) as u32
         }
@@ -150,7 +150,7 @@ pub const fn trailing_zeros(self) -> u32 {
         /// ```
         #[stable(feature = "leading_trailing_ones", since = "1.46.0")]
         #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn leading_ones(self) -> u32 {
             (!self).leading_zeros()
         }
@@ -169,7 +169,7 @@ pub const fn leading_ones(self) -> u32 {
         /// ```
         #[stable(feature = "leading_trailing_ones", since = "1.46.0")]
         #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn trailing_ones(self) -> u32 {
             (!self).trailing_zeros()
         }
@@ -193,7 +193,7 @@ pub const fn trailing_ones(self) -> u32 {
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn rotate_left(self, n: u32) -> Self {
             intrinsics::rotate_left(self, n as $SelfT)
         }
@@ -218,7 +218,7 @@ pub const fn rotate_left(self, n: u32) -> Self {
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn rotate_right(self, n: u32) -> Self {
             intrinsics::rotate_right(self, n as $SelfT)
         }
@@ -237,7 +237,7 @@ pub const fn rotate_right(self, n: u32) -> Self {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn swap_bytes(self) -> Self {
             intrinsics::bswap(self as $ActualT) as Self
         }
@@ -257,8 +257,8 @@ pub const fn swap_bytes(self) -> Self {
         #[doc = concat!("assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits());")]
         /// ```
         #[stable(feature = "reverse_bits", since = "1.37.0")]
-        #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[rustc_const_stable(feature = "const_math", since = "1.37.0")]
+        #[inline(always)]
         #[must_use]
         pub const fn reverse_bits(self) -> Self {
             intrinsics::bitreverse(self as $ActualT) as Self
@@ -284,7 +284,7 @@ pub const fn reverse_bits(self) -> Self {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn from_be(x: Self) -> Self {
             #[cfg(target_endian = "big")]
             {
@@ -316,7 +316,7 @@ pub const fn from_be(x: Self) -> Self {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn from_le(x: Self) -> Self {
             #[cfg(target_endian = "little")]
             {
@@ -348,7 +348,7 @@ pub const fn from_le(x: Self) -> Self {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn to_be(self) -> Self { // or not to be?
             #[cfg(target_endian = "big")]
             {
@@ -380,7 +380,7 @@ pub const fn to_be(self) -> Self { // or not to be?
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_math", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn to_le(self) -> Self {
             #[cfg(target_endian = "little")]
             {
@@ -426,7 +426,7 @@ pub const fn checked_add(self, rhs: Self) -> Option<Self> {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub unsafe fn unchecked_add(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_add`.
@@ -464,7 +464,7 @@ pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub unsafe fn unchecked_sub(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_sub`.
@@ -502,7 +502,7 @@ pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub unsafe fn unchecked_mul(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_mul`.
@@ -727,7 +727,7 @@ pub const fn checked_pow(self, mut exp: u32) -> Option<Self> {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn saturating_add(self, rhs: Self) -> Self {
             intrinsics::saturating_add(self, rhs)
         }
@@ -747,7 +747,7 @@ pub const fn saturating_add(self, rhs: Self) -> Self {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn saturating_sub(self, rhs: Self) -> Self {
             intrinsics::saturating_sub(self, rhs)
         }
@@ -813,7 +813,7 @@ pub const fn saturating_pow(self, exp: u32) -> Self {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_add(self, rhs: Self) -> Self {
             intrinsics::wrapping_add(self, rhs)
         }
@@ -833,7 +833,7 @@ pub const fn wrapping_add(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_sub(self, rhs: Self) -> Self {
             intrinsics::wrapping_sub(self, rhs)
         }
@@ -856,7 +856,7 @@ pub const fn wrapping_sub(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_mul(self, rhs: Self) -> Self {
             intrinsics::wrapping_mul(self, rhs)
         }
@@ -878,7 +878,7 @@ pub const fn wrapping_mul(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_wrapping_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_div(self, rhs: Self) -> Self {
             self / rhs
         }
@@ -903,7 +903,7 @@ pub const fn wrapping_div(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_div_euclid(self, rhs: Self) -> Self {
             self / rhs
         }
@@ -926,7 +926,7 @@ pub const fn wrapping_div_euclid(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_wrapping_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_rem(self, rhs: Self) -> Self {
             self % rhs
         }
@@ -952,7 +952,7 @@ pub const fn wrapping_rem(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self {
             self % rhs
         }
@@ -1008,7 +1008,7 @@ pub const fn wrapping_neg(self) -> Self {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_shl(self, rhs: u32) -> Self {
             // SAFETY: the masking by the bitsize of the type ensures that we do not shift
             // out of bounds
@@ -1040,7 +1040,7 @@ pub const fn wrapping_shl(self, rhs: u32) -> Self {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn wrapping_shr(self, rhs: u32) -> Self {
             // SAFETY: the masking by the bitsize of the type ensures that we do not shift
             // out of bounds
@@ -1106,7 +1106,7 @@ pub const fn wrapping_pow(self, mut exp: u32) -> Self {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
             let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT);
             (a as Self, b)
@@ -1131,7 +1131,7 @@ pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
             let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT);
             (a as Self, b)
@@ -1158,7 +1158,7 @@ pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
             let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT);
             (a as Self, b)
@@ -1182,7 +1182,7 @@ pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")]
         /// ```
-        #[inline]
+        #[inline(always)]
         #[stable(feature = "wrapping", since = "1.7.0")]
         #[rustc_const_stable(feature = "const_overflowing_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
@@ -1212,7 +1212,7 @@ pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) {
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")]
         /// ```
-        #[inline]
+        #[inline(always)]
         #[stable(feature = "euclidean_division", since = "1.38.0")]
         #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
@@ -1239,7 +1239,7 @@ pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) {
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")]
         /// ```
-        #[inline]
+        #[inline(always)]
         #[stable(feature = "wrapping", since = "1.7.0")]
         #[rustc_const_stable(feature = "const_overflowing_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
@@ -1269,7 +1269,7 @@ pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")]
         /// ```
-        #[inline]
+        #[inline(always)]
         #[stable(feature = "euclidean_division", since = "1.38.0")]
         #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
@@ -1293,7 +1293,7 @@ pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) {
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false));")]
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), ", true));")]
         /// ```
-        #[inline]
+        #[inline(always)]
         #[stable(feature = "wrapping", since = "1.7.0")]
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         pub const fn overflowing_neg(self) -> (Self, bool) {
@@ -1320,7 +1320,7 @@ pub const fn overflowing_neg(self) -> (Self, bool) {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
             (self.wrapping_shl(rhs), (rhs > ($BITS - 1)))
         }
@@ -1345,7 +1345,7 @@ pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
         #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
             (self.wrapping_shr(rhs), (rhs > ($BITS - 1)))
         }
@@ -1458,7 +1458,7 @@ pub const fn pow(self, mut exp: u32) -> Self {
         #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         #[rustc_inherit_overflow_checks]
         pub const fn div_euclid(self, rhs: Self) -> Self {
             self / rhs
@@ -1486,7 +1486,7 @@ pub const fn div_euclid(self, rhs: Self) -> Self {
         #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[inline]
+        #[inline(always)]
         #[rustc_inherit_overflow_checks]
         pub const fn rem_euclid(self, rhs: Self) -> Self {
             self % rhs
@@ -1504,7 +1504,7 @@ pub const fn rem_euclid(self, rhs: Self) -> Self {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")]
-        #[inline]
+        #[inline(always)]
         pub const fn is_power_of_two(self) -> bool {
             self.count_ones() == 1
         }
index 2f78ba8f28e29189697d5c78d375a792d13c3854..ecaff053bd5c0ebe49471c176c3ade6b909fdc09 100644 (file)
@@ -1,4 +1,5 @@
-use crate::ops::Try;
+use crate::convert;
+use crate::ops::{self, Try};
 
 /// Used to tell an operation whether it should exit early or go on as usual.
 ///
@@ -81,6 +82,35 @@ fn from_ok(v: Self::Ok) -> Self {
     }
 }
 
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<B, C> ops::TryV2 for ControlFlow<B, C> {
+    type Output = C;
+    type Residual = ControlFlow<B, convert::Infallible>;
+
+    #[inline]
+    fn from_output(output: Self::Output) -> Self {
+        ControlFlow::Continue(output)
+    }
+
+    #[inline]
+    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+        match self {
+            ControlFlow::Continue(c) => ControlFlow::Continue(c),
+            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
+        }
+    }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<B, C> ops::FromResidual for ControlFlow<B, C> {
+    #[inline]
+    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
+        match residual {
+            ControlFlow::Break(b) => ControlFlow::Break(b),
+        }
+    }
+}
+
 impl<B, C> ControlFlow<B, C> {
     /// Returns `true` if this is a `Break` variant.
     ///
index a8dea4e9b4ea8f631c39ce79430183c98e6238ea..964378cc9c3c6d394ab8a37d789c90e8e7c9ca29 100644 (file)
@@ -61,6 +61,10 @@ pub trait Index<Idx: ?Sized> {
     type Output: ?Sized;
 
     /// Performs the indexing (`container[index]`) operation.
+    ///
+    /// # Panics
+    ///
+    /// May panic if the index is out of bounds.
     #[stable(feature = "rust1", since = "1.0.0")]
     #[track_caller]
     fn index(&self, index: Idx) -> &Self::Output;
@@ -161,6 +165,10 @@ pub trait Index<Idx: ?Sized> {
 #[doc(alias = "[]")]
 pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
     /// Performs the mutable indexing (`container[index]`) operation.
+    ///
+    /// # Panics
+    ///
+    /// May panic if the index is out of bounds.
     #[stable(feature = "rust1", since = "1.0.0")]
     #[track_caller]
     fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
index 354ad6b7b7333b0cc4a2b42083db8dca7b808738..1b07936ccde1dceb2bcbd551a6c2d2952cd080bc 100644 (file)
 mod index;
 mod range;
 mod r#try;
+mod try_trait;
 mod unsize;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 #[unstable(feature = "try_trait", issue = "42327")]
 pub use self::r#try::Try;
 
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+pub use self::try_trait::FromResidual;
+
+#[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")]
+pub use self::try_trait::Try as TryV2;
+
 #[unstable(feature = "generator_trait", issue = "43122")]
 pub use self::generator::{Generator, GeneratorState};
 
diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs
new file mode 100644 (file)
index 0000000..0c819b0
--- /dev/null
@@ -0,0 +1,243 @@
+use crate::ops::ControlFlow;
+
+/// The `?` operator and `try {}` blocks.
+///
+/// `try_*` methods typically involve a type implementing this trait.  For
+/// example, the closures passed to [`Iterator::try_fold`] and
+/// [`Iterator::try_for_each`] must return such a type.
+///
+/// `Try` types are typically those containing two or more categories of values,
+/// some subset of which are so commonly handled via early returns that it's
+/// worth providing a terse (but still visible) syntax to make that easy.
+///
+/// This is most often seen for error handling with [`Result`] and [`Option`].
+/// The quintessential implementation of this trait is on [`ControlFlow`].
+///
+/// # Using `Try` in Generic Code
+///
+/// `Iterator::try_fold` was stabilized to call back in Rust 1.27, but
+/// this trait is much newer.  To illustrate the various associated types and
+/// methods, let's implement our own version.
+///
+/// As a reminder, an infallible version of a fold looks something like this:
+/// ```
+/// fn simple_fold<A, T>(
+///     iter: impl Iterator<Item = T>,
+///     mut accum: A,
+///     mut f: impl FnMut(A, T) -> A,
+/// ) -> A {
+///     for x in iter {
+///         accum = f(accum, x);
+///     }
+///     accum
+/// }
+/// ```
+///
+/// So instead of `f` returning just an `A`, we'll need it to return some other
+/// type that produces an `A` in the "don't short circuit" path.  Conveniently,
+/// that's also the type we need to return from the function.
+///
+/// Let's add a new generic parameter `R` for that type, and bound it to the
+/// output type that we want:
+/// ```
+/// # #![feature(try_trait_v2)]
+/// # #![feature(try_trait_transition)]
+/// # use std::ops::TryV2 as Try;
+/// fn simple_try_fold_1<A, T, R: Try<Output = A>>(
+///     iter: impl Iterator<Item = T>,
+///     mut accum: A,
+///     mut f: impl FnMut(A, T) -> R,
+/// ) -> R {
+///     todo!()
+/// }
+/// ```
+///
+/// If we get through the entire iterator, we need to wrap up the accumulator
+/// into the return type using [`Try::from_output`]:
+/// ```
+/// # #![feature(try_trait_v2)]
+/// # #![feature(try_trait_transition)]
+/// # #![feature(control_flow_enum)]
+/// # use std::ops::{ControlFlow, TryV2 as Try};
+/// fn simple_try_fold_2<A, T, R: Try<Output = A>>(
+///     iter: impl Iterator<Item = T>,
+///     mut accum: A,
+///     mut f: impl FnMut(A, T) -> R,
+/// ) -> R {
+///     for x in iter {
+///         let cf = f(accum, x).branch();
+///         match cf {
+///             ControlFlow::Continue(a) => accum = a,
+///             ControlFlow::Break(_) => todo!(),
+///         }
+///     }
+///     R::from_output(accum)
+/// }
+/// ```
+///
+/// We'll also need [`FromResidual::from_residual`] to turn the residual back
+/// into the original type.  But because it's a supertrait of `Try`, we don't
+/// need to mention it in the bounds.  All types which implement `Try` can be
+/// recreated from their corresponding residual, so we'll just call it:
+/// ```
+/// # #![feature(try_trait_v2)]
+/// # #![feature(try_trait_transition)]
+/// # #![feature(control_flow_enum)]
+/// # use std::ops::{ControlFlow, TryV2 as Try};
+/// pub fn simple_try_fold_3<A, T, R: Try<Output = A>>(
+///     iter: impl Iterator<Item = T>,
+///     mut accum: A,
+///     mut f: impl FnMut(A, T) -> R,
+/// ) -> R {
+///     for x in iter {
+///         let cf = f(accum, x).branch();
+///         match cf {
+///             ControlFlow::Continue(a) => accum = a,
+///             ControlFlow::Break(r) => return R::from_residual(r),
+///         }
+///     }
+///     R::from_output(accum)
+/// }
+/// ```
+///
+/// But this "call `branch`, then `match` on it, and `return` if it was a
+/// `Break`" is exactly what happens inside the `?` operator.  So rather than
+/// do all this manually, we can just use `?` instead:
+/// ```compile_fail (enable again once ? converts to the new trait)
+/// # #![feature(try_trait_v2)]
+/// # #![feature(try_trait_transition)]
+/// # use std::ops::TryV2 as Try;
+/// fn simple_try_fold<A, T, R: Try<Output = A>>(
+///     iter: impl Iterator<Item = T>,
+///     mut accum: A,
+///     mut f: impl FnMut(A, T) -> R,
+/// ) -> R {
+///     for x in iter {
+///         accum = f(accum, x)?;
+///     }
+///     R::from_output(accum)
+/// }
+/// ```
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+pub trait Try: FromResidual {
+    /// The type of the value produced by `?` when *not* short-circuiting.
+    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    type Output;
+
+    /// The type of the value passed to [`FromResidual::from_residual`]
+    /// as part of `?` when short-circuiting.
+    ///
+    /// This represents the possible values of the `Self` type which are *not*
+    /// represented by the `Output` type.
+    ///
+    /// # Note to Implementors
+    ///
+    /// The choice of this type is critical to interconversion.
+    /// Unlike the `Output` type, which will often be a raw generic type,
+    /// this type is typically a newtype of some sort to "color" the type
+    /// so that it's distinguishable from the residuals of other types.
+    ///
+    /// This is why `Result<T, E>::Residual` is not `E`, but `Result<Infallible, E>`.
+    /// That way it's distinct from `ControlFlow<E>::Residual`, for example,
+    /// and thus `?` on `ControlFlow` cannot be used in a method returning `Result`.
+    ///
+    /// If you're making a generic type `Foo<T>` that implements `Try<Output = T>`,
+    /// then typically you can use `Foo<std::convert::Infallible>` as its `Residual`
+    /// type: that type will have a "hole" in the correct place, and will maintain the
+    /// "foo-ness" of the residual so other types need to opt-in to interconversion.
+    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    type Residual;
+
+    /// Constructs the type from its `Output` type.
+    ///
+    /// This should be implemented consistently with the `branch` method
+    /// such that applying the `?` operator will get back the original value:
+    /// `Try::from_output(x).branch() --> ControlFlow::Continue(x)`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(try_trait_v2)]
+    /// #![feature(control_flow_enum)]
+    /// #![feature(try_trait_transition)]
+    /// use std::ops::TryV2 as Try;
+    ///
+    /// assert_eq!(<Result<_, String> as Try>::from_output(3), Ok(3));
+    /// assert_eq!(<Option<_> as Try>::from_output(4), Some(4));
+    /// assert_eq!(
+    ///     <std::ops::ControlFlow<String, _> as Try>::from_output(5),
+    ///     std::ops::ControlFlow::Continue(5),
+    /// );
+    ///
+    /// # fn make_question_mark_work() -> Option<()> {
+    /// assert_eq!(Option::from_output(4)?, 4);
+    /// # None }
+    /// # make_question_mark_work();
+    ///
+    /// // This is used, for example, on the accumulator in `try_fold`:
+    /// let r = std::iter::empty().try_fold(4, |_, ()| -> Option<_> { unreachable!() });
+    /// assert_eq!(r, Some(4));
+    /// ```
+    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    fn from_output(output: Self::Output) -> Self;
+
+    /// Used in `?` to decide whether the operator should produce a value
+    /// (because this returned [`ControlFlow::Continue`])
+    /// or propagate a value back to the caller
+    /// (because this returned [`ControlFlow::Break`]).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(try_trait_v2)]
+    /// #![feature(control_flow_enum)]
+    /// #![feature(try_trait_transition)]
+    /// use std::ops::{ControlFlow, TryV2 as Try};
+    ///
+    /// assert_eq!(Ok::<_, String>(3).branch(), ControlFlow::Continue(3));
+    /// assert_eq!(Err::<String, _>(3).branch(), ControlFlow::Break(Err(3)));
+    ///
+    /// assert_eq!(Some(3).branch(), ControlFlow::Continue(3));
+    /// assert_eq!(None::<String>.branch(), ControlFlow::Break(None));
+    ///
+    /// assert_eq!(ControlFlow::<String, _>::Continue(3).branch(), ControlFlow::Continue(3));
+    /// assert_eq!(
+    ///     ControlFlow::<_, String>::Break(3).branch(),
+    ///     ControlFlow::Break(ControlFlow::Break(3)),
+    /// );
+    /// ```
+    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
+}
+
+/// Used to specify which residuals can be converted into which [`Try`] types.
+///
+/// Every `Try` type needs to be recreatable from its own associated
+/// `Residual` type, but can also have additional `FromResidual` implementations
+/// to support interconversion with other `Try` types.
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+pub trait FromResidual<R = <Self as Try>::Residual> {
+    /// Constructs the type from a compatible `Residual` type.
+    ///
+    /// This should be implemented consistently with the `branch` method such
+    /// that applying the `?` operator will get back an equivalent residual:
+    /// `FromResidual::from_residual(r).branch() --> ControlFlow::Break(r)`.
+    /// (It may not be an *identical* residual when interconversion is involved.)
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(try_trait_v2)]
+    /// #![feature(control_flow_enum)]
+    /// use std::ops::{ControlFlow, FromResidual};
+    ///
+    /// assert_eq!(Result::<String, i64>::from_residual(Err(3_u8)), Err(3));
+    /// assert_eq!(Option::<String>::from_residual(None), None);
+    /// assert_eq!(
+    ///     ControlFlow::<_, String>::from_residual(ControlFlow::Break(5)),
+    ///     ControlFlow::Break(5),
+    /// );
+    /// ```
+    #[unstable(feature = "try_trait_v2", issue = "84277")]
+    fn from_residual(residual: R) -> Self;
+}
index 1e1e9613696986401d96be8da99dc14eba98ceba..04551dded8c774208a604eac123769c2d22bcd68 100644 (file)
 use crate::iter::{FromIterator, FusedIterator, TrustedLen};
 use crate::pin::Pin;
 use crate::{
-    hint, mem,
-    ops::{self, Deref, DerefMut},
+    convert, hint, mem,
+    ops::{self, ControlFlow, Deref, DerefMut},
 };
 
 /// The `Option` type. See [the module level documentation](self) for more.
@@ -489,8 +489,8 @@ pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
         }
     }
 
-    /// Applies a function to the contained value (if any),
-    /// or returns the provided default (if not).
+    /// Returns the provided default result (if none),
+    /// or applies a function to the contained value (if any).
     ///
     /// Arguments passed to `map_or` are eagerly evaluated; if you are passing
     /// the result of a function call, it is recommended to use [`map_or_else`],
@@ -516,8 +516,8 @@ pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
         }
     }
 
-    /// Applies a function to the contained value (if any),
-    /// or computes a default (if not).
+    /// Computes a default function result (if none), or
+    /// applies a different function to the contained value (if any).
     ///
     /// # Examples
     ///
@@ -594,34 +594,6 @@ pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
         }
     }
 
-    /// Inserts `value` into the option then returns a mutable reference to it.
-    ///
-    /// If the option already contains a value, the old value is dropped.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// let mut opt = None;
-    /// let val = opt.insert(1);
-    /// assert_eq!(*val, 1);
-    /// assert_eq!(opt.unwrap(), 1);
-    /// let val = opt.insert(2);
-    /// assert_eq!(*val, 2);
-    /// *val = 3;
-    /// assert_eq!(opt.unwrap(), 3);
-    /// ```
-    #[inline]
-    #[stable(feature = "option_insert", since = "1.53.0")]
-    pub fn insert(&mut self, value: T) -> &mut T {
-        *self = Some(value);
-
-        match self {
-            Some(v) => v,
-            // SAFETY: the code above just filled the option
-            None => unsafe { hint::unreachable_unchecked() },
-        }
-    }
-
     /////////////////////////////////////////////////////////////////////////
     // Iterator constructors
     /////////////////////////////////////////////////////////////////////////
@@ -849,12 +821,46 @@ pub fn xor(self, optb: Option<T>) -> Option<T> {
     }
 
     /////////////////////////////////////////////////////////////////////////
-    // Entry-like operations to insert if None and return a reference
+    // Entry-like operations to insert a value and return a reference
     /////////////////////////////////////////////////////////////////////////
 
+    /// Inserts `value` into the option then returns a mutable reference to it.
+    ///
+    /// If the option already contains a value, the old value is dropped.
+    ///
+    /// See also [`Option::get_or_insert`], which doesn't update the value if
+    /// the option already contains [`Some`].
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// let mut opt = None;
+    /// let val = opt.insert(1);
+    /// assert_eq!(*val, 1);
+    /// assert_eq!(opt.unwrap(), 1);
+    /// let val = opt.insert(2);
+    /// assert_eq!(*val, 2);
+    /// *val = 3;
+    /// assert_eq!(opt.unwrap(), 3);
+    /// ```
+    #[inline]
+    #[stable(feature = "option_insert", since = "1.53.0")]
+    pub fn insert(&mut self, value: T) -> &mut T {
+        *self = Some(value);
+
+        match self {
+            Some(v) => v,
+            // SAFETY: the code above just filled the option
+            None => unsafe { hint::unreachable_unchecked() },
+        }
+    }
+
     /// Inserts `value` into the option if it is [`None`], then
     /// returns a mutable reference to the contained value.
     ///
+    /// See also [`Option::insert`], which updates the value even if
+    /// the option already contains [`Some`].
+    ///
     /// # Examples
     ///
     /// ```
@@ -1658,6 +1664,35 @@ fn from_error(_: NoneError) -> Self {
     }
 }
 
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T> ops::TryV2 for Option<T> {
+    type Output = T;
+    type Residual = Option<convert::Infallible>;
+
+    #[inline]
+    fn from_output(output: Self::Output) -> Self {
+        Some(output)
+    }
+
+    #[inline]
+    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+        match self {
+            Some(v) => ControlFlow::Continue(v),
+            None => ControlFlow::Break(None),
+        }
+    }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T> ops::FromResidual for Option<T> {
+    #[inline]
+    fn from_residual(residual: Option<convert::Infallible>) -> Self {
+        match residual {
+            None => None,
+        }
+    }
+}
+
 impl<T> Option<Option<T>> {
     /// Converts from `Option<Option<T>>` to `Option<T>`
     ///
index 4571ba154eacb750e53eb33b5ed99a0f3049983e..b9b2ba9ae61e97f2bd2761790e896ddda1215a05 100644 (file)
@@ -226,7 +226,7 @@ pub unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit<T>>
     #[stable(feature = "rust1", since = "1.0.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
     #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
-    #[inline]
+    #[inline(always)]
     pub const unsafe fn offset(self, count: isize) -> *const T
     where
         T: Sized,
@@ -288,7 +288,7 @@ pub unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit<T>>
     #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
     #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
-    #[inline]
+    #[inline(always)]
     pub const fn wrapping_offset(self, count: isize) -> *const T
     where
         T: Sized,
@@ -507,7 +507,7 @@ pub const fn guaranteed_ne(self, other: *const T) -> bool
     #[stable(feature = "pointer_methods", since = "1.26.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
     #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
-    #[inline]
+    #[inline(always)]
     pub const unsafe fn add(self, count: usize) -> Self
     where
         T: Sized,
@@ -634,7 +634,7 @@ pub const fn guaranteed_ne(self, other: *const T) -> bool
     #[stable(feature = "pointer_methods", since = "1.26.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
     #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
-    #[inline]
+    #[inline(always)]
     pub const fn wrapping_add(self, count: usize) -> Self
     where
         T: Sized,
index ba08823e343b116fc44c3d030bcd1388f7ba785c..55c019c51d51b5763c86aaaa6fc6e99ee557bcbc 100644 (file)
@@ -231,7 +231,7 @@ pub unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit<T>>
     #[stable(feature = "rust1", since = "1.0.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
     #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
-    #[inline]
+    #[inline(always)]
     pub const unsafe fn offset(self, count: isize) -> *mut T
     where
         T: Sized,
@@ -294,7 +294,7 @@ pub unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit<T>>
     #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
     #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
-    #[inline]
+    #[inline(always)]
     pub const fn wrapping_offset(self, count: isize) -> *mut T
     where
         T: Sized,
@@ -613,7 +613,7 @@ pub const fn guaranteed_eq(self, other: *mut T) -> bool
     #[stable(feature = "pointer_methods", since = "1.26.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
     #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
-    #[inline]
+    #[inline(always)]
     pub const unsafe fn add(self, count: usize) -> Self
     where
         T: Sized,
@@ -740,7 +740,7 @@ pub const fn guaranteed_eq(self, other: *mut T) -> bool
     #[stable(feature = "pointer_methods", since = "1.26.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
     #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
-    #[inline]
+    #[inline(always)]
     pub const fn wrapping_add(self, count: usize) -> Self
     where
         T: Sized,
index 1227d9b01f011105a316b693704d63e22802caa0..6d1e28f4cd7d712d73ea2f1143acd8d7b8d5c94e 100644 (file)
@@ -1,5 +1,9 @@
 #![allow(missing_docs)]
 #![unstable(feature = "raw", issue = "27751")]
+#![rustc_deprecated(
+    since = "1.53.0",
+    reason = "use pointer metadata APIs instead https://github.com/rust-lang/rust/issues/81513"
+)]
 
 //! Contains struct definitions for the layout of compiler built-in types.
 //!
index 20f8095b7d1cee998b7115c3949b65514839ed01..e0071f806aaafc54b3fe4ec0820f2b00d2a0de1d 100644 (file)
 #![stable(feature = "rust1", since = "1.0.0")]
 
 use crate::iter::{self, FromIterator, FusedIterator, TrustedLen};
-use crate::ops::{self, Deref, DerefMut};
+use crate::ops::{self, ControlFlow, Deref, DerefMut};
 use crate::{convert, fmt, hint};
 
 /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).
@@ -506,8 +506,8 @@ pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Result<U, E> {
         }
     }
 
-    /// Applies a function to the contained value (if [`Ok`]),
-    /// or returns the provided default (if [`Err`]).
+    /// Returns the provided default (if [`Err`]), or
+    /// applies a function to the contained value (if [`Ok`]),
     ///
     /// Arguments passed to `map_or` are eagerly evaluated; if you are passing
     /// the result of a function call, it is recommended to use [`map_or_else`],
@@ -533,9 +533,9 @@ pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
         }
     }
 
-    /// Maps a `Result<T, E>` to `U` by applying a function to a
-    /// contained [`Ok`] value, or a fallback function to a
-    /// contained [`Err`] value.
+    /// Maps a `Result<T, E>` to `U` by applying a fallback function to a
+    /// contained [`Err`] value, or a default function to a
+    /// contained [`Ok`] value.
     ///
     /// This function can be used to unpack a successful result
     /// while handling an error.
@@ -1646,3 +1646,32 @@ fn from_error(v: E) -> Self {
         Err(v)
     }
 }
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E> ops::TryV2 for Result<T, E> {
+    type Output = T;
+    type Residual = Result<convert::Infallible, E>;
+
+    #[inline]
+    fn from_output(output: Self::Output) -> Self {
+        Ok(output)
+    }
+
+    #[inline]
+    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+        match self {
+            Ok(v) => ControlFlow::Continue(v),
+            Err(e) => ControlFlow::Break(Err(e)),
+        }
+    }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Result<T, F> {
+    #[inline]
+    fn from_residual(residual: Result<convert::Infallible, E>) -> Self {
+        match residual {
+            Err(e) => Err(From::from(e)),
+        }
+    }
+}
index 22fa08b97957019843051e19c9b8a1d4f6f4ef0a..906dcb1e8bceefd84ce2bddfdd0a5df4ada3be08 100644 (file)
@@ -146,7 +146,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 #[unstable(feature = "inherent_ascii_escape", issue = "77174")]
 impl<'a> fmt::Debug for EscapeAscii<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("EscapeAscii { .. }")
+        f.debug_struct("EscapeAscii").finish_non_exhaustive()
     }
 }
 
index c92b37b14be4fa91d021d66822095ff000600db4..f722430354991961f314ba0091a414b2b6d9dbe8 100644 (file)
@@ -81,6 +81,8 @@ impl Sealed for ops::RangeFull {}
     impl Sealed for ops::RangeInclusive<usize> {}
     #[stable(feature = "slice_get_slice", since = "1.28.0")]
     impl Sealed for ops::RangeToInclusive<usize> {}
+    #[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")]
+    impl Sealed for (ops::Bound<usize>, ops::Bound<usize>) {}
 }
 
 /// A helper trait used for indexing operations.
@@ -546,3 +548,113 @@ pub fn range<R>(range: R, bounds: ops::RangeTo<usize>) -> ops::Range<usize>
 
     ops::Range { start, end }
 }
+
+/// Convert pair of `ops::Bound`s into `ops::Range` without performing any bounds checking and (in debug) overflow checking
+fn into_range_unchecked(
+    len: usize,
+    (start, end): (ops::Bound<usize>, ops::Bound<usize>),
+) -> ops::Range<usize> {
+    use ops::Bound;
+    let start = match start {
+        Bound::Included(i) => i,
+        Bound::Excluded(i) => i + 1,
+        Bound::Unbounded => 0,
+    };
+    let end = match end {
+        Bound::Included(i) => i + 1,
+        Bound::Excluded(i) => i,
+        Bound::Unbounded => len,
+    };
+    start..end
+}
+
+/// Convert pair of `ops::Bound`s into `ops::Range`.
+/// Returns `None` on overflowing indices.
+fn into_range(
+    len: usize,
+    (start, end): (ops::Bound<usize>, ops::Bound<usize>),
+) -> Option<ops::Range<usize>> {
+    use ops::Bound;
+    let start = match start {
+        Bound::Included(start) => start,
+        Bound::Excluded(start) => start.checked_add(1)?,
+        Bound::Unbounded => 0,
+    };
+
+    let end = match end {
+        Bound::Included(end) => end.checked_add(1)?,
+        Bound::Excluded(end) => end,
+        Bound::Unbounded => len,
+    };
+
+    // Don't bother with checking `start < end` and `end <= len`
+    // since these checks are handled by `Range` impls
+
+    Some(start..end)
+}
+
+/// Convert pair of `ops::Bound`s into `ops::Range`.
+/// Panics on overflowing indices.
+fn into_slice_range(
+    len: usize,
+    (start, end): (ops::Bound<usize>, ops::Bound<usize>),
+) -> ops::Range<usize> {
+    use ops::Bound;
+    let start = match start {
+        Bound::Included(start) => start,
+        Bound::Excluded(start) => {
+            start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
+        }
+        Bound::Unbounded => 0,
+    };
+
+    let end = match end {
+        Bound::Included(end) => {
+            end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
+        }
+        Bound::Excluded(end) => end,
+        Bound::Unbounded => len,
+    };
+
+    // Don't bother with checking `start < end` and `end <= len`
+    // since these checks are handled by `Range` impls
+
+    start..end
+}
+
+#[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")]
+unsafe impl<T> SliceIndex<[T]> for (ops::Bound<usize>, ops::Bound<usize>) {
+    type Output = [T];
+
+    #[inline]
+    fn get(self, slice: &[T]) -> Option<&Self::Output> {
+        into_range(slice.len(), self)?.get(slice)
+    }
+
+    #[inline]
+    fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
+        into_range(slice.len(), self)?.get_mut(slice)
+    }
+
+    #[inline]
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
+        // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
+        unsafe { into_range_unchecked(slice.len(), self).get_unchecked(slice) }
+    }
+
+    #[inline]
+    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
+        // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
+        unsafe { into_range_unchecked(slice.len(), self).get_unchecked_mut(slice) }
+    }
+
+    #[inline]
+    fn index(self, slice: &[T]) -> &Self::Output {
+        into_slice_range(slice.len(), self).index(slice)
+    }
+
+    #[inline]
+    fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
+        into_slice_range(slice.len(), self).index_mut(slice)
+    }
+}
index 4eac017f9153c74e1c9713910fe53b0e9f3b550b..724137aba9f3c57696bbd6dff4baf58719ad13a9 100644 (file)
@@ -189,6 +189,30 @@ impl<'a> CharIndices<'a> {
     pub fn as_str(&self) -> &'a str {
         self.iter.as_str()
     }
+
+    /// Returns the byte position of the next character, or the length
+    /// of the underlying string if there are no more characters.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(char_indices_offset)]
+    /// let mut chars = "a楽".char_indices();
+    ///
+    /// assert_eq!(chars.offset(), 0);
+    /// assert_eq!(chars.next(), Some((0, 'a')));
+    ///
+    /// assert_eq!(chars.offset(), 1);
+    /// assert_eq!(chars.next(), Some((1, '楽')));
+    ///
+    /// assert_eq!(chars.offset(), 4);
+    /// assert_eq!(chars.next(), None);
+    /// ```
+    #[inline]
+    #[unstable(feature = "char_indices_offset", issue = "83871")]
+    pub fn offset(&self) -> usize {
+        self.front_offset
+    }
 }
 
 /// An iterator over the bytes of a string slice.
@@ -1359,7 +1383,7 @@ pub struct EncodeUtf16<'a> {
 #[stable(feature = "collection_debug", since = "1.17.0")]
 impl fmt::Debug for EncodeUtf16<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("EncodeUtf16 { .. }")
+        f.debug_struct("EncodeUtf16").finish_non_exhaustive()
     }
 }
 
index 42c9d9f0cc03953379d14357857cd53ffbb2e43f..2765c21a46db11b1e2ad5d142255319d126c8e7c 100644 (file)
@@ -1,6 +1,7 @@
 #![stable(feature = "futures_api", since = "1.36.0")]
 
-use crate::ops::Try;
+use crate::convert;
+use crate::ops::{self, ControlFlow, Try};
 use crate::result::Result;
 
 /// Indicates whether a value is available or if the current task has been
@@ -152,6 +153,36 @@ fn from_ok(x: Self::Ok) -> Self {
     }
 }
 
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E> ops::TryV2 for Poll<Result<T, E>> {
+    type Output = Poll<T>;
+    type Residual = Result<convert::Infallible, E>;
+
+    #[inline]
+    fn from_output(c: Self::Output) -> Self {
+        c.map(Ok)
+    }
+
+    #[inline]
+    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+        match self {
+            Poll::Ready(Ok(x)) => ControlFlow::Continue(Poll::Ready(x)),
+            Poll::Ready(Err(e)) => ControlFlow::Break(Err(e)),
+            Poll::Pending => ControlFlow::Continue(Poll::Pending),
+        }
+    }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Poll<Result<T, F>> {
+    #[inline]
+    fn from_residual(x: Result<convert::Infallible, E>) -> Self {
+        match x {
+            Err(e) => Poll::Ready(Err(From::from(e))),
+        }
+    }
+}
+
 #[stable(feature = "futures_api", since = "1.36.0")]
 impl<T, E> Try for Poll<Option<Result<T, E>>> {
     type Ok = Poll<Option<T>>;
@@ -177,3 +208,36 @@ fn from_ok(x: Self::Ok) -> Self {
         x.map(|x| x.map(Ok))
     }
 }
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E> ops::TryV2 for Poll<Option<Result<T, E>>> {
+    type Output = Poll<Option<T>>;
+    type Residual = Result<convert::Infallible, E>;
+
+    #[inline]
+    fn from_output(c: Self::Output) -> Self {
+        c.map(|x| x.map(Ok))
+    }
+
+    #[inline]
+    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+        match self {
+            Poll::Ready(Some(Ok(x))) => ControlFlow::Continue(Poll::Ready(Some(x))),
+            Poll::Ready(Some(Err(e))) => ControlFlow::Break(Err(e)),
+            Poll::Ready(None) => ControlFlow::Continue(Poll::Ready(None)),
+            Poll::Pending => ControlFlow::Continue(Poll::Pending),
+        }
+    }
+}
+
+#[unstable(feature = "try_trait_v2", issue = "84277")]
+impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>>
+    for Poll<Option<Result<T, F>>>
+{
+    #[inline]
+    fn from_residual(x: Result<convert::Infallible, E>) -> Self {
+        match x {
+            Err(e) => Poll::Ready(Some(Err(From::from(e)))),
+        }
+    }
+}
index fa6a6c2cccc015184b42537d5b1ab23b13449a15..bfea39e3211fcf4d84f0586847857ecf2a83b93d 100644 (file)
@@ -135,17 +135,21 @@ impl Duration {
 
     /// The maximum duration.
     ///
-    /// It is roughly equal to a duration of 584,942,417,355 years.
+    /// May vary by platform as necessary. Must be able to contain the difference between
+    /// two instances of [`Instant`] or two instances of [`SystemTime`].
+    /// This constraint gives it a value of about 584,942,417,355 years in practice,
+    /// which is currently used on all platforms.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(duration_constants)]
     /// use std::time::Duration;
     ///
     /// assert_eq!(Duration::MAX, Duration::new(u64::MAX, 1_000_000_000 - 1));
     /// ```
-    #[unstable(feature = "duration_constants", issue = "57391")]
+    /// [`Instant`]: ../../std/time/struct.Instant.html
+    /// [`SystemTime`]: ../../std/time/struct.SystemTime.html
+    #[stable(feature = "duration_saturating_ops", since = "1.53.0")]
     pub const MAX: Duration = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
 
     /// Creates a new `Duration` from the specified number of whole seconds and
index 129c121e8ceac0f5cd0974a9b15832a14445c5a2..7580010a28b4a5394ef5665d09f0a0a12a509e62 100644 (file)
@@ -105,12 +105,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         }
 
         assert_eq!("Foo { .. }", format!("{:?}", Foo));
-        assert_eq!(
-            "Foo {
-    ..
-}",
-            format!("{:#?}", Foo)
-        );
+        assert_eq!("Foo { .. }", format!("{:#?}", Foo));
     }
 
     #[test]
index f6bfe67e1b12c9c7fff5d79aea2a32fba0d7be7f..35e4d213dde1e9f18939988a5744b2de30b9fddc 100644 (file)
@@ -1,6 +1,5 @@
 #![feature(alloc_layout_extra)]
 #![feature(array_chunks)]
-#![feature(array_from_ref)]
 #![feature(array_methods)]
 #![feature(array_map)]
 #![feature(array_windows)]
index c0b75036f4f3eb4107ed241d4c54ae495ee797f0..dfdbc9305d2a360e56262aa10d14578ae817651f 100644 (file)
@@ -97,6 +97,9 @@ fn test_transmute_copy() {
     assert_eq!(1, unsafe { transmute_copy(&1) });
 }
 
+// Remove this test when `std::raw` is removed.
+// The replacement pointer metadata APIs are tested in library/core/tests/ptr.rs
+#[allow(deprecated)]
 #[test]
 fn test_transmute() {
     trait Foo {
index 7e198631cc7eb7ab48b2a6f46464b1804fb3744c..3a98cd9d2ee91be6c98e58c65acf05694956b95c 100644 (file)
@@ -1280,6 +1280,9 @@ macro_rules! panic_cases {
             }
         )*) => {$(
             mod $case_name {
+                #[allow(unused_imports)]
+                use core::ops::Bound;
+
                 #[test]
                 fn pass() {
                     let mut v = $data;
@@ -1376,6 +1379,24 @@ fn simple() {
             bad: data[7..=6];
             message: "out of range";
         }
+
+        in mod boundpair_len {
+            data: [0, 1, 2, 3, 4, 5];
+
+            good: data[(Bound::Included(6), Bound::Unbounded)] == [];
+            good: data[(Bound::Unbounded, Bound::Included(5))] == [0, 1, 2, 3, 4, 5];
+            good: data[(Bound::Unbounded, Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5];
+            good: data[(Bound::Included(0), Bound::Included(5))] == [0, 1, 2, 3, 4, 5];
+            good: data[(Bound::Included(0), Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5];
+            good: data[(Bound::Included(2), Bound::Excluded(4))] == [2, 3];
+            good: data[(Bound::Excluded(1), Bound::Included(4))] == [2, 3, 4];
+            good: data[(Bound::Excluded(5), Bound::Excluded(6))] == [];
+            good: data[(Bound::Included(6), Bound::Excluded(6))] == [];
+            good: data[(Bound::Excluded(5), Bound::Included(5))] == [];
+            good: data[(Bound::Included(6), Bound::Included(5))] == [];
+            bad: data[(Bound::Unbounded, Bound::Included(6))];
+            message: "out of range";
+        }
     }
 
     panic_cases! {
@@ -1416,6 +1437,14 @@ fn simple() {
             bad: data[4..=2];
             message: "but ends at";
         }
+
+        in mod boundpair_neg_width {
+            data: [0, 1, 2, 3, 4, 5];
+
+            good: data[(Bound::Included(4), Bound::Excluded(4))] == [];
+            bad: data[(Bound::Included(4), Bound::Excluded(3))];
+            message: "but ends at";
+        }
     }
 
     panic_cases! {
@@ -1434,6 +1463,20 @@ fn simple() {
             bad: data[..= usize::MAX];
             message: "maximum usize";
         }
+
+        in mod boundpair_overflow_end {
+            data: [0; 1];
+
+            bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))];
+            message: "maximum usize";
+        }
+
+        in mod boundpair_overflow_start {
+            data: [0; 1];
+
+            bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)];
+            message: "maximum usize";
+        }
     } // panic_cases!
 }
 
index 3dcc5cd2b5911f1651734d7ab923b33419983b9c..bdf559847cc8511761d675be1de794e0e947ebe0 100644 (file)
@@ -2257,7 +2257,7 @@ impl<'a, K, V, F> fmt::Debug for DrainFilter<'a, K, V, F>
     F: FnMut(&K, &mut V) -> bool,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("DrainFilter { .. }")
+        f.debug_struct("DrainFilter").finish_non_exhaustive()
     }
 }
 
@@ -2957,7 +2957,7 @@ fn default() -> RandomState {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for RandomState {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("RandomState { .. }")
+        f.debug_struct("RandomState").finish_non_exhaustive()
     }
 }
 
index 8c801b9f1285db193e8af703a92e155ca5ae3b33..5220c8ad70977657a13778eb4a16c174af444417 100644 (file)
@@ -1533,7 +1533,7 @@ impl<'a, K, F> fmt::Debug for DrainFilter<'a, K, F>
     F: FnMut(&K) -> bool,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("DrainFilter { .. }")
+        f.debug_struct("DrainFilter").finish_non_exhaustive()
     }
 }
 
index 9763a2da341510aa3a00701fd6a6cf847ed3fc56..d20bb5858410129201362d5dcf1ff1a8bebc9bda 100644 (file)
@@ -154,7 +154,7 @@ fn size_hint(&self) -> (usize, Option<usize>) {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Vars {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Vars { .. }")
+        f.debug_struct("Vars").finish_non_exhaustive()
     }
 }
 
@@ -172,7 +172,7 @@ fn size_hint(&self) -> (usize, Option<usize>) {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for VarsOs {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("VarsOs { .. }")
+        f.debug_struct("VarOs").finish_non_exhaustive()
     }
 }
 
@@ -419,7 +419,7 @@ fn size_hint(&self) -> (usize, Option<usize>) {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for SplitPaths<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("SplitPaths { .. }")
+        f.debug_struct("SplitPaths").finish_non_exhaustive()
     }
 }
 
@@ -710,14 +710,14 @@ pub struct ArgsOs {
 /// passed as-is.
 ///
 /// On glibc Linux systems, arguments are retrieved by placing a function in `.init_array`.
-/// Glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard
+/// glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard
 /// extension. This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it
 /// does on macOS and Windows.
 ///
 /// # Panics
 ///
 /// The returned iterator will panic during iteration if any argument to the
-/// process is not valid unicode. If this is not desired,
+/// process is not valid Unicode. If this is not desired,
 /// use the [`args_os`] function instead.
 ///
 /// # Examples
@@ -735,17 +735,25 @@ pub fn args() -> Args {
     Args { inner: args_os() }
 }
 
-/// Returns the arguments which this program was started with (normally passed
+/// Returns the arguments that this program was started with (normally passed
 /// via the command line).
 ///
 /// The first element is traditionally the path of the executable, but it can be
-/// set to arbitrary text, and it may not even exist, so this property should
+/// set to arbitrary text, and may not even exist. This means this property should
 /// not be relied upon for security purposes.
 ///
-/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array".
-/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension.
-/// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS
-/// and Windows.
+/// On Unix systems the shell usually expands unquoted arguments with glob patterns
+/// (such as `*` and `?`). On Windows this is not done, and such arguments are
+/// passed as-is.
+///
+/// On glibc Linux systems, arguments are retrieved by placing a function in `.init_array`.
+/// glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard
+/// extension. This allows `std::env::args_os` to work even in a `cdylib` or `staticlib`, as it
+/// does on macOS and Windows.
+///
+/// Note that the returned iterator will not check if the arguments to the
+/// process are valid Unicode. If you want to panic on invalid UTF-8,
+/// use the [`args`] function instead.
 ///
 /// # Examples
 ///
@@ -799,7 +807,7 @@ fn next_back(&mut self) -> Option<String> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Args {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Args").field("inner", &self.inner.inner.inner_debug()).finish()
+        f.debug_struct("Args").field("inner", &self.inner.inner).finish()
     }
 }
 
@@ -840,7 +848,7 @@ fn next_back(&mut self) -> Option<OsString> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for ArgsOs {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("ArgsOs").field("inner", &self.inner.inner_debug()).finish()
+        f.debug_struct("ArgsOs").field("inner", &self.inner).finish()
     }
 }
 
index 5c969741592e62179b267247b9a2609e0c7cf772..ce8d3a56f7af4bff6e395c39a4075b49d9562747 100644 (file)
@@ -1329,7 +1329,9 @@ fn metadata_access_times() {
         match (a.created(), b.created()) {
             (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
             (Err(e1), Err(e2))
-                if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {}
+                if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other
+                    || e1.kind() == ErrorKind::Unsupported
+                        && e2.kind() == ErrorKind::Unsupported => {}
             (a, b) => {
                 panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,)
             }
index 97c92aa35069642012d2bcd2350985d4ad0cf553..9bed12bf2ae2b1c47030e111ffb39163a14e04e3 100644 (file)
@@ -180,6 +180,12 @@ pub enum ErrorKind {
     /// read.
     #[stable(feature = "read_exact", since = "1.6.0")]
     UnexpectedEof,
+
+    /// This operation is unsupported on this platform.
+    ///
+    /// This means that the operation can never succeed.
+    #[stable(feature = "unsupported_error", since = "1.53.0")]
+    Unsupported,
 }
 
 impl ErrorKind {
@@ -203,6 +209,7 @@ pub(crate) fn as_str(&self) -> &'static str {
             ErrorKind::Interrupted => "operation interrupted",
             ErrorKind::Other => "other os error",
             ErrorKind::UnexpectedEof => "unexpected end of file",
+            ErrorKind::Unsupported => "unsupported",
         }
     }
 }
index 9625984195bd2cc5ef64777bc43f07ae04392320..94c70c4f267b16a82892d0221428040c52c8a729 100644 (file)
 
 const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
 
+pub(crate) fn cleanup() {
+    stdio::cleanup()
+}
+
 struct Guard<'a> {
     buf: &'a mut Vec<u8>,
     len: usize,
index 1e72c9e0611eaf565bb4d82670fdecdc538e1e93..2b0d2b7e0be23e87e3a674591c78d535c699589e 100644 (file)
@@ -13,7 +13,6 @@
 use crate::sync::atomic::{AtomicBool, Ordering};
 use crate::sync::{Arc, Mutex, MutexGuard};
 use crate::sys::stdio;
-use crate::sys_common;
 use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
 
 type LocalStream = Arc<Mutex<Vec<u8>>>;
@@ -373,7 +372,7 @@ pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Stdin {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Stdin { .. }")
+        f.debug_struct("Stdin").finish_non_exhaustive()
     }
 }
 
@@ -467,7 +466,7 @@ fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for StdinLock<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("StdinLock { .. }")
+        f.debug_struct("StdinLock").finish_non_exhaustive()
     }
 }
 
@@ -508,6 +507,8 @@ pub struct StdoutLock<'a> {
     inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
 }
 
+static STDOUT: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = SyncOnceCell::new();
+
 /// Constructs a new handle to the standard output of the current process.
 ///
 /// Each handle returned is a reference to a shared global buffer whose access
@@ -549,34 +550,28 @@ pub struct StdoutLock<'a> {
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn stdout() -> Stdout {
-    static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
-        SyncOnceCell::new();
-
-    fn cleanup() {
-        if let Some(instance) = INSTANCE.get() {
-            // Flush the data and disable buffering during shutdown
-            // by replacing the line writer by one with zero
-            // buffering capacity.
-            // We use try_lock() instead of lock(), because someone
-            // might have leaked a StdoutLock, which would
-            // otherwise cause a deadlock here.
-            if let Some(lock) = Pin::static_ref(instance).try_lock() {
-                *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
-            }
-        }
-    }
-
     Stdout {
-        inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
-            || unsafe {
-                let _ = sys_common::at_exit(cleanup);
-                ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))
-            },
+        inner: Pin::static_ref(&STDOUT).get_or_init_pin(
+            || unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) },
             |mutex| unsafe { mutex.init() },
         ),
     }
 }
 
+pub fn cleanup() {
+    if let Some(instance) = STDOUT.get() {
+        // Flush the data and disable buffering during shutdown
+        // by replacing the line writer by one with zero
+        // buffering capacity.
+        // We use try_lock() instead of lock(), because someone
+        // might have leaked a StdoutLock, which would
+        // otherwise cause a deadlock here.
+        if let Some(lock) = Pin::static_ref(instance).try_lock() {
+            *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
+        }
+    }
+}
+
 impl Stdout {
     /// Locks this handle to the standard output stream, returning a writable
     /// guard.
@@ -607,7 +602,7 @@ pub fn lock(&self) -> StdoutLock<'_> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Stdout {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Stdout { .. }")
+        f.debug_struct("Stdout").finish_non_exhaustive()
     }
 }
 
@@ -689,7 +684,7 @@ fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for StdoutLock<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("StdoutLock { .. }")
+        f.debug_struct("StdoutLock").finish_non_exhaustive()
     }
 }
 
@@ -804,7 +799,7 @@ pub fn lock(&self) -> StderrLock<'_> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Stderr {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Stderr { .. }")
+        f.debug_struct("Stderr").finish_non_exhaustive()
     }
 }
 
@@ -886,7 +881,7 @@ fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for StderrLock<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("StderrLock { .. }")
+        f.debug_struct("StderrLock").finish_non_exhaustive()
     }
 }
 
index f472361f916db3f0615e92b732791f8c75ad53d8..73f2f3eb3f5dc9ec50265a782b9a93836c0a2072 100644 (file)
@@ -78,7 +78,7 @@ fn stream_position(&mut self) -> io::Result<u64> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Empty {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Empty { .. }")
+        f.debug_struct("Empty").finish_non_exhaustive()
     }
 }
 
@@ -150,7 +150,7 @@ unsafe fn initializer(&self) -> Initializer {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Repeat {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Repeat { .. }")
+        f.debug_struct("Repeat").finish_non_exhaustive()
     }
 }
 
@@ -236,6 +236,6 @@ fn flush(&mut self) -> io::Result<()> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Sink {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Sink { .. }")
+        f.debug_struct("Sink").finish_non_exhaustive()
     }
 }
index 90603cd983677170eec54427e938a6b92fe20fc4..0ab9f490fd4202b4f2e339fd612c99c06bdcdfad 100644 (file)
 // std may use features in a platform-specific way
 #![allow(unused_features)]
 #![feature(rustc_allow_const_fn_unstable)]
-#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))]
+#![cfg_attr(
+    test,
+    feature(internal_output_capture, print_internals, update_panic_count, thread_local_const_init)
+)]
 #![cfg_attr(
     all(target_vendor = "fortanix", target_env = "sgx"),
     feature(slice_index_methods, coerce_unsized, sgx_platform)
 #![feature(assert_matches)]
 #![feature(associated_type_bounds)]
 #![feature(atomic_mut_ptr)]
+#![feature(bench_black_box)]
 #![feature(box_syntax)]
 #![feature(c_variadic)]
 #![feature(cfg_accessible)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::ptr;
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated, deprecated_in_future)]
 pub use core::raw;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::result;
index da2415e361077606abda1e2abface5b1f23651a8..9b629e19be53d2ed01600560cd32cf7caaba63e2 100644 (file)
@@ -334,6 +334,8 @@ pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
 
     /// An IPv4 address representing an unspecified address: 0.0.0.0
     ///
+    /// This corresponds to the constant `INADDR_ANY` in other languages.
+    ///
     /// # Examples
     ///
     /// ```
@@ -342,6 +344,7 @@ pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
     /// let addr = Ipv4Addr::UNSPECIFIED;
     /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0));
     /// ```
+    #[doc(alias = "INADDR_ANY")]
     #[stable(feature = "ip_constructors", since = "1.30.0")]
     pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0);
 
index 64b22b64f4bf12cc840705a32200735d665df35a..c37111f665c4745ce76db1158705db32f2d1ed54 100644 (file)
@@ -498,7 +498,7 @@ mod prim_pointer {}
 /// - [`Copy`]
 /// - [`Clone`]
 /// - [`Debug`]
-/// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`)
+/// - [`IntoIterator`] (implemented for `[T; N]`, `&[T; N]` and `&mut [T; N]`)
 /// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
 /// - [`Hash`]
 /// - [`AsRef`], [`AsMut`]
@@ -517,7 +517,8 @@ mod prim_pointer {}
 ///
 /// # Examples
 ///
-/// ```
+#[cfg_attr(bootstrap, doc = "```ignore")]
+#[cfg_attr(not(bootstrap), doc = "```")]
 /// let mut array: [i32; 3] = [0; 3];
 ///
 /// array[1] = 1;
@@ -526,31 +527,16 @@ mod prim_pointer {}
 /// assert_eq!([1, 2], &array[1..]);
 ///
 /// // This loop prints: 0 1 2
-/// for x in &array {
+/// for x in array {
 ///     print!("{} ", x);
 /// }
 /// ```
 ///
-/// An array itself is not iterable:
-///
-/// ```compile_fail,E0277
-/// let array: [i32; 3] = [0; 3];
-///
-/// for x in array { }
-/// // error: the trait bound `[i32; 3]: std::iter::Iterator` is not satisfied
-/// ```
-///
-/// The solution is to coerce the array to a slice by calling a slice method:
+/// You can also iterate over reference to the array's elements:
 ///
 /// ```
-/// # let array: [i32; 3] = [0; 3];
-/// for x in array.iter() { }
-/// ```
-///
-/// You can also use the array reference's [`IntoIterator`] implementation:
+/// let array: [i32; 3] = [0; 3];
 ///
-/// ```
-/// # let array: [i32; 3] = [0; 3];
 /// for x in &array { }
 /// ```
 ///
@@ -564,6 +550,57 @@ mod prim_pointer {}
 /// move_away(roa);
 /// ```
 ///
+/// # Editions
+///
+/// Prior to Rust 1.53, arrays did not implement `IntoIterator` by value, so the method call
+/// `array.into_iter()` auto-referenced into a slice iterator. That behavior is preserved in the
+/// 2015 and 2018 editions of Rust for compatability, ignoring `IntoIterator` by value.
+///
+#[cfg_attr(bootstrap, doc = "```rust,edition2018,ignore")]
+#[cfg_attr(not(bootstrap), doc = "```rust,edition2018")]
+/// # #![allow(array_into_iter)] // override our `deny(warnings)`
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This creates a slice iterator, producing references to each value.
+/// for item in array.into_iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // The `array_into_iter` lint suggests this change for future compatibility:
+/// for item in array.iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // You can explicitly iterate an array by value using
+/// // `IntoIterator::into_iter` or `std::array::IntoIter::new`:
+/// for item in IntoIterator::into_iter(array).enumerate() {
+///     let (i, x): (usize, i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
+/// Starting in the 2021 edition, `array.into_iter()` will use `IntoIterator` normally to iterate
+/// by value, and `iter()` should be used to iterate by reference like previous editions.
+///
+/// ```rust,edition2021,ignore
+/// # // FIXME: ignored because 2021 testing is still unstable
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This iterates by reference:
+/// for item in array.iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // This iterates by value:
+/// for item in array.into_iter().enumerate() {
+///     let (i, x): (usize, i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
 /// [slice]: prim@slice
 /// [`Debug`]: fmt::Debug
 /// [`Hash`]: hash::Hash
@@ -807,10 +844,10 @@ mod prim_tuple {}
 ///
 /// Additionally, `f32` can represent some special values:
 ///
-/// - -0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so -0.0 is a
-///   possible value. For comparison `-0.0 == +0.0` is true but floating point operations can
-///   carry the sign bit through arithmetic operations. This means `-1.0 * 0.0` produces -0.0 and
-///   a negative number rounded to a value smaller than a float can represent also produces -0.0.
+/// - −0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so −0.0 is a
+///   possible value. For comparison −0.0 = +0.0, but floating point operations can carry
+///   the sign bit through arithmetic operations. This means −0.0 × +0.0 produces −0.0 and
+///   a negative number rounded to a value smaller than a float can represent also produces 0.0.
 /// - [∞](#associatedconstant.INFINITY) and
 ///   [−∞](#associatedconstant.NEG_INFINITY): these result from calculations
 ///   like `1.0 / 0.0`.
index 5690de681cab94b677c55e12e947f29f7ddd5a68..b45c620fd0b068e25fbb2d27fb046a8f13a3bd30 100644 (file)
@@ -312,7 +312,7 @@ fn from_inner(pipe: AnonPipe) -> ChildStdin {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for ChildStdin {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("ChildStdin { .. }")
+        f.debug_struct("ChildStdin").finish_non_exhaustive()
     }
 }
 
@@ -373,7 +373,7 @@ fn from_inner(pipe: AnonPipe) -> ChildStdout {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for ChildStdout {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("ChildStdout { .. }")
+        f.debug_struct("ChildStdout").finish_non_exhaustive()
     }
 }
 
@@ -434,7 +434,7 @@ fn from_inner(pipe: AnonPipe) -> ChildStderr {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for ChildStderr {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("ChildStderr { .. }")
+        f.debug_struct("ChildStderr").finish_non_exhaustive()
     }
 }
 
@@ -1257,7 +1257,7 @@ fn from_inner(inner: imp::Stdio) -> Stdio {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Stdio {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Stdio { .. }")
+        f.debug_struct("Stdio").finish_non_exhaustive()
     }
 }
 
@@ -1749,7 +1749,7 @@ pub fn wait_with_output(mut self) -> io::Result<Output> {
 /// [platform-specific behavior]: #platform-specific-behavior
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn exit(code: i32) -> ! {
-    crate::sys_common::cleanup();
+    crate::sys_common::rt::cleanup();
     crate::sys::os::exit(code)
 }
 
index 45af9f68a0f6b149c365f04b6ce6f4cea56d61f2..1e19aff51f8d6bb8054298096333b013590490f1 100644 (file)
@@ -26,33 +26,16 @@ fn lang_start_internal(
     argv: *const *const u8,
 ) -> isize {
     use crate::panic;
-    use crate::sys;
     use crate::sys_common;
-    use crate::sys_common::thread_info;
-    use crate::thread::Thread;
 
-    sys::init();
+    // SAFETY: Only called once during runtime initialization.
+    unsafe { sys_common::rt::init(argc, argv) };
 
-    unsafe {
-        let main_guard = sys::thread::guard::init();
-        sys::stack_overflow::init();
+    let exit_code = panic::catch_unwind(main);
 
-        // Next, set up the current Thread with the guard information we just
-        // created. Note that this isn't necessary in general for new threads,
-        // but we just do this to name the main thread and to give it correct
-        // info about the stack bounds.
-        let thread = Thread::new(Some("main".to_owned()));
-        thread_info::set(main_guard, thread);
+    sys_common::rt::cleanup();
 
-        // Store our args if necessary in a squirreled away location
-        sys::args::init(argc, argv);
-
-        // Let's run some code!
-        let exit_code = panic::catch_unwind(main);
-
-        sys_common::cleanup();
-        exit_code.unwrap_or(101) as isize
-    }
+    exit_code.unwrap_or(101) as isize
 }
 
 #[cfg(not(test))]
index eab26b6c7150ca69feba282d03556a66b8868f54..a17b82f82e8c2eb8e9c7529c7f152169727b5721 100644 (file)
@@ -60,7 +60,7 @@ struct BarrierState {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Barrier {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Barrier { .. }")
+        f.debug_struct("Barrier").finish_non_exhaustive()
     }
 }
 
index ffc1e57f4e03fa539309e54d93e85a28e418d0e0..2f0b32c90d0ee0a585190e264a452997dce0b903 100644 (file)
@@ -2,9 +2,8 @@
 mod tests;
 
 use crate::fmt;
-use crate::sync::{mutex, MutexGuard, PoisonError};
+use crate::sync::{mutex, poison, LockResult, MutexGuard, PoisonError};
 use crate::sys_common::condvar as sys;
-use crate::sys_common::poison::{self, LockResult};
 use crate::time::{Duration, Instant};
 
 /// A type indicating whether a timed wait on a condition variable returned
@@ -548,7 +547,7 @@ pub fn notify_all(&self) {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Condvar {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Condvar { .. }")
+        f.debug_struct("Condvar").finish_non_exhaustive()
     }
 }
 
index b6699910b07cf1801a761dbf05604e15f9a899c7..ee35598bab5fe1fb99b38155d4e770ffedf9a3bc 100644 (file)
 #[allow(deprecated)]
 pub use self::once::{Once, OnceState, ONCE_INIT};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
+pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use crate::sys_common::poison::{LockResult, PoisonError, TryLockError, TryLockResult};
+pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
 
 pub mod mpsc;
 
 mod condvar;
 mod mutex;
 mod once;
+mod poison;
 mod rwlock;
index c8f0a6b99fe6b37ce0c00bd6b5db590cec742a33..ea1d598d26461b4b10f1f3058f150b2e506c2bd9 100644 (file)
@@ -1477,7 +1477,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> fmt::Debug for SendError<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        "SendError(..)".fmt(f)
+        f.debug_struct("SendError").finish_non_exhaustive()
     }
 }
 
index 98c34282e0c49f6a303f4c4faf94efc7a3bcba06..773ab18b2ce6555a57ec88f3e908f2cc9cc26882 100644 (file)
@@ -3,11 +3,9 @@
 
 use crate::cell::UnsafeCell;
 use crate::fmt;
-use crate::mem;
 use crate::ops::{Deref, DerefMut};
-use crate::ptr;
+use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
 use crate::sys_common::mutex as sys;
-use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
 
 /// A mutual exclusion primitive useful for protecting shared data
 ///
@@ -376,23 +374,8 @@ pub fn into_inner(self) -> LockResult<T>
     where
         T: Sized,
     {
-        // We know statically that there are no outstanding references to
-        // `self` so there's no need to lock the inner mutex.
-        //
-        // To get the inner value, we'd like to call `data.into_inner()`,
-        // but because `Mutex` impl-s `Drop`, we can't move out of it, so
-        // we'll have to destructure it manually instead.
-        unsafe {
-            // Like `let Mutex { inner, poison, data } = self`.
-            let (inner, poison, data) = {
-                let Mutex { ref inner, ref poison, ref data } = self;
-                (ptr::read(inner), ptr::read(poison), ptr::read(data))
-            };
-            mem::forget(self);
-            drop(inner);
-
-            poison::map_result(poison.borrow(), |_| data.into_inner())
-        }
+        let data = self.data.into_inner();
+        poison::map_result(self.poison.borrow(), |_| data)
     }
 
     /// Returns a mutable reference to the underlying data.
index a24a5cb2ae39f0097cc73d5e9673833de80c3368..6da6c18e47799d26ce75b57f630d46c89f5e4d9b 100644 (file)
@@ -481,7 +481,7 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl fmt::Debug for Once {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Once { .. }")
+        f.debug_struct("Once").finish_non_exhaustive()
     }
 }
 
diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs
new file mode 100644 (file)
index 0000000..05e1833
--- /dev/null
@@ -0,0 +1,259 @@
+use crate::error::Error;
+use crate::fmt;
+use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::thread;
+
+pub struct Flag {
+    failed: AtomicBool,
+}
+
+// Note that the Ordering uses to access the `failed` field of `Flag` below is
+// always `Relaxed`, and that's because this isn't actually protecting any data,
+// it's just a flag whether we've panicked or not.
+//
+// The actual location that this matters is when a mutex is **locked** which is
+// where we have external synchronization ensuring that we see memory
+// reads/writes to this flag.
+//
+// As a result, if it matters, we should see the correct value for `failed` in
+// all cases.
+
+impl Flag {
+    pub const fn new() -> Flag {
+        Flag { failed: AtomicBool::new(false) }
+    }
+
+    #[inline]
+    pub fn borrow(&self) -> LockResult<Guard> {
+        let ret = Guard { panicking: thread::panicking() };
+        if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
+    }
+
+    #[inline]
+    pub fn done(&self, guard: &Guard) {
+        if !guard.panicking && thread::panicking() {
+            self.failed.store(true, Ordering::Relaxed);
+        }
+    }
+
+    #[inline]
+    pub fn get(&self) -> bool {
+        self.failed.load(Ordering::Relaxed)
+    }
+}
+
+pub struct Guard {
+    panicking: bool,
+}
+
+/// A type of error which can be returned whenever a lock is acquired.
+///
+/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock
+/// is held. The precise semantics for when a lock is poisoned is documented on
+/// each lock, but once a lock is poisoned then all future acquisitions will
+/// return this error.
+///
+/// # Examples
+///
+/// ```
+/// use std::sync::{Arc, Mutex};
+/// use std::thread;
+///
+/// let mutex = Arc::new(Mutex::new(1));
+///
+/// // poison the mutex
+/// let c_mutex = Arc::clone(&mutex);
+/// let _ = thread::spawn(move || {
+///     let mut data = c_mutex.lock().unwrap();
+///     *data = 2;
+///     panic!();
+/// }).join();
+///
+/// match mutex.lock() {
+///     Ok(_) => unreachable!(),
+///     Err(p_err) => {
+///         let data = p_err.get_ref();
+///         println!("recovered: {}", data);
+///     }
+/// };
+/// ```
+/// [`Mutex`]: crate::sync::Mutex
+/// [`RwLock`]: crate::sync::RwLock
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct PoisonError<T> {
+    guard: T,
+}
+
+/// An enumeration of possible errors associated with a [`TryLockResult`] which
+/// 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`].
+///
+/// [`try_lock`]: crate::sync::Mutex::try_lock
+/// [`try_read`]: crate::sync::RwLock::try_read
+/// [`try_write`]: crate::sync::RwLock::try_write
+/// [`Mutex`]: crate::sync::Mutex
+/// [`RwLock`]: crate::sync::RwLock
+#[stable(feature = "rust1", since = "1.0.0")]
+pub enum TryLockError<T> {
+    /// The lock could not be acquired because another thread failed while holding
+    /// the lock.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError<T>),
+    /// The lock could not be acquired at this time because the operation would
+    /// otherwise block.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    WouldBlock,
+}
+
+/// A type alias for the result of a lock method which can be poisoned.
+///
+/// The [`Ok`] variant of this result indicates that the primitive was not
+/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates
+/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries
+/// the associated guard, and it can be acquired through the [`into_inner`]
+/// method.
+///
+/// [`into_inner`]: PoisonError::into_inner
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
+
+/// A type alias for the result of a nonblocking locking method.
+///
+/// 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.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Debug for PoisonError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("PoisonError").finish_non_exhaustive()
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Display for PoisonError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "poisoned lock: another task failed inside".fmt(f)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> Error for PoisonError<T> {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "poisoned lock: another task failed inside"
+    }
+}
+
+impl<T> PoisonError<T> {
+    /// Creates a `PoisonError`.
+    ///
+    /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock)
+    /// or [`RwLock::read`](crate::sync::RwLock::read).
+    #[stable(feature = "sync_poison", since = "1.2.0")]
+    pub fn new(guard: T) -> PoisonError<T> {
+        PoisonError { guard }
+    }
+
+    /// Consumes this error indicating that a lock is poisoned, returning the
+    /// underlying guard to allow access regardless.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::collections::HashSet;
+    /// use std::sync::{Arc, Mutex};
+    /// use std::thread;
+    ///
+    /// let mutex = Arc::new(Mutex::new(HashSet::new()));
+    ///
+    /// // poison the mutex
+    /// let c_mutex = Arc::clone(&mutex);
+    /// let _ = thread::spawn(move || {
+    ///     let mut data = c_mutex.lock().unwrap();
+    ///     data.insert(10);
+    ///     panic!();
+    /// }).join();
+    ///
+    /// let p_err = mutex.lock().unwrap_err();
+    /// let data = p_err.into_inner();
+    /// println!("recovered {} items", data.len());
+    /// ```
+    #[stable(feature = "sync_poison", since = "1.2.0")]
+    pub fn into_inner(self) -> T {
+        self.guard
+    }
+
+    /// Reaches into this error indicating that a lock is poisoned, returning a
+    /// reference to the underlying guard to allow access regardless.
+    #[stable(feature = "sync_poison", since = "1.2.0")]
+    pub fn get_ref(&self) -> &T {
+        &self.guard
+    }
+
+    /// Reaches into this error indicating that a lock is poisoned, returning a
+    /// mutable reference to the underlying guard to allow access regardless.
+    #[stable(feature = "sync_poison", since = "1.2.0")]
+    pub fn get_mut(&mut self) -> &mut T {
+        &mut self.guard
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> From<PoisonError<T>> for TryLockError<T> {
+    fn from(err: PoisonError<T>) -> TryLockError<T> {
+        TryLockError::Poisoned(err)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Debug for TryLockError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
+            TryLockError::WouldBlock => "WouldBlock".fmt(f),
+        }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Display for TryLockError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
+            TryLockError::WouldBlock => "try_lock failed because the operation would block",
+        }
+        .fmt(f)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> Error for TryLockError<T> {
+    #[allow(deprecated, deprecated_in_future)]
+    fn description(&self) -> &str {
+        match *self {
+            TryLockError::Poisoned(ref p) => p.description(),
+            TryLockError::WouldBlock => "try_lock failed because the operation would block",
+        }
+    }
+
+    #[allow(deprecated)]
+    fn cause(&self) -> Option<&dyn Error> {
+        match *self {
+            TryLockError::Poisoned(ref p) => Some(p),
+            _ => None,
+        }
+    }
+}
+
+pub fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
+where
+    F: FnOnce(T) -> U,
+{
+    match result {
+        Ok(t) => Ok(f(t)),
+        Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))),
+    }
+}
index 351804ec979e1b3b5b8224996882aa96bcdaed32..b01bcec1361d7c7986f94be9e856c5ad1ece88ed 100644 (file)
@@ -6,7 +6,7 @@
 use crate::mem;
 use crate::ops::{Deref, DerefMut};
 use crate::ptr;
-use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
+use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
 use crate::sys_common::rwlock as sys;
 
 /// A reader-writer lock
index 7727293927282b34e8b1afb750ec36faffbd93d1..4eb0d8437ba5f5f7ef8ab050689853d5d5738fdb 100644 (file)
@@ -1,5 +1,5 @@
 use crate::ffi::OsString;
-use crate::marker::PhantomData;
+use crate::fmt;
 use crate::vec;
 
 /// One-time global initialization.
@@ -19,15 +19,17 @@ pub fn args() -> Args {
 
 pub struct Args {
     iter: vec::IntoIter<OsString>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
-impl Args {
-    pub fn inner_debug(&self) -> &[OsString] {
-        self.iter.as_slice()
+impl fmt::Debug for Args {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.iter.as_slice().fmt(f)
     }
 }
 
+impl !Send for Args {}
+impl !Sync for Args {}
+
 impl Iterator for Args {
     type Item = OsString;
     fn next(&mut self) -> Option<OsString> {
@@ -53,7 +55,6 @@ fn next_back(&mut self) -> Option<OsString> {
 mod imp {
     use super::Args;
     use crate::ffi::{CStr, OsString};
-    use crate::marker::PhantomData;
     use crate::ptr;
     use crate::sys_common::os_str_bytes::*;
 
@@ -76,7 +77,7 @@ pub unsafe fn cleanup() {
     }
 
     pub fn args() -> Args {
-        Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData }
+        Args { iter: clone().into_iter() }
     }
 
     fn clone() -> Vec<OsString> {
index 1c0515a150398910ffc6f1450cbf2512acc61570..c400f5f2c2e8444edd72f816d764b7d343720426 100644 (file)
@@ -1,9 +1,10 @@
 #![unstable(reason = "not public", issue = "none", feature = "fd")]
 
-use crate::io::{self, ErrorKind, Read};
+use crate::io::{self, Read};
 use crate::mem;
 use crate::sys::cvt;
 use crate::sys::hermit::abi;
+use crate::sys::unsupported;
 use crate::sys_common::AsInner;
 
 #[derive(Debug)]
@@ -46,7 +47,7 @@ pub fn duplicate(&self) -> io::Result<FileDesc> {
         self.duplicate_path(&[])
     }
     pub fn duplicate_path(&self, _path: &[u8]) -> io::Result<FileDesc> {
-        Err(io::Error::new_const(ErrorKind::Other, &"duplicate isn't supported"))
+        unsupported()
     }
 
     pub fn nonblocking(&self) -> io::Result<bool> {
@@ -54,11 +55,11 @@ pub fn nonblocking(&self) -> io::Result<bool> {
     }
 
     pub fn set_cloexec(&self) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"cloexec isn't supported"))
+        unsupported()
     }
 
     pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"nonblocking isn't supported"))
+        unsupported()
     }
 }
 
index 0f33282fa8370514fa18bf76b6f32a5457be49f1..5b3f2fa4e8275e19e208fd1743c408a3d6ab70cb 100644 (file)
@@ -9,7 +9,7 @@
 use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
 use crate::sys::hermit::fd::FileDesc;
 use crate::sys::time::SystemTime;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::sys_common::os_str_bytes::OsStrExt;
 
 pub use crate::sys_common::fs::copy;
@@ -22,11 +22,11 @@ fn cstr(path: &Path) -> io::Result<CString> {
 #[derive(Debug)]
 pub struct File(FileDesc);
 
-pub struct FileAttr(Void);
+pub struct FileAttr(!);
 
-pub struct ReadDir(Void);
+pub struct ReadDir(!);
 
-pub struct DirEntry(Void);
+pub struct DirEntry(!);
 
 #[derive(Clone, Debug)]
 pub struct OpenOptions {
@@ -41,64 +41,64 @@ pub struct OpenOptions {
     mode: i32,
 }
 
-pub struct FilePermissions(Void);
+pub struct FilePermissions(!);
 
-pub struct FileType(Void);
+pub struct FileType(!);
 
 #[derive(Debug)]
 pub struct DirBuilder {}
 
 impl FileAttr {
     pub fn size(&self) -> u64 {
-        match self.0 {}
+        self.0
     }
 
     pub fn perm(&self) -> FilePermissions {
-        match self.0 {}
+        self.0
     }
 
     pub fn file_type(&self) -> FileType {
-        match self.0 {}
+        self.0
     }
 
     pub fn modified(&self) -> io::Result<SystemTime> {
-        match self.0 {}
+        self.0
     }
 
     pub fn accessed(&self) -> io::Result<SystemTime> {
-        match self.0 {}
+        self.0
     }
 
     pub fn created(&self) -> io::Result<SystemTime> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Clone for FileAttr {
     fn clone(&self) -> FileAttr {
-        match self.0 {}
+        self.0
     }
 }
 
 impl FilePermissions {
     pub fn readonly(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_readonly(&mut self, _readonly: bool) {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Clone for FilePermissions {
     fn clone(&self) -> FilePermissions {
-        match self.0 {}
+        self.0
     }
 }
 
 impl PartialEq for FilePermissions {
     fn eq(&self, _other: &FilePermissions) -> bool {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -106,27 +106,27 @@ impl Eq for FilePermissions {}
 
 impl fmt::Debug for FilePermissions {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
 impl FileType {
     pub fn is_dir(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_file(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_symlink(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Clone for FileType {
     fn clone(&self) -> FileType {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -134,7 +134,7 @@ impl Copy for FileType {}
 
 impl PartialEq for FileType {
     fn eq(&self, _other: &FileType) -> bool {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -142,19 +142,19 @@ impl Eq for FileType {}
 
 impl Hash for FileType {
     fn hash<H: Hasher>(&self, _h: &mut H) {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Debug for FileType {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Debug for ReadDir {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -162,25 +162,25 @@ impl Iterator for ReadDir {
     type Item = io::Result<DirEntry>;
 
     fn next(&mut self) -> Option<io::Result<DirEntry>> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl DirEntry {
     pub fn path(&self) -> PathBuf {
-        match self.0 {}
+        self.0
     }
 
     pub fn file_name(&self) -> OsString {
-        match self.0 {}
+        self.0
     }
 
     pub fn metadata(&self) -> io::Result<FileAttr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn file_type(&self) -> io::Result<FileType> {
-        match self.0 {}
+        self.0
     }
 }
 
diff --git a/library/std/src/sys/hermit/io.rs b/library/std/src/sys/hermit/io.rs
deleted file mode 100644 (file)
index d5f475b..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-use crate::mem;
-
-#[derive(Copy, Clone)]
-pub struct IoSlice<'a>(&'a [u8]);
-
-impl<'a> IoSlice<'a> {
-    #[inline]
-    pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
-        IoSlice(buf)
-    }
-
-    #[inline]
-    pub fn advance(&mut self, n: usize) {
-        self.0 = &self.0[n..]
-    }
-
-    #[inline]
-    pub fn as_slice(&self) -> &[u8] {
-        self.0
-    }
-}
-
-pub struct IoSliceMut<'a>(&'a mut [u8]);
-
-impl<'a> IoSliceMut<'a> {
-    #[inline]
-    pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
-        IoSliceMut(buf)
-    }
-
-    #[inline]
-    pub fn advance(&mut self, n: usize) {
-        let slice = mem::replace(&mut self.0, &mut []);
-        let (_, remaining) = slice.split_at_mut(n);
-        self.0 = remaining;
-    }
-
-    #[inline]
-    pub fn as_slice(&self) -> &[u8] {
-        self.0
-    }
-
-    #[inline]
-    pub fn as_mut_slice(&mut self) -> &mut [u8] {
-        self.0
-    }
-}
index 1ecda25c03de5ca05ddc8eed8e2041bb4294fffa..3364d215776693a133c05eed8d9c6005852c378e 100644 (file)
 pub mod ext;
 pub mod fd;
 pub mod fs;
+#[path = "../unsupported/io.rs"]
 pub mod io;
 pub mod memchr;
 pub mod mutex;
 pub mod net;
 pub mod os;
+#[path = "../unix/path.rs"]
 pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
 pub mod process;
 pub mod rwlock;
-pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
 pub mod thread_local_dtor;
+#[path = "../unsupported/thread_local_key.rs"]
 pub mod thread_local_key;
 pub mod time;
 
@@ -56,16 +58,11 @@ pub fn unsupported<T>() -> crate::io::Result<T> {
 
 pub fn unsupported_err() -> crate::io::Error {
     crate::io::Error::new_const(
-        crate::io::ErrorKind::Other,
+        crate::io::ErrorKind::Unsupported,
         &"operation not supported on HermitCore yet",
     )
 }
 
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
 pub unsafe fn strlen(start: *const c_char) -> usize {
     let mut str = start;
 
@@ -101,9 +98,17 @@ pub extern "C" fn __rust_abort() {
     abort_internal();
 }
 
-#[cfg(not(test))]
-pub fn init() {
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
     let _ = net::init();
+    args::init(argc, argv);
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+    args::cleanup();
 }
 
 #[cfg(not(test))]
index 7053487ccfbe02671eab2c1bdbb3bbfb647ed7a1..5f8839157eafc0b5b6af665cc6f6a4a7d7379747 100644 (file)
@@ -6,7 +6,7 @@
 use crate::sync::Arc;
 use crate::sys::hermit::abi;
 use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6};
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::sys_common::AsInner;
 use crate::time::Duration;
 
@@ -166,7 +166,7 @@ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
     }
 
     pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        Err(io::Error::new_const(ErrorKind::Other, &"socket_addr isn't supported"))
+        unsupported()
     }
 
     pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
@@ -199,7 +199,7 @@ pub fn ttl(&self) -> io::Result<u32> {
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        Err(io::Error::new_const(ErrorKind::Other, &"take_error isn't supported"))
+        unsupported()
     }
 
     pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
@@ -247,27 +247,27 @@ pub fn duplicate(&self) -> io::Result<TcpListener> {
     }
 
     pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn ttl(&self) -> io::Result<u32> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn only_v6(&self) -> io::Result<bool> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 }
 
@@ -281,127 +281,127 @@ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 impl UdpSocket {
     pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn duplicate(&self) -> io::Result<UdpSocket> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn broadcast(&self) -> io::Result<bool> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn multicast_loop_v4(&self) -> io::Result<bool> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn multicast_loop_v6(&self) -> io::Result<bool> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn ttl(&self) -> io::Result<u32> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn send(&self, _: &[u8]) -> io::Result<usize> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 
     pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
-        Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
+        unsupported()
     }
 }
 
@@ -411,18 +411,18 @@ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-pub struct LookupHost(Void);
+pub struct LookupHost(!);
 
 impl LookupHost {
     pub fn port(&self) -> u16 {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Iterator for LookupHost {
     type Item = SocketAddr;
     fn next(&mut self) -> Option<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 }
 
index 78eabf8f81e984e149bd551efed65503381eed02..81cd68a74e6654e85ba8d81b0c8fa83d7d654af9 100644 (file)
@@ -9,7 +9,7 @@
 use crate::str;
 use crate::sync::Mutex;
 use crate::sys::hermit::abi;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::sys_common::os_str_bytes::*;
 use crate::vec;
 
@@ -29,7 +29,7 @@ pub fn chdir(_: &path::Path) -> io::Result<()> {
     unsupported()
 }
 
-pub struct SplitPaths<'a>(&'a Void);
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
 
 pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
     panic!("unsupported")
@@ -38,7 +38,7 @@ pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
 impl<'a> Iterator for SplitPaths<'a> {
     type Item = PathBuf;
     fn next(&mut self) -> Option<PathBuf> {
-        match *self.0 {}
+        self.0
     }
 }
 
@@ -110,9 +110,11 @@ fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
 
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Env {}
+impl !Sync for Env {}
+
 impl Iterator for Env {
     type Item = (OsString, OsString);
     fn next(&mut self) -> Option<(OsString, OsString)> {
@@ -134,7 +136,7 @@ pub fn env() -> Env {
             result.push((key.clone(), value.clone()));
         }
 
-        return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+        return Env { iter: result.into_iter() };
     }
 }
 
diff --git a/library/std/src/sys/hermit/path.rs b/library/std/src/sys/hermit/path.rs
deleted file mode 100644 (file)
index 840a7ae..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-use crate::ffi::OsStr;
-use crate::path::Prefix;
-
-#[inline]
-pub fn is_sep_byte(b: u8) -> bool {
-    b == b'/'
-}
-
-#[inline]
-pub fn is_verbatim_sep(b: u8) -> bool {
-    b == b'/'
-}
-
-pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
-    None
-}
-
-pub const MAIN_SEP_STR: &str = "/";
-pub const MAIN_SEP: char = '/';
diff --git a/library/std/src/sys/hermit/stack_overflow.rs b/library/std/src/sys/hermit/stack_overflow.rs
deleted file mode 100644 (file)
index 121fe42..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#[inline]
-pub unsafe fn init() {}
-
-#[inline]
-pub unsafe fn cleanup() {}
diff --git a/library/std/src/sys/hermit/thread_local_key.rs b/library/std/src/sys/hermit/thread_local_key.rs
deleted file mode 100644 (file)
index bf1b49e..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-pub type Key = usize;
-
-#[inline]
-pub unsafe fn create(_dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
-    panic!("should not be used on the hermit target");
-}
-
-#[inline]
-pub unsafe fn set(_key: Key, _value: *mut u8) {
-    panic!("should not be used on the hermit target");
-}
-
-#[inline]
-pub unsafe fn get(_key: Key) -> *mut u8 {
-    panic!("should not be used on the hermit target");
-}
-
-#[inline]
-pub unsafe fn destroy(_key: Key) {
-    panic!("should not be used on the hermit target");
-}
-
-#[inline]
-pub fn requires_synchronized_create() -> bool {
-    panic!("should not be used on the hermit target");
-}
index 50c2660ebcf1f3e2ac6b775f3585f61565da2c6a..2450a7aac5ede3b02fd34cf7324c75661d59f735 100644 (file)
 mod common;
 
 cfg_if::cfg_if! {
-    if #[cfg(target_os = "vxworks")] {
-        mod vxworks;
-        pub use self::vxworks::*;
-    } else if #[cfg(unix)] {
+    if #[cfg(unix)] {
         mod unix;
         pub use self::unix::*;
     } else if #[cfg(windows)] {
index 2d2e692ec7d35e09346d12053ed81dbd62cbcb13..ef4176c4ac0f0caa3b7573b2ff530ffba30f7b1a 100644 (file)
@@ -1,5 +1,6 @@
 use super::abi::usercalls::{alloc, raw::ByteBuffer};
 use crate::ffi::OsString;
+use crate::fmt;
 use crate::slice;
 use crate::sync::atomic::{AtomicUsize, Ordering};
 use crate::sys::os_str::Buf;
@@ -22,8 +23,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
     }
 }
 
-pub unsafe fn cleanup() {}
-
 pub fn args() -> Args {
     let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() };
     if let Some(args) = args { Args(args.iter()) } else { Args([].iter()) }
@@ -31,9 +30,9 @@ pub fn args() -> Args {
 
 pub struct Args(slice::Iter<'static, OsString>);
 
-impl Args {
-    pub fn inner_debug(&self) -> &[OsString] {
-        self.0.as_slice()
+impl fmt::Debug for Args {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0.as_slice().fmt(f)
     }
 }
 
index 795a4d190cf4ebab7c060b53fa323a7079f5ad28..7223ade68158fd1a84a489437462ae81cb73b07b 100644 (file)
@@ -63,12 +63,14 @@ pub trait TryIntoRawFd: Sized {
 }
 
 impl AsRawFd for net::TcpStream {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         *self.as_inner().as_inner().as_inner().as_inner()
     }
 }
 
 impl AsRawFd for net::TcpListener {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         *self.as_inner().as_inner().as_inner().as_inner()
     }
@@ -87,6 +89,7 @@ pub struct TcpStreamMetadata {
 impl FromRawFd for net::TcpStream {
     type Metadata = TcpStreamMetadata;
 
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpStream {
         let fd = sys::fd::FileDesc::from_inner(fd);
         let socket = sys::net::Socket::from_inner((fd, metadata.local_addr));
@@ -105,6 +108,7 @@ pub struct TcpListenerMetadata {
 impl FromRawFd for net::TcpListener {
     type Metadata = TcpListenerMetadata;
 
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpListener {
         let fd = sys::fd::FileDesc::from_inner(fd);
         let socket = sys::net::Socket::from_inner((fd, metadata.local_addr));
@@ -113,6 +117,7 @@ unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpListener {
 }
 
 impl TryIntoRawFd for net::TcpStream {
+    #[inline]
     fn try_into_raw_fd(self) -> Result<RawFd, Self> {
         let (socket, peer_addr) = self.into_inner().into_inner();
         match socket.try_into_inner() {
@@ -126,6 +131,7 @@ fn try_into_raw_fd(self) -> Result<RawFd, Self> {
 }
 
 impl TryIntoRawFd for net::TcpListener {
+    #[inline]
     fn try_into_raw_fd(self) -> Result<RawFd, Self> {
         match self.into_inner().into_inner().try_into_inner() {
             Ok(fd) => Ok(fd.into_inner()),
index 3cd245f40d967f9388f293bf37e536b293039360..059d6cb5ba13122994c69ee86439d1be1fe1181a 100644 (file)
@@ -32,7 +32,6 @@
 #[path = "../unsupported/process.rs"]
 pub mod process;
 pub mod rwlock;
-pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
 pub mod thread_local_key;
 
 pub use crate::sys_common::os_str_bytes as os_str;
 
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+    unsafe {
+        args::init(argc, argv);
+    }
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {}
 
 /// This function is used to implement functionality that simply doesn't exist.
 /// Programs relying on this functionality will need to deal with the error.
@@ -50,7 +58,7 @@ pub fn unsupported<T>() -> crate::io::Result<T> {
 }
 
 pub fn unsupported_err() -> crate::io::Error {
-    crate::io::Error::new_const(ErrorKind::Other, &"operation not supported on SGX yet")
+    crate::io::Error::new_const(ErrorKind::Unsupported, &"operation not supported on SGX yet")
 }
 
 /// This function is used to implement various functions that doesn't exist,
@@ -115,11 +123,6 @@ pub fn decode_error_kind(code: i32) -> ErrorKind {
     }
 }
 
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
 pub unsafe fn strlen(mut s: *const c_char) -> usize {
     let mut n = 0;
     while unsafe { *s } != 0 {
index c0c5d55548c5f52a60fe079901a6cf96af72bed3..5ccedece0f84b78eeeb07025b373a629515759af 100644 (file)
@@ -5,7 +5,7 @@
 use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
 use crate::sync::Arc;
 use crate::sys::fd::FileDesc;
-use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner, Void};
+use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner};
 use crate::time::Duration;
 
 use super::abi::usercalls;
@@ -310,7 +310,7 @@ fn from_inner(inner: Socket) -> TcpListener {
     }
 }
 
-pub struct UdpSocket(Void);
+pub struct UdpSocket(!);
 
 impl UdpSocket {
     pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
@@ -318,129 +318,129 @@ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
     }
 
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        match self.0 {}
+        self.0
     }
 
     pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        match self.0 {}
+        self.0
     }
 
     pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn duplicate(&self) -> io::Result<UdpSocket> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn broadcast(&self) -> io::Result<bool> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn multicast_loop_v4(&self) -> io::Result<bool> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn multicast_loop_v6(&self) -> io::Result<bool> {
-        match self.0 {}
+        self.0
     }
 
     pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn ttl(&self) -> io::Result<u32> {
-        match self.0 {}
+        self.0
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn send(&self, _: &[u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Debug for UdpSocket {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -462,7 +462,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-pub struct LookupHost(Void);
+pub struct LookupHost(!);
 
 impl LookupHost {
     fn new(host: String) -> io::Result<LookupHost> {
@@ -470,14 +470,14 @@ fn new(host: String) -> io::Result<LookupHost> {
     }
 
     pub fn port(&self) -> u16 {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Iterator for LookupHost {
     type Item = SocketAddr;
     fn next(&mut self) -> Option<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 }
 
index 56fc84b4a3fcac372b714f3d4d16d85ec357f9d7..144248d60c9cfb15d644ce99703cc77c44b950f3 100644 (file)
@@ -5,12 +5,13 @@
 use crate::ffi::{OsStr, OsString};
 use crate::fmt;
 use crate::io;
+use crate::marker::PhantomData;
 use crate::path::{self, PathBuf};
 use crate::str;
 use crate::sync::atomic::{AtomicUsize, Ordering};
 use crate::sync::Mutex;
 use crate::sync::Once;
-use crate::sys::{decode_error_kind, sgx_ineffective, unsupported, Void};
+use crate::sys::{decode_error_kind, sgx_ineffective, unsupported};
 use crate::vec;
 
 pub fn errno() -> i32 {
@@ -35,7 +36,7 @@ pub fn chdir(_: &path::Path) -> io::Result<()> {
     sgx_ineffective(())
 }
 
-pub struct SplitPaths<'a>(&'a Void);
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
 
 pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
     panic!("unsupported")
@@ -44,7 +45,7 @@ pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
 impl<'a> Iterator for SplitPaths<'a> {
     type Item = PathBuf;
     fn next(&mut self) -> Option<PathBuf> {
-        match *self.0 {}
+        self.0
     }
 }
 
diff --git a/library/std/src/sys/sgx/stack_overflow.rs b/library/std/src/sys/sgx/stack_overflow.rs
deleted file mode 100644 (file)
index b96652a..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#[cfg_attr(test, allow(dead_code))]
-pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
index 69676472493909b72be73204bc05a7262e595815..fc423e393d4a4b8e823ba74772ba556c5c899240 100644 (file)
@@ -6,7 +6,7 @@
 #![allow(dead_code)] // runtime init functions not used during testing
 
 use crate::ffi::OsString;
-use crate::marker::PhantomData;
+use crate::fmt;
 use crate::vec;
 
 /// One-time global initialization.
@@ -26,12 +26,14 @@ pub fn args() -> Args {
 
 pub struct Args {
     iter: vec::IntoIter<OsString>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
-impl Args {
-    pub fn inner_debug(&self) -> &[OsString] {
-        self.iter.as_slice()
+impl !Send for Args {}
+impl !Sync for Args {}
+
+impl fmt::Debug for Args {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.iter.as_slice().fmt(f)
     }
 }
 
@@ -76,7 +78,6 @@ fn next_back(&mut self) -> Option<OsString> {
 mod imp {
     use super::Args;
     use crate::ffi::{CStr, OsString};
-    use crate::marker::PhantomData;
     use crate::os::unix::prelude::*;
     use crate::ptr;
     use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
@@ -133,7 +134,7 @@ pub unsafe fn cleanup() {
     }
 
     pub fn args() -> Args {
-        Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData }
+        Args { iter: clone().into_iter() }
     }
 
     fn clone() -> Vec<OsString> {
@@ -155,7 +156,6 @@ fn clone() -> Vec<OsString> {
 mod imp {
     use super::Args;
     use crate::ffi::CStr;
-    use crate::marker::PhantomData;
 
     pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
 
@@ -180,7 +180,7 @@ pub fn args() -> Args {
                 })
                 .collect::<Vec<_>>()
         };
-        Args { iter: vec.into_iter(), _dont_send_or_sync_me: PhantomData }
+        Args { iter: vec.into_iter() }
     }
 
     // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
@@ -247,6 +247,6 @@ pub fn args() -> Args {
             }
         }
 
-        Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData }
+        Args { iter: res.into_iter() }
     }
 }
index 7f5e9b04dba4b69b38f1cf1ade48f6096f7ba8b4..3a88dc083b06c735fc0274abeea9ea357dfbe659 100644 (file)
@@ -173,3 +173,14 @@ pub mod os {
     pub const EXE_SUFFIX: &str = "";
     pub const EXE_EXTENSION: &str = "";
 }
+
+#[cfg(target_os = "vxworks")]
+pub mod os {
+    pub const FAMILY: &str = "unix";
+    pub const OS: &str = "vxworks";
+    pub const DLL_PREFIX: &str = "lib";
+    pub const DLL_SUFFIX: &str = ".so";
+    pub const DLL_EXTENSION: &str = "so";
+    pub const EXE_SUFFIX: &str = "";
+    pub const EXE_EXTENSION: &str = "";
+}
index ef3c689bd392144b4e03a6b9bb14b93bd5b05dc6..07c30bfa9ed154b6b9add61ecbcbf0f7089a3d31 100644 (file)
@@ -104,18 +104,21 @@ pub trait IntoRawFd {
 
 #[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
 impl AsRawFd for RawFd {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         *self
     }
 }
 #[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
 impl IntoRawFd for RawFd {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self
     }
 }
 #[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
 impl FromRawFd for RawFd {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
         fd
     }
@@ -123,18 +126,21 @@ unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl AsRawFd for fs::File {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().raw()
     }
 }
 #[stable(feature = "from_raw_os", since = "1.1.0")]
 impl FromRawFd for fs::File {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
         fs::File::from_inner(sys::fs::File::from_inner(fd))
     }
 }
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawFd for fs::File {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.into_inner().into_fd().into_raw()
     }
@@ -142,6 +148,7 @@ fn into_raw_fd(self) -> RawFd {
 
 #[stable(feature = "asraw_stdio", since = "1.21.0")]
 impl AsRawFd for io::Stdin {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDIN_FILENO
     }
@@ -149,6 +156,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "asraw_stdio", since = "1.21.0")]
 impl AsRawFd for io::Stdout {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDOUT_FILENO
     }
@@ -156,6 +164,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "asraw_stdio", since = "1.21.0")]
 impl AsRawFd for io::Stderr {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDERR_FILENO
     }
@@ -163,6 +172,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
 impl<'a> AsRawFd for io::StdinLock<'a> {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDIN_FILENO
     }
@@ -170,6 +180,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
 impl<'a> AsRawFd for io::StdoutLock<'a> {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDOUT_FILENO
     }
@@ -177,6 +188,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
 impl<'a> AsRawFd for io::StderrLock<'a> {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDERR_FILENO
     }
index e5048f7e545e0722d5a33877de597d4e16064f7b..735bf35a3ced6fc367ee523ce136a30cfb0afcfb 100644 (file)
@@ -62,6 +62,8 @@
         use crate::os::redox as platform;
         #[cfg(target_os = "solaris")]
         use crate::os::solaris as platform;
+        #[cfg(target_os = "vxworks")]
+        use crate::os::vxworks as platform;
     }
 }
 
index a8c13fbb874808f64a90669864cd00aeb057a0d0..9e39f70f68e698ab594df22812f5097b53d0a3eb 100644 (file)
@@ -879,6 +879,7 @@ pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl AsRawFd for UnixDatagram {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         *self.0.as_inner()
     }
@@ -886,6 +887,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl FromRawFd for UnixDatagram {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
         UnixDatagram(Socket::from_inner(fd))
     }
@@ -893,6 +895,7 @@ unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl IntoRawFd for UnixDatagram {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.0.into_inner()
     }
index 9803c6e27462c2dda9c222afed428dfa1038ed34..bdd08fe8380fa56e2cf5bd2a738319e43b7a0725 100644 (file)
@@ -240,6 +240,7 @@ pub fn incoming(&self) -> Incoming<'_> {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl AsRawFd for UnixListener {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         *self.0.as_inner()
     }
@@ -247,6 +248,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl FromRawFd for UnixListener {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
         UnixListener(Socket::from_inner(fd))
     }
@@ -254,6 +256,7 @@ unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl IntoRawFd for UnixListener {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.0.into_inner()
     }
index c42fee4c73bcde72def7340c9909dac50b1d9032..b3f12844101241db0d322a28f06a07f73a3ea95c 100644 (file)
@@ -6,6 +6,7 @@ macro_rules! impl_as_raw_fd {
     ($($t:ident)*) => {$(
         #[stable(feature = "rust1", since = "1.0.0")]
         impl AsRawFd for net::$t {
+            #[inline]
             fn as_raw_fd(&self) -> RawFd {
                 *self.as_inner().socket().as_inner()
             }
@@ -18,6 +19,7 @@ macro_rules! impl_from_raw_fd {
     ($($t:ident)*) => {$(
         #[stable(feature = "from_raw_os", since = "1.1.0")]
         impl FromRawFd for net::$t {
+            #[inline]
             unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
                 let socket = sys::net::Socket::from_inner(fd);
                 net::$t::from_inner(sys_common::net::$t::from_inner(socket))
@@ -31,6 +33,7 @@ macro_rules! impl_into_raw_fd {
     ($($t:ident)*) => {$(
         #[stable(feature = "into_raw_os", since = "1.4.0")]
         impl IntoRawFd for net::$t {
+            #[inline]
             fn into_raw_fd(self) -> RawFd {
                 self.into_inner().into_socket().into_inner()
             }
index fc08edacb828dbb5e7c6bc84df41819414b88397..a6f6e091305d51cb7c7a41f228ac248d693a1919 100644 (file)
@@ -654,6 +654,7 @@ fn flush(&mut self) -> io::Result<()> {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl AsRawFd for UnixStream {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         *self.0.as_inner()
     }
@@ -661,6 +662,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl FromRawFd for UnixStream {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
         UnixStream(Socket::from_inner(fd))
     }
@@ -668,6 +670,7 @@ unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl IntoRawFd for UnixStream {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.0.into_inner()
     }
index bc3bc0dcb0ce38343f2c702b63f724c6c11e2bf8..355855bcd10e203d6921f3560833bc3270fa4513 100644 (file)
@@ -274,6 +274,7 @@ fn into_raw(self) -> i32 {
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl FromRawFd for process::Stdio {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
         let fd = sys::fd::FileDesc::new(fd);
         let io = sys::process::Stdio::Fd(fd);
@@ -283,6 +284,7 @@ unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl AsRawFd for process::ChildStdin {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().raw()
     }
@@ -290,6 +292,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl AsRawFd for process::ChildStdout {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().raw()
     }
@@ -297,6 +300,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl AsRawFd for process::ChildStderr {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().raw()
     }
@@ -304,6 +308,7 @@ fn as_raw_fd(&self) -> RawFd {
 
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawFd for process::ChildStdin {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.into_inner().into_fd().into_raw()
     }
@@ -311,6 +316,7 @@ fn into_raw_fd(self) -> RawFd {
 
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawFd for process::ChildStdout {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.into_inner().into_fd().into_raw()
     }
@@ -318,6 +324,7 @@ fn into_raw_fd(self) -> RawFd {
 
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawFd for process::ChildStderr {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.into_inner().into_fd().into_raw()
     }
index 759565bab73f38a1b6964760735f45d2cc05e8da..16a7f727696ec971b1730f2b08b28906eded469b 100644 (file)
@@ -366,7 +366,7 @@ pub fn created(&self) -> io::Result<SystemTime> {
         }
 
         Err(io::Error::new_const(
-            io::ErrorKind::Other,
+            io::ErrorKind::Unsupported,
             &"creation time is not available on this platform \
                             currently",
         ))
index d60a4b5591fae39f7b9c90f050bf3db2a3fe05f6..3cf637c82285af801e14643a4875b6f19adcd4c1 100644 (file)
@@ -1,6 +1,9 @@
 macro_rules! unimpl {
     () => {
-        return Err(io::Error::new_const(io::ErrorKind::Other, &"No networking available on L4Re."));
+        return Err(io::Error::new_const(
+            io::ErrorKind::Unsupported,
+            &"No networking available on L4Re.",
+        ));
     };
 }
 
index 44328ffc22e5b6713a91c307c9cb4abf114d3144..a0ee69c2f72ddf871b6fef8320c7097437dacf21 100644 (file)
 
 pub use crate::sys_common::os_str_bytes as os_str;
 
-#[cfg(not(test))]
-pub fn init() {
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
     // The standard streams might be closed on application startup. To prevent
     // std::io::{stdin, stdout,stderr} objects from using other unrelated file
     // resources opened later, we reopen standards streams when they are closed.
-    unsafe {
-        sanitize_standard_fds();
-    }
+    sanitize_standard_fds();
 
     // By default, some platforms will send a *signal* when an EPIPE error
     // would otherwise be delivered. This runtime doesn't install a SIGPIPE
@@ -60,25 +59,24 @@ pub fn init() {
     //
     // Hence, we set SIGPIPE to ignore when the program starts up in order
     // to prevent this problem.
-    unsafe {
-        reset_sigpipe();
-    }
+    reset_sigpipe();
+
+    stack_overflow::init();
+    args::init(argc, argv);
 
-    cfg_if::cfg_if! {
-        if #[cfg(miri)] {
-            // The standard fds are always available in Miri.
-            unsafe fn sanitize_standard_fds() {}
-        } else if #[cfg(not(any(
-            target_os = "emscripten",
-            target_os = "fuchsia",
-            // The poll on Darwin doesn't set POLLNVAL for closed fds.
-            target_os = "macos",
-            target_os = "ios",
-            target_os = "redox",
-        )))] {
-            // In the case when all file descriptors are open, the poll has been
-            // observed to perform better than fcntl (on GNU/Linux).
-            unsafe fn sanitize_standard_fds() {
+    unsafe fn sanitize_standard_fds() {
+        #[cfg(not(miri))]
+        // The standard fds are always available in Miri.
+        cfg_if::cfg_if! {
+            if #[cfg(not(any(
+                target_os = "emscripten",
+                target_os = "fuchsia",
+                target_os = "vxworks",
+                // The poll on Darwin doesn't set POLLNVAL for closed fds.
+                target_os = "macos",
+                target_os = "ios",
+                target_os = "redox",
+            )))] {
                 use crate::sys::os::errno;
                 let pfds: &mut [_] = &mut [
                     libc::pollfd { fd: 0, events: 0, revents: 0 },
@@ -103,9 +101,7 @@ unsafe fn sanitize_standard_fds() {
                         libc::abort();
                     }
                 }
-            }
-        } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
-            unsafe fn sanitize_standard_fds() {
+            } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
                 use crate::sys::os::errno;
                 for fd in 0..3 {
                     if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
@@ -115,17 +111,20 @@ unsafe fn sanitize_standard_fds() {
                     }
                 }
             }
-        } else {
-            unsafe fn sanitize_standard_fds() {}
         }
     }
 
-    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
     unsafe fn reset_sigpipe() {
+        #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
         assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
     }
-    #[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
-    unsafe fn reset_sigpipe() {}
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+    args::cleanup();
+    stack_overflow::cleanup();
 }
 
 #[cfg(target_os = "android")]
@@ -148,6 +147,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
         libc::EINVAL => ErrorKind::InvalidInput,
         libc::ETIMEDOUT => ErrorKind::TimedOut,
         libc::EEXIST => ErrorKind::AlreadyExists,
+        libc::ENOSYS => ErrorKind::Unsupported,
 
         // These two constants can have the same value on some systems,
         // but different values on others, so we can't use a match
index ce2c4e88c7e5a59ce7c7671d10fb1892c248bbfc..984c08c2ad53171d5741f1ea4b923a7a9490ac32 100644 (file)
@@ -12,7 +12,6 @@
 use crate::fmt;
 use crate::io;
 use crate::iter;
-use crate::marker::PhantomData;
 use crate::mem;
 use crate::memchr;
 use crate::path::{self, PathBuf};
@@ -21,8 +20,8 @@
 use crate::str;
 use crate::sys::cvt;
 use crate::sys::fd;
+use crate::sys::rwlock::{RWLockReadGuard, StaticRWLock};
 use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
-use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock};
 use crate::vec;
 
 use libc::{c_char, c_int, c_void};
@@ -85,11 +84,6 @@ pub fn errno() -> i32 {
     unsafe { libc::errnoGet() }
 }
 
-#[cfg(target_os = "vxworks")]
-pub fn set_errno(e: i32) {
-    unsafe { libc::errnoSet(e as c_int) };
-}
-
 #[cfg(target_os = "dragonfly")]
 pub fn errno() -> i32 {
     extern "C" {
@@ -447,7 +441,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
 #[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
 pub fn current_exe() -> io::Result<PathBuf> {
     use crate::io::ErrorKind;
-    Err(io::Error::new_const(ErrorKind::Other, &"Not yet implemented!"))
+    Err(io::Error::new_const(ErrorKind::Unsupported, &"Not yet implemented!"))
 }
 
 #[cfg(target_os = "vxworks")]
@@ -465,9 +459,11 @@ pub fn current_exe() -> io::Result<PathBuf> {
 
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Env {}
+impl !Sync for Env {}
+
 impl Iterator for Env {
     type Item = (OsString, OsString);
     fn next(&mut self) -> Option<(OsString, OsString)> {
@@ -515,7 +511,7 @@ pub fn env() -> Env {
                 environ = environ.add(1);
             }
         }
-        return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+        return Env { iter: result.into_iter() };
     }
 
     fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
@@ -642,7 +638,7 @@ pub fn getppid() -> u32 {
     unsafe { libc::getppid() as u32 }
 }
 
-#[cfg(target_env = "gnu")]
+#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
 pub fn glibc_version() -> Option<(usize, usize)> {
     if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
         parse_glibc_version(version_str)
@@ -651,7 +647,7 @@ pub fn glibc_version() -> Option<(usize, usize)> {
     }
 }
 
-#[cfg(target_env = "gnu")]
+#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
 fn glibc_version_cstr() -> Option<&'static CStr> {
     weak! {
         fn gnu_get_libc_version() -> *const libc::c_char
@@ -665,7 +661,7 @@ fn gnu_get_libc_version() -> *const libc::c_char
 
 // Returns Some((major, minor)) if the string is a valid "x.y" version,
 // ignoring any extra dot-separated parts. Otherwise return None.
-#[cfg(target_env = "gnu")]
+#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
 fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
     let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
     match (parsed_ints.next(), parsed_ints.next()) {
index 1b7b93f9d4a5f93ad8ba68e35ec8065c5268d08e..f67c70c01772fd7d7a5dd648ca8c1fbab5a08f33 100644 (file)
@@ -4,11 +4,17 @@
 pub use crate::sys_common::process::CommandEnvs;
 
 mod process_common;
-#[cfg(not(target_os = "fuchsia"))]
-#[path = "process_unix.rs"]
-mod process_inner;
-#[cfg(target_os = "fuchsia")]
-#[path = "process_fuchsia.rs"]
-mod process_inner;
-#[cfg(target_os = "fuchsia")]
-mod zircon;
+
+cfg_if::cfg_if! {
+    if #[cfg(target_os = "fuchsia")] {
+        #[path = "process_fuchsia.rs"]
+        mod process_inner;
+        mod zircon;
+    } else if #[cfg(target_os = "vxworks")] {
+        #[path = "process_vxworks.rs"]
+        mod process_inner;
+    } else {
+        #[path = "process_unix.rs"]
+        mod process_inner;
+    }
+}
diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs
new file mode 100644 (file)
index 0000000..eecdb62
--- /dev/null
@@ -0,0 +1,237 @@
+use crate::fmt;
+use crate::io::{self, Error, ErrorKind};
+use crate::sys;
+use crate::sys::cvt;
+use crate::sys::process::process_common::*;
+use crate::sys_common::thread;
+use libc::RTP_ID;
+use libc::{self, c_char, c_int};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+    pub fn spawn(
+        &mut self,
+        default: Stdio,
+        needs_stdin: bool,
+    ) -> io::Result<(Process, StdioPipes)> {
+        use crate::sys::cvt_r;
+        let envp = self.capture_env();
+
+        if self.saw_nul() {
+            return Err(io::Error::new_const(
+                ErrorKind::InvalidInput,
+                &"nul byte found in provided data",
+            ));
+        }
+        let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+        let mut p = Process { pid: 0, status: None };
+
+        unsafe {
+            macro_rules! t {
+                ($e:expr) => {
+                    match $e {
+                        Ok(e) => e,
+                        Err(e) => return Err(e.into()),
+                    }
+                };
+            }
+
+            let mut orig_stdin = libc::STDIN_FILENO;
+            let mut orig_stdout = libc::STDOUT_FILENO;
+            let mut orig_stderr = libc::STDERR_FILENO;
+
+            if let Some(fd) = theirs.stdin.fd() {
+                orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO)));
+                t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
+            }
+            if let Some(fd) = theirs.stdout.fd() {
+                orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO)));
+                t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
+            }
+            if let Some(fd) = theirs.stderr.fd() {
+                orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO)));
+                t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
+            }
+
+            if let Some(ref cwd) = *self.get_cwd() {
+                t!(cvt(libc::chdir(cwd.as_ptr())));
+            }
+
+            // pre_exec closures are ignored on VxWorks
+            let _ = self.get_closures();
+
+            let c_envp = envp
+                .as_ref()
+                .map(|c| c.as_ptr())
+                .unwrap_or_else(|| *sys::os::environ() as *const _);
+            let stack_size = thread::min_stack();
+
+            // ensure that access to the environment is synchronized
+            let _lock = sys::os::env_read_lock();
+
+            let ret = libc::rtpSpawn(
+                self.get_program_cstr().as_ptr(),
+                self.get_argv().as_ptr() as *mut *const c_char, // argv
+                c_envp as *mut *const c_char,
+                100 as c_int, // initial priority
+                stack_size,   // initial stack size.
+                0,            // options
+                0,            // task options
+            );
+
+            // Because FileDesc was not used, each duplicated file descriptor
+            // needs to be closed manually
+            if orig_stdin != libc::STDIN_FILENO {
+                t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO)));
+                libc::close(orig_stdin);
+            }
+            if orig_stdout != libc::STDOUT_FILENO {
+                t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO)));
+                libc::close(orig_stdout);
+            }
+            if orig_stderr != libc::STDERR_FILENO {
+                t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO)));
+                libc::close(orig_stderr);
+            }
+
+            if ret != libc::RTP_ID_ERROR {
+                p.pid = ret;
+                Ok((p, ours))
+            } else {
+                Err(io::Error::last_os_error())
+            }
+        }
+    }
+
+    pub fn exec(&mut self, default: Stdio) -> io::Error {
+        let ret = Command::spawn(self, default, false);
+        match ret {
+            Ok(t) => unsafe {
+                let mut status = 0 as c_int;
+                libc::waitpid(t.0.pid, &mut status, 0);
+                libc::exit(0);
+            },
+            Err(e) => e,
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+/// The unique id of the process (this should never be negative).
+pub struct Process {
+    pid: RTP_ID,
+    status: Option<ExitStatus>,
+}
+
+impl Process {
+    pub fn id(&self) -> u32 {
+        self.pid as u32
+    }
+
+    pub fn kill(&mut self) -> io::Result<()> {
+        // If we've already waited on this process then the pid can be recycled
+        // and used for another process, and we probably shouldn't be killing
+        // random processes, so just return an error.
+        if self.status.is_some() {
+            Err(Error::new_const(
+                ErrorKind::InvalidInput,
+                &"invalid argument: can't kill an exited process",
+            ))
+        } else {
+            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+        }
+    }
+
+    pub fn wait(&mut self) -> io::Result<ExitStatus> {
+        use crate::sys::cvt_r;
+        if let Some(status) = self.status {
+            return Ok(status);
+        }
+        let mut status = 0 as c_int;
+        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
+        self.status = Some(ExitStatus::new(status));
+        Ok(ExitStatus::new(status))
+    }
+
+    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+        if let Some(status) = self.status {
+            return Ok(Some(status));
+        }
+        let mut status = 0 as c_int;
+        let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
+        if pid == 0 {
+            Ok(None)
+        } else {
+            self.status = Some(ExitStatus::new(status));
+            Ok(Some(ExitStatus::new(status)))
+        }
+    }
+}
+
+/// Unix exit statuses
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(c_int);
+
+impl ExitStatus {
+    pub fn new(status: c_int) -> ExitStatus {
+        ExitStatus(status)
+    }
+
+    fn exited(&self) -> bool {
+        libc::WIFEXITED(self.0)
+    }
+
+    pub fn success(&self) -> bool {
+        self.code() == Some(0)
+    }
+
+    pub fn code(&self) -> Option<i32> {
+        if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
+    }
+
+    pub fn signal(&self) -> Option<i32> {
+        if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
+    }
+
+    pub fn core_dumped(&self) -> bool {
+        // This method is not yet properly implemented on VxWorks
+        false
+    }
+
+    pub fn stopped_signal(&self) -> Option<i32> {
+        if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None }
+    }
+
+    pub fn continued(&self) -> bool {
+        // This method is not yet properly implemented on VxWorks
+        false
+    }
+
+    pub fn into_raw(&self) -> c_int {
+        self.0
+    }
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
+impl From<c_int> for ExitStatus {
+    fn from(a: c_int) -> ExitStatus {
+        ExitStatus(a)
+    }
+}
+
+impl fmt::Display for ExitStatus {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if let Some(code) = self.code() {
+            write!(f, "exit code: {}", code)
+        } else {
+            let signal = self.signal().unwrap();
+            write!(f, "signal: {}", signal)
+        }
+    }
+}
index 38ddb41700c4b323c6ec2719f3c6d1072a019e91..44f9eabc319a0c968621c37d255fdbdb096a3a50 100644 (file)
@@ -18,7 +18,8 @@ pub fn hashmap_random_keys() -> (u64, u64) {
     not(target_os = "freebsd"),
     not(target_os = "netbsd"),
     not(target_os = "fuchsia"),
-    not(target_os = "redox")
+    not(target_os = "redox"),
+    not(target_os = "vxworks")
 ))]
 mod imp {
     use crate::fs::File;
@@ -237,3 +238,29 @@ pub fn fill_bytes(v: &mut [u8]) {
         file.read_exact(v).expect("failed to read rand:")
     }
 }
+
+#[cfg(target_os = "vxworks")]
+mod imp {
+    use crate::io;
+    use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
+
+    pub fn fill_bytes(v: &mut [u8]) {
+        static RNG_INIT: AtomicBool = AtomicBool::new(false);
+        while !RNG_INIT.load(Relaxed) {
+            let ret = unsafe { libc::randSecure() };
+            if ret < 0 {
+                panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+            } else if ret > 0 {
+                RNG_INIT.store(true, Relaxed);
+                break;
+            }
+            unsafe { libc::usleep(10) };
+        }
+        let ret = unsafe {
+            libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
+        };
+        if ret < 0 {
+            panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+        }
+    }
+}
index 2b5067a34f648fa41baa290706ad3a53a347cce3..d97d9d712fc93a383450b3f270fb5bdfb5a662e7 100644 (file)
@@ -139,3 +139,55 @@ pub unsafe fn destroy(&self) {
         }
     }
 }
+
+pub struct StaticRWLock(RWLock);
+
+impl StaticRWLock {
+    pub const fn new() -> StaticRWLock {
+        StaticRWLock(RWLock::new())
+    }
+
+    /// Acquires shared access to the underlying lock, blocking the current
+    /// thread to do so.
+    ///
+    /// The lock is automatically unlocked when the returned guard is dropped.
+    #[inline]
+    pub fn read_with_guard(&'static self) -> RWLockReadGuard {
+        // SAFETY: All methods require static references, therefore self
+        // cannot be moved between invocations.
+        unsafe {
+            self.0.read();
+        }
+        RWLockReadGuard(&self.0)
+    }
+
+    /// Acquires write access to the underlying lock, blocking the current thread
+    /// to do so.
+    ///
+    /// The lock is automatically unlocked when the returned guard is dropped.
+    #[inline]
+    pub fn write_with_guard(&'static self) -> RWLockWriteGuard {
+        // SAFETY: All methods require static references, therefore self
+        // cannot be moved between invocations.
+        unsafe {
+            self.0.write();
+        }
+        RWLockWriteGuard(&self.0)
+    }
+}
+
+pub struct RWLockReadGuard(&'static RWLock);
+
+impl Drop for RWLockReadGuard {
+    fn drop(&mut self) {
+        unsafe { self.0.read_unlock() }
+    }
+}
+
+pub struct RWLockWriteGuard(&'static RWLock);
+
+impl Drop for RWLockWriteGuard {
+    fn drop(&mut self) {
+        unsafe { self.0.write_unlock() }
+    }
+}
index c3275eb6f0e50a836eab60f3061528e6bda9d44a..c3f410353b915bcd212e20b967b1ed2067273c1d 100644 (file)
@@ -92,3 +92,9 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
         }
     }
 }
+
+#[cfg(target_os = "vxworks")]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+    use crate::sys_common::thread_local_dtor::register_dtor_fallback;
+    register_dtor_fallback(t, dtor);
+}
index 71d0c5fa13e18a1a6167cec35eb34317c3829df6..c924a7d8a26723043424a4e64914619919bf2245 100644 (file)
@@ -1,17 +1,14 @@
 use crate::ffi::OsString;
 
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-pub unsafe fn cleanup() {}
-
 pub struct Args {}
 
 pub fn args() -> Args {
     Args {}
 }
 
-impl Args {
-    pub fn inner_debug(&self) -> &[OsString] {
-        &[]
+impl fmt::Debug for Args {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().finish()
     }
 }
 
index 01e4fd3c994d42db470020386ba4a429150d8301..6e72a7c632ed0b4755d3f56034a75c0296e9f31d 100644 (file)
@@ -10,15 +10,23 @@ pub mod memchr {
 // spec definition?
 use crate::os::raw::c_char;
 
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {}
 
 pub fn unsupported<T>() -> std_io::Result<T> {
     Err(unsupported_err())
 }
 
 pub fn unsupported_err() -> std_io::Error {
-    std_io::Error::new_const(std_io::ErrorKind::Other, &"operation not supported on this platform")
+    std_io::Error::new_const(
+        std_io::ErrorKind::Unsupported,
+        &"operation not supported on this platform",
+    )
 }
 
 pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
@@ -33,11 +41,6 @@ pub fn hashmap_random_keys() -> (u64, u64) {
     (1, 2)
 }
 
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
 pub unsafe fn strlen(mut s: *const c_char) -> usize {
     // SAFETY: The caller must guarantee `s` points to a valid 0-terminated string.
     unsafe {
index 4271d9b334588b888d8cbca52a9e0ad2aba2c15c..cd533761e37326d224965fe66faaab44455dd9d3 100644 (file)
@@ -4,77 +4,77 @@
 use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
 use crate::path::{Path, PathBuf};
 use crate::sys::time::SystemTime;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 
-pub struct File(Void);
+pub struct File(!);
 
-pub struct FileAttr(Void);
+pub struct FileAttr(!);
 
-pub struct ReadDir(Void);
+pub struct ReadDir(!);
 
-pub struct DirEntry(Void);
+pub struct DirEntry(!);
 
 #[derive(Clone, Debug)]
 pub struct OpenOptions {}
 
-pub struct FilePermissions(Void);
+pub struct FilePermissions(!);
 
-pub struct FileType(Void);
+pub struct FileType(!);
 
 #[derive(Debug)]
 pub struct DirBuilder {}
 
 impl FileAttr {
     pub fn size(&self) -> u64 {
-        match self.0 {}
+        self.0
     }
 
     pub fn perm(&self) -> FilePermissions {
-        match self.0 {}
+        self.0
     }
 
     pub fn file_type(&self) -> FileType {
-        match self.0 {}
+        self.0
     }
 
     pub fn modified(&self) -> io::Result<SystemTime> {
-        match self.0 {}
+        self.0
     }
 
     pub fn accessed(&self) -> io::Result<SystemTime> {
-        match self.0 {}
+        self.0
     }
 
     pub fn created(&self) -> io::Result<SystemTime> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Clone for FileAttr {
     fn clone(&self) -> FileAttr {
-        match self.0 {}
+        self.0
     }
 }
 
 impl FilePermissions {
     pub fn readonly(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_readonly(&mut self, _readonly: bool) {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Clone for FilePermissions {
     fn clone(&self) -> FilePermissions {
-        match self.0 {}
+        self.0
     }
 }
 
 impl PartialEq for FilePermissions {
     fn eq(&self, _other: &FilePermissions) -> bool {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -82,27 +82,27 @@ impl Eq for FilePermissions {}
 
 impl fmt::Debug for FilePermissions {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
 impl FileType {
     pub fn is_dir(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_file(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_symlink(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Clone for FileType {
     fn clone(&self) -> FileType {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -110,7 +110,7 @@ impl Copy for FileType {}
 
 impl PartialEq for FileType {
     fn eq(&self, _other: &FileType) -> bool {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -118,19 +118,19 @@ impl Eq for FileType {}
 
 impl Hash for FileType {
     fn hash<H: Hasher>(&self, _h: &mut H) {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Debug for FileType {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Debug for ReadDir {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -138,25 +138,25 @@ impl Iterator for ReadDir {
     type Item = io::Result<DirEntry>;
 
     fn next(&mut self) -> Option<io::Result<DirEntry>> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl DirEntry {
     pub fn path(&self) -> PathBuf {
-        match self.0 {}
+        self.0
     }
 
     pub fn file_name(&self) -> OsString {
-        match self.0 {}
+        self.0
     }
 
     pub fn metadata(&self) -> io::Result<FileAttr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn file_type(&self) -> io::Result<FileType> {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -179,59 +179,59 @@ pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
     }
 
     pub fn file_attr(&self) -> io::Result<FileAttr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn fsync(&self) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn datasync(&self) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn truncate(&self, _size: u64) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_read_vectored(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_write_vectored(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn flush(&self) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
-        match self.0 {}
+        self.0
     }
 
     pub fn duplicate(&self) -> io::Result<File> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -247,7 +247,7 @@ pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
 
 impl fmt::Debug for File {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
index d9efdec33d93749948315dec25cd8fd468c2f8b4..32ca68ef15b5b54c3c4d1714cd978b17d581cfd1 100644 (file)
@@ -15,7 +15,6 @@
 pub mod pipe;
 pub mod process;
 pub mod rwlock;
-pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
 #[cfg(target_thread_local)]
index 5c9f1098f9b7f0e8422840ffcce0a91d5ae1223b..96203c74b576cf48bd735f7275de7a1a38ea0817 100644 (file)
@@ -2,10 +2,10 @@
 use crate::fmt;
 use crate::io::{self, IoSlice, IoSliceMut};
 use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::time::Duration;
 
-pub struct TcpStream(Void);
+pub struct TcpStream(!);
 
 impl TcpStream {
     pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
@@ -17,97 +17,97 @@ pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
     }
 
     pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_read_vectored(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn write(&self, _: &[u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_write_vectored(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn duplicate(&self) -> io::Result<TcpStream> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn nodelay(&self) -> io::Result<bool> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn ttl(&self) -> io::Result<u32> {
-        match self.0 {}
+        self.0
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Debug for TcpStream {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
-pub struct TcpListener(Void);
+pub struct TcpListener(!);
 
 impl TcpListener {
     pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
@@ -115,49 +115,49 @@ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
     }
 
     pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
-        match self.0 {}
+        self.0
     }
 
     pub fn duplicate(&self) -> io::Result<TcpListener> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn ttl(&self) -> io::Result<u32> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn only_v6(&self) -> io::Result<bool> {
-        match self.0 {}
+        self.0
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Debug for TcpListener {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
-pub struct UdpSocket(Void);
+pub struct UdpSocket(!);
 
 impl UdpSocket {
     pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
@@ -165,144 +165,144 @@ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
     }
 
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 
     pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        match self.0 {}
+        self.0
     }
 
     pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        match self.0 {}
+        self.0
     }
 
     pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn duplicate(&self) -> io::Result<UdpSocket> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn broadcast(&self) -> io::Result<bool> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn multicast_loop_v4(&self) -> io::Result<bool> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn multicast_loop_v6(&self) -> io::Result<bool> {
-        match self.0 {}
+        self.0
     }
 
     pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn ttl(&self) -> io::Result<u32> {
-        match self.0 {}
+        self.0
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        match self.0 {}
+        self.0
     }
 
     pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn send(&self, _: &[u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Debug for UdpSocket {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
-pub struct LookupHost(Void);
+pub struct LookupHost(!);
 
 impl LookupHost {
     pub fn port(&self) -> u16 {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Iterator for LookupHost {
     type Item = SocketAddr;
     fn next(&mut self) -> Option<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 }
 
index 897927e7b79a7f4fec3dbe41446547012d1ab26b..e30395a0b1d92d9b10664fe4953ae3242bc96efd 100644 (file)
@@ -1,8 +1,9 @@
-use super::{unsupported, Void};
+use super::unsupported;
 use crate::error::Error as StdError;
 use crate::ffi::{OsStr, OsString};
 use crate::fmt;
 use crate::io;
+use crate::marker::PhantomData;
 use crate::path::{self, PathBuf};
 
 pub fn errno() -> i32 {
@@ -21,7 +22,7 @@ pub fn chdir(_: &path::Path) -> io::Result<()> {
     unsupported()
 }
 
-pub struct SplitPaths<'a>(&'a Void);
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
 
 pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
     panic!("unsupported")
@@ -30,7 +31,7 @@ pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
 impl<'a> Iterator for SplitPaths<'a> {
     type Item = PathBuf;
     fn next(&mut self) -> Option<PathBuf> {
-        match *self.0 {}
+        self.0
     }
 }
 
@@ -62,12 +63,12 @@ pub fn current_exe() -> io::Result<PathBuf> {
     unsupported()
 }
 
-pub struct Env(Void);
+pub struct Env(!);
 
 impl Iterator for Env {
     type Item = (OsString, OsString);
     fn next(&mut self) -> Option<(OsString, OsString)> {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -80,11 +81,11 @@ pub fn getenv(_: &OsStr) -> io::Result<Option<OsString>> {
 }
 
 pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
-    Err(io::Error::new_const(io::ErrorKind::Other, &"cannot set env vars on this platform"))
+    Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot set env vars on this platform"))
 }
 
 pub fn unsetenv(_: &OsStr) -> io::Result<()> {
-    Err(io::Error::new_const(io::ErrorKind::Other, &"cannot unset env vars on this platform"))
+    Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot unset env vars on this platform"))
 }
 
 pub fn temp_dir() -> PathBuf {
index 10d0925823eb94660278ce2390c892c093d7e3c6..25514c2322fa45d3890efeb67f86dc2a99b0c99c 100644 (file)
@@ -1,35 +1,34 @@
 use crate::io::{self, IoSlice, IoSliceMut};
-use crate::sys::Void;
 
-pub struct AnonPipe(Void);
+pub struct AnonPipe(!);
 
 impl AnonPipe {
     pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_read_vectored(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        match self.0 {}
+        self.0
     }
 
     pub fn is_write_vectored(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn diverge(&self) -> ! {
-        match self.0 {}
+        self.0
     }
 }
 
index 3ede2291d5a917ddf8fc51ada341869f6c2c6f95..38ac0a1ddd5f9ce1043be9a155bd48c937efffea 100644 (file)
@@ -5,7 +5,7 @@
 use crate::path::Path;
 use crate::sys::fs::File;
 use crate::sys::pipe::AnonPipe;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::sys_common::process::{CommandEnv, CommandEnvs};
 
 pub use crate::ffi::OsString as EnvKey;
@@ -94,21 +94,21 @@ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-pub struct ExitStatus(Void);
+pub struct ExitStatus(!);
 
 impl ExitStatus {
     pub fn success(&self) -> bool {
-        match self.0 {}
+        self.0
     }
 
     pub fn code(&self) -> Option<i32> {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Clone for ExitStatus {
     fn clone(&self) -> ExitStatus {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -116,7 +116,7 @@ impl Copy for ExitStatus {}
 
 impl PartialEq for ExitStatus {
     fn eq(&self, _other: &ExitStatus) -> bool {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -124,13 +124,13 @@ impl Eq for ExitStatus {}
 
 impl fmt::Debug for ExitStatus {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
 impl fmt::Display for ExitStatus {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {}
+        self.0
     }
 }
 
@@ -146,23 +146,23 @@ pub fn as_i32(&self) -> i32 {
     }
 }
 
-pub struct Process(Void);
+pub struct Process(!);
 
 impl Process {
     pub fn id(&self) -> u32 {
-        match self.0 {}
+        self.0
     }
 
     pub fn kill(&mut self) -> io::Result<()> {
-        match self.0 {}
+        self.0
     }
 
     pub fn wait(&mut self) -> io::Result<ExitStatus> {
-        match self.0 {}
+        self.0
     }
 
     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
-        match self.0 {}
+        self.0
     }
 }
 
diff --git a/library/std/src/sys/unsupported/stack_overflow.rs b/library/std/src/sys/unsupported/stack_overflow.rs
deleted file mode 100644 (file)
index 3255539..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
index 20ae309db30d7379c29095c4f4f339fd43a9ae71..cda8510e1baebe1316cd57b8bb0c01e35a1fb145 100644 (file)
@@ -1,9 +1,9 @@
-use super::{unsupported, Void};
+use super::unsupported;
 use crate::ffi::CStr;
 use crate::io;
 use crate::time::Duration;
 
-pub struct Thread(Void);
+pub struct Thread(!);
 
 pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
 
@@ -26,7 +26,7 @@ pub fn sleep(_dur: Duration) {
     }
 
     pub fn join(self) {
-        match self.0 {}
+        self.0
     }
 }
 
diff --git a/library/std/src/sys/vxworks/env.rs b/library/std/src/sys/vxworks/env.rs
deleted file mode 100644 (file)
index fe1aedd..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-pub mod os {
-    pub const FAMILY: &str = "vxworks";
-    pub const OS: &str = "vxworks";
-    pub const DLL_PREFIX: &str = "lib";
-    pub const DLL_SUFFIX: &str = ".so";
-    pub const DLL_EXTENSION: &str = "so";
-    pub const EXE_SUFFIX: &str = "";
-    pub const EXE_EXTENSION: &str = "";
-}
diff --git a/library/std/src/sys/vxworks/mod.rs b/library/std/src/sys/vxworks/mod.rs
deleted file mode 100644 (file)
index c20edaa..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-#![allow(dead_code)]
-#![allow(missing_docs, nonstandard_style)]
-
-use crate::io::ErrorKind;
-
-pub use self::rand::hashmap_random_keys;
-pub use crate::os::vxworks as platform;
-pub use libc::strlen;
-
-#[macro_use]
-#[path = "../unix/weak.rs"]
-pub mod weak;
-
-#[path = "../unix/alloc.rs"]
-pub mod alloc;
-#[path = "../unix/args.rs"]
-pub mod args;
-#[path = "../unix/cmath.rs"]
-pub mod cmath;
-#[path = "../unix/condvar.rs"]
-pub mod condvar;
-pub mod env;
-#[path = "../unix/ext/mod.rs"]
-pub mod ext;
-#[path = "../unix/fd.rs"]
-pub mod fd;
-#[path = "../unix/fs.rs"]
-pub mod fs;
-#[path = "../unix/io.rs"]
-pub mod io;
-#[path = "../unix/memchr.rs"]
-pub mod memchr;
-#[path = "../unix/mutex.rs"]
-pub mod mutex;
-#[path = "../unix/net.rs"]
-pub mod net;
-#[path = "../unix/os.rs"]
-pub mod os;
-#[path = "../unix/path.rs"]
-pub mod path;
-#[path = "../unix/pipe.rs"]
-pub mod pipe;
-pub mod process;
-pub mod rand;
-#[path = "../unix/rwlock.rs"]
-pub mod rwlock;
-#[path = "../unix/stack_overflow.rs"]
-pub mod stack_overflow;
-#[path = "../unix/stdio.rs"]
-pub mod stdio;
-#[path = "../unix/thread.rs"]
-pub mod thread;
-pub mod thread_local_dtor;
-#[path = "../unix/thread_local_key.rs"]
-pub mod thread_local_key;
-#[path = "../unix/time.rs"]
-pub mod time;
-
-pub use crate::sys_common::os_str_bytes as os_str;
-
-#[cfg(not(test))]
-pub fn init() {
-    // ignore SIGPIPE
-    unsafe {
-        assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
-    }
-}
-
-pub use libc::signal;
-
-pub fn decode_error_kind(errno: i32) -> ErrorKind {
-    match errno as libc::c_int {
-        libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
-        libc::ECONNRESET => ErrorKind::ConnectionReset,
-        libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
-        libc::EPIPE => ErrorKind::BrokenPipe,
-        libc::ENOTCONN => ErrorKind::NotConnected,
-        libc::ECONNABORTED => ErrorKind::ConnectionAborted,
-        libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
-        libc::EADDRINUSE => ErrorKind::AddrInUse,
-        libc::ENOENT => ErrorKind::NotFound,
-        libc::EINTR => ErrorKind::Interrupted,
-        libc::EINVAL => ErrorKind::InvalidInput,
-        libc::ETIMEDOUT => ErrorKind::TimedOut,
-        libc::EEXIST => ErrorKind::AlreadyExists,
-
-        // These two constants can have the same value on some systems,
-        // but different values on others, so we can't use a match
-        // clause
-        x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => ErrorKind::WouldBlock,
-
-        _ => ErrorKind::Other,
-    }
-}
-
-#[doc(hidden)]
-pub trait IsMinusOne {
-    fn is_minus_one(&self) -> bool;
-}
-
-macro_rules! impl_is_minus_one {
-    ($($t:ident)*) => ($(impl IsMinusOne for $t {
-        fn is_minus_one(&self) -> bool {
-            *self == -1
-        }
-    })*)
-}
-
-impl_is_minus_one! { i8 i16 i32 i64 isize }
-
-pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
-    if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) }
-}
-
-pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
-where
-    T: IsMinusOne,
-    F: FnMut() -> T,
-{
-    loop {
-        match cvt(f()) {
-            Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
-            other => return other,
-        }
-    }
-}
-
-// On Unix-like platforms, libc::abort will unregister signal handlers
-// including the SIGABRT handler, preventing the abort from being blocked, and
-// fclose streams, with the side effect of flushing them so libc buffered
-// output will be printed.  Additionally the shell will generally print a more
-// understandable error message like "Abort trap" rather than "Illegal
-// instruction" that intrinsics::abort would cause, as intrinsics::abort is
-// implemented as an illegal instruction.
-pub fn abort_internal() -> ! {
-    unsafe { libc::abort() }
-}
diff --git a/library/std/src/sys/vxworks/process/mod.rs b/library/std/src/sys/vxworks/process/mod.rs
deleted file mode 100644 (file)
index dc6130e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
-pub use self::process_inner::{ExitStatus, Process};
-pub use crate::ffi::OsString as EnvKey;
-pub use crate::sys_common::process::CommandEnvs;
-
-#[path = "../../unix/process/process_common.rs"]
-mod process_common;
-#[path = "process_vxworks.rs"]
-mod process_inner;
diff --git a/library/std/src/sys/vxworks/process/process_vxworks.rs b/library/std/src/sys/vxworks/process/process_vxworks.rs
deleted file mode 100644 (file)
index 2954523..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-use crate::fmt;
-use crate::io::{self, Error, ErrorKind};
-use crate::sys;
-use crate::sys::cvt;
-use crate::sys::process::process_common::*;
-use crate::sys_common::thread;
-use libc::RTP_ID;
-use libc::{self, c_char, c_int};
-
-////////////////////////////////////////////////////////////////////////////////
-// Command
-////////////////////////////////////////////////////////////////////////////////
-
-impl Command {
-    pub fn spawn(
-        &mut self,
-        default: Stdio,
-        needs_stdin: bool,
-    ) -> io::Result<(Process, StdioPipes)> {
-        use crate::sys::cvt_r;
-        const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
-        let envp = self.capture_env();
-
-        if self.saw_nul() {
-            return Err(io::Error::new_const(
-                ErrorKind::InvalidInput,
-                &"nul byte found in provided data",
-            ));
-        }
-        let (ours, theirs) = self.setup_io(default, needs_stdin)?;
-        let mut p = Process { pid: 0, status: None };
-
-        unsafe {
-            macro_rules! t {
-                ($e:expr) => {
-                    match $e {
-                        Ok(e) => e,
-                        Err(e) => return Err(e.into()),
-                    }
-                };
-            }
-
-            let mut orig_stdin = libc::STDIN_FILENO;
-            let mut orig_stdout = libc::STDOUT_FILENO;
-            let mut orig_stderr = libc::STDERR_FILENO;
-
-            if let Some(fd) = theirs.stdin.fd() {
-                orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO)));
-                t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
-            }
-            if let Some(fd) = theirs.stdout.fd() {
-                orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO)));
-                t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
-            }
-            if let Some(fd) = theirs.stderr.fd() {
-                orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO)));
-                t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
-            }
-
-            if let Some(ref cwd) = *self.get_cwd() {
-                t!(cvt(libc::chdir(cwd.as_ptr())));
-            }
-
-            let c_envp = envp
-                .as_ref()
-                .map(|c| c.as_ptr())
-                .unwrap_or_else(|| *sys::os::environ() as *const _);
-            let stack_size = thread::min_stack();
-
-            // ensure that access to the environment is synchronized
-            let _lock = sys::os::env_lock();
-
-            let ret = libc::rtpSpawn(
-                self.get_program_cstr().as_ptr(),
-                self.get_argv().as_ptr() as *mut *const c_char, // argv
-                c_envp as *mut *const c_char,
-                100 as c_int, // initial priority
-                stack_size,   // initial stack size.
-                0,            // options
-                0,            // task options
-            );
-
-            // Because FileDesc was not used, each duplicated file descriptor
-            // needs to be closed manually
-            if orig_stdin != libc::STDIN_FILENO {
-                t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO)));
-                libc::close(orig_stdin);
-            }
-            if orig_stdout != libc::STDOUT_FILENO {
-                t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO)));
-                libc::close(orig_stdout);
-            }
-            if orig_stderr != libc::STDERR_FILENO {
-                t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO)));
-                libc::close(orig_stderr);
-            }
-
-            if ret != libc::RTP_ID_ERROR {
-                p.pid = ret;
-                Ok((p, ours))
-            } else {
-                Err(io::Error::last_os_error())
-            }
-        }
-    }
-
-    pub fn exec(&mut self, default: Stdio) -> io::Error {
-        let ret = Command::spawn(self, default, false);
-        match ret {
-            Ok(t) => unsafe {
-                let mut status = 0 as c_int;
-                libc::waitpid(t.0.pid, &mut status, 0);
-                libc::exit(0);
-            },
-            Err(e) => e,
-        }
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Processes
-////////////////////////////////////////////////////////////////////////////////
-
-/// The unique id of the process (this should never be negative).
-pub struct Process {
-    pid: RTP_ID,
-    status: Option<ExitStatus>,
-}
-
-impl Process {
-    pub fn id(&self) -> u32 {
-        self.pid as u32
-    }
-
-    pub fn kill(&mut self) -> io::Result<()> {
-        // If we've already waited on this process then the pid can be recycled
-        // and used for another process, and we probably shouldn't be killing
-        // random processes, so just return an error.
-        if self.status.is_some() {
-            Err(Error::new_const(
-                ErrorKind::InvalidInput,
-                &"invalid argument: can't kill an exited process",
-            ))
-        } else {
-            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
-        }
-    }
-
-    pub fn wait(&mut self) -> io::Result<ExitStatus> {
-        use crate::sys::cvt_r;
-        if let Some(status) = self.status {
-            return Ok(status);
-        }
-        let mut status = 0 as c_int;
-        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
-        self.status = Some(ExitStatus::new(status));
-        Ok(ExitStatus::new(status))
-    }
-
-    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
-        if let Some(status) = self.status {
-            return Ok(Some(status));
-        }
-        let mut status = 0 as c_int;
-        let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
-        if pid == 0 {
-            Ok(None)
-        } else {
-            self.status = Some(ExitStatus::new(status));
-            Ok(Some(ExitStatus::new(status)))
-        }
-    }
-}
-
-/// Unix exit statuses
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct ExitStatus(c_int);
-
-impl ExitStatus {
-    pub fn new(status: c_int) -> ExitStatus {
-        ExitStatus(status)
-    }
-
-    fn exited(&self) -> bool {
-        libc::WIFEXITED(self.0)
-    }
-
-    pub fn success(&self) -> bool {
-        self.code() == Some(0)
-    }
-
-    pub fn code(&self) -> Option<i32> {
-        if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
-    }
-
-    pub fn signal(&self) -> Option<i32> {
-        if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
-    }
-}
-
-/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
-impl From<c_int> for ExitStatus {
-    fn from(a: c_int) -> ExitStatus {
-        ExitStatus(a)
-    }
-}
-
-impl fmt::Display for ExitStatus {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if let Some(code) = self.code() {
-            write!(f, "exit code: {}", code)
-        } else {
-            let signal = self.signal().unwrap();
-            write!(f, "signal: {}", signal)
-        }
-    }
-}
diff --git a/library/std/src/sys/vxworks/rand.rs b/library/std/src/sys/vxworks/rand.rs
deleted file mode 100644 (file)
index 3a1ff5f..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-use crate::mem;
-use crate::slice;
-
-pub fn hashmap_random_keys() -> (u64, u64) {
-    let mut v = (0, 0);
-    unsafe {
-        let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8, mem::size_of_val(&v));
-        imp::fill_bytes(view);
-    }
-    return v;
-}
-
-mod imp {
-    use crate::io;
-    use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
-
-    pub fn fill_bytes(v: &mut [u8]) {
-        static RNG_INIT: AtomicBool = AtomicBool::new(false);
-        while !RNG_INIT.load(Relaxed) {
-            let ret = unsafe { libc::randSecure() };
-            if ret < 0 {
-                panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
-            } else if ret > 0 {
-                RNG_INIT.store(true, Relaxed);
-                break;
-            }
-            unsafe { libc::usleep(10) };
-        }
-        let ret = unsafe {
-            libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
-        };
-        if ret < 0 {
-            panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
-        }
-    }
-}
diff --git a/library/std/src/sys/vxworks/thread_local_dtor.rs b/library/std/src/sys/vxworks/thread_local_dtor.rs
deleted file mode 100644 (file)
index 5391ed8..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#![cfg(target_thread_local)]
-#![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_dtor::register_dtor_fallback;
-    register_dtor_fallback(t, dtor);
-}
index 9a27218e1fb70cb2510b36260277d8f7497ad81a..c42c310e3a254f934b15cbc8e464e53f218e612b 100644 (file)
@@ -1,25 +1,20 @@
 #![deny(unsafe_op_in_unsafe_fn)]
 
 use crate::ffi::{CStr, OsStr, OsString};
-use crate::marker::PhantomData;
+use crate::fmt;
 use crate::os::wasi::ffi::OsStrExt;
 use crate::vec;
 
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-
-pub unsafe fn cleanup() {}
-
 pub struct Args {
     iter: vec::IntoIter<OsString>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Args {}
+impl !Sync for Args {}
+
 /// Returns the command line arguments
 pub fn args() -> Args {
-    Args {
-        iter: maybe_args().unwrap_or(Vec::new()).into_iter(),
-        _dont_send_or_sync_me: PhantomData,
-    }
+    Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() }
 }
 
 fn maybe_args() -> Option<Vec<OsString>> {
@@ -38,9 +33,9 @@ fn maybe_args() -> Option<Vec<OsString>> {
     }
 }
 
-impl Args {
-    pub fn inner_debug(&self) -> &[OsString] {
-        self.iter.as_slice()
+impl fmt::Debug for Args {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.iter.as_slice().fmt(f)
     }
 }
 
index 3c480aa8e19bf5f43194a840d1e3fcc27a12f8c3..b2e79cc1b4a9d59c59d680e2f22aa038c0f1d97f 100644 (file)
@@ -54,126 +54,147 @@ pub trait IntoRawFd {
 
 #[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
 impl AsRawFd for RawFd {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         *self
     }
 }
 #[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
 impl IntoRawFd for RawFd {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self
     }
 }
 #[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
 impl FromRawFd for RawFd {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
         fd
     }
 }
 
 impl AsRawFd for net::TcpStream {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().as_raw()
     }
 }
 
 impl FromRawFd for net::TcpStream {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream {
         net::TcpStream::from_inner(sys::net::TcpStream::from_inner(fd))
     }
 }
 
 impl IntoRawFd for net::TcpStream {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.into_inner().into_fd().into_raw()
     }
 }
 
 impl AsRawFd for net::TcpListener {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().as_raw()
     }
 }
 
 impl FromRawFd for net::TcpListener {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener {
         net::TcpListener::from_inner(sys::net::TcpListener::from_inner(fd))
     }
 }
 
 impl IntoRawFd for net::TcpListener {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.into_inner().into_fd().into_raw()
     }
 }
 
 impl AsRawFd for net::UdpSocket {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().as_raw()
     }
 }
 
 impl FromRawFd for net::UdpSocket {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket {
         net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(fd))
     }
 }
 
 impl IntoRawFd for net::UdpSocket {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.into_inner().into_fd().into_raw()
     }
 }
 
 impl AsRawFd for fs::File {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().as_raw()
     }
 }
 
 impl FromRawFd for fs::File {
+    #[inline]
     unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
         fs::File::from_inner(sys::fs::File::from_inner(fd))
     }
 }
 
 impl IntoRawFd for fs::File {
+    #[inline]
     fn into_raw_fd(self) -> RawFd {
         self.into_inner().into_fd().into_raw()
     }
 }
 
 impl AsRawFd for io::Stdin {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDIN_FILENO as RawFd
     }
 }
 
 impl AsRawFd for io::Stdout {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDOUT_FILENO as RawFd
     }
 }
 
 impl AsRawFd for io::Stderr {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDERR_FILENO as RawFd
     }
 }
 
 impl<'a> AsRawFd for io::StdinLock<'a> {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDIN_FILENO as RawFd
     }
 }
 
 impl<'a> AsRawFd for io::StdoutLock<'a> {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDOUT_FILENO as RawFd
     }
 }
 
 impl<'a> AsRawFd for io::StderrLock<'a> {
+    #[inline]
     fn as_raw_fd(&self) -> RawFd {
         libc::STDERR_FILENO as RawFd
     }
index a0a37ef8316a87ca6f063361418f32fe676e6815..2584d35b6ef8aad68162d2593a7d8ea855f70b5d 100644 (file)
@@ -42,8 +42,6 @@
 pub mod process;
 #[path = "../unsupported/rwlock.rs"]
 pub mod rwlock;
-#[path = "../unsupported/stack_overflow.rs"]
-pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
 #[path = "../unsupported/thread_local_dtor.rs"]
@@ -78,6 +76,7 @@ pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind {
         wasi::ERRNO_TIMEDOUT => TimedOut,
         wasi::ERRNO_EXIST => AlreadyExists,
         wasi::ERRNO_AGAIN => WouldBlock,
+        wasi::ERRNO_NOSYS => Unsupported,
         _ => Other,
     }
 }
index 3f294e7df418ee8ae91be66b15d534a3a360822e..06860673d90e00ec1c0c41536db7564c15c7dec5 100644 (file)
@@ -5,7 +5,7 @@
 use crate::fmt;
 use crate::io::{self, IoSlice, IoSliceMut};
 use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::sys_common::FromInner;
 use crate::time::Duration;
 
@@ -343,18 +343,18 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-pub struct LookupHost(Void);
+pub struct LookupHost(!);
 
 impl LookupHost {
     pub fn port(&self) -> u16 {
-        match self.0 {}
+        self.0
     }
 }
 
 impl Iterator for LookupHost {
     type Item = SocketAddr;
     fn next(&mut self) -> Option<SocketAddr> {
-        match self.0 {}
+        self.0
     }
 }
 
index 185d6109cb93e93a5cb7a39b42225009670fa212..f129ee55a8391b2c6a858dab3fb2d9c390896175 100644 (file)
@@ -10,7 +10,7 @@
 use crate::path::{self, PathBuf};
 use crate::str;
 use crate::sys::memchr;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::vec;
 
 // Add a few symbols not in upstream `libc` just yet.
@@ -87,7 +87,7 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
     }
 }
 
-pub struct SplitPaths<'a>(&'a Void);
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
 
 pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
     panic!("unsupported")
@@ -96,7 +96,7 @@ pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
 impl<'a> Iterator for SplitPaths<'a> {
     type Item = PathBuf;
     fn next(&mut self) -> Option<PathBuf> {
-        match *self.0 {}
+        self.0
     }
 }
 
@@ -129,9 +129,11 @@ pub fn current_exe() -> io::Result<PathBuf> {
 }
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Env {}
+impl !Sync for Env {}
+
 impl Iterator for Env {
     type Item = (OsString, OsString);
     fn next(&mut self) -> Option<(OsString, OsString)> {
@@ -155,7 +157,7 @@ pub fn env() -> Env {
                 environ = environ.add(1);
             }
         }
-        return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+        return Env { iter: result.into_iter() };
     }
 
     // See src/libstd/sys/unix/os.rs, same as that
index 8eaa5f09cb65691cae8944a93d7141bfc256cee9..74515553a82182d7e8521fb49a6bc5105c154f63 100644 (file)
@@ -3,10 +3,10 @@
 use crate::ffi::CStr;
 use crate::io;
 use crate::mem;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::time::Duration;
 
-pub struct Thread(Void);
+pub struct Thread(!);
 
 pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
 
@@ -59,7 +59,7 @@ pub fn sleep(dur: Duration) {
     }
 
     pub fn join(self) {
-        match self.0 {}
+        self.0
     }
 }
 
index 3b6557ae3257f73b1497af9e1ddbd858c39664aa..fde1ab79e1f4b97d73bc1ed1e3333755ab2e93f1 100644 (file)
@@ -1,25 +1,21 @@
 use crate::ffi::OsString;
-use crate::marker::PhantomData;
+use crate::fmt;
 use crate::vec;
 
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
-    // On wasm these should always be null, so there's nothing for us to do here
-}
-
-pub unsafe fn cleanup() {}
-
 pub fn args() -> Args {
-    Args { iter: Vec::new().into_iter(), _dont_send_or_sync_me: PhantomData }
+    Args { iter: Vec::new().into_iter() }
 }
 
 pub struct Args {
     iter: vec::IntoIter<OsString>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
-impl Args {
-    pub fn inner_debug(&self) -> &[OsString] {
-        self.iter.as_slice()
+impl !Send for Args {}
+impl !Sync for Args {}
+
+impl fmt::Debug for Args {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.iter.as_slice().fmt(f)
     }
 }
 
index 82683c0f624cf88487ca777b748493eb5a4d4d4a..8705910c73a8180e2ca8ec09c25f67732bc6de40 100644 (file)
@@ -35,8 +35,6 @@
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
 pub mod process;
-#[path = "../unsupported/stack_overflow.rs"]
-pub mod stack_overflow;
 #[path = "../unsupported/stdio.rs"]
 pub mod stdio;
 pub mod thread;
index 5eafb77da1dcdc5883b27b079cf81c8c1f911e61..b7bf95c89b482cd532966169bfad10fcc77ee6ac 100644 (file)
@@ -1,9 +1,9 @@
 use crate::ffi::CStr;
 use crate::io;
-use crate::sys::{unsupported, Void};
+use crate::sys::unsupported;
 use crate::time::Duration;
 
-pub struct Thread(Void);
+pub struct Thread(!);
 
 pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
 
@@ -47,7 +47,7 @@ pub fn sleep(dur: Duration) {
     }
 
     pub fn join(self) {
-        match self.0 {}
+        self.0
     }
 }
 
index bcc2ea9ae00f02f5cffb7515f07133a4a902d192..f1264130faf7abe64148586f4ab2ecd52efc365e 100644 (file)
 
 use core::iter;
 
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-
-pub unsafe fn cleanup() {}
-
 pub fn args() -> Args {
     unsafe {
         let lp_cmd_line = c::GetCommandLineW();
@@ -164,19 +160,9 @@ pub struct Args {
     parsed_args_list: vec::IntoIter<OsString>,
 }
 
-pub struct ArgsInnerDebug<'a> {
-    args: &'a Args,
-}
-
-impl<'a> fmt::Debug for ArgsInnerDebug<'a> {
+impl fmt::Debug for Args {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.args.parsed_args_list.as_slice().fmt(f)
-    }
-}
-
-impl Args {
-    pub fn inner_debug(&self) -> ArgsInnerDebug<'_> {
-        ArgsInnerDebug { args: self }
+        self.parsed_args_list.as_slice().fmt(f)
     }
 }
 
index e75f9a4bfd5e337ca75e4eb1b031079caa1f5d2a..31b5d015ed0c31a94b52f6c7b409f71d5c0c8b90 100644 (file)
@@ -59,6 +59,7 @@ pub trait IntoRawHandle {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl AsRawHandle for fs::File {
+    #[inline]
     fn as_raw_handle(&self) -> RawHandle {
         self.as_inner().handle().raw() as RawHandle
     }
@@ -108,6 +109,7 @@ fn as_raw_handle(&self) -> RawHandle {
 
 #[stable(feature = "from_raw_os", since = "1.1.0")]
 impl FromRawHandle for fs::File {
+    #[inline]
     unsafe fn from_raw_handle(handle: RawHandle) -> fs::File {
         let handle = handle as c::HANDLE;
         fs::File::from_inner(sys::fs::File::from_inner(handle))
@@ -116,6 +118,7 @@ unsafe fn from_raw_handle(handle: RawHandle) -> fs::File {
 
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawHandle for fs::File {
+    #[inline]
     fn into_raw_handle(self) -> RawHandle {
         self.into_inner().into_handle().into_raw() as *mut _
     }
@@ -161,18 +164,21 @@ pub trait IntoRawSocket {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl AsRawSocket for net::TcpStream {
+    #[inline]
     fn as_raw_socket(&self) -> RawSocket {
         *self.as_inner().socket().as_inner()
     }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
 impl AsRawSocket for net::TcpListener {
+    #[inline]
     fn as_raw_socket(&self) -> RawSocket {
         *self.as_inner().socket().as_inner()
     }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
 impl AsRawSocket for net::UdpSocket {
+    #[inline]
     fn as_raw_socket(&self) -> RawSocket {
         *self.as_inner().socket().as_inner()
     }
@@ -180,6 +186,7 @@ fn as_raw_socket(&self) -> RawSocket {
 
 #[stable(feature = "from_raw_os", since = "1.1.0")]
 impl FromRawSocket for net::TcpStream {
+    #[inline]
     unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream {
         let sock = sys::net::Socket::from_inner(sock);
         net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock))
@@ -187,6 +194,7 @@ unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream {
 }
 #[stable(feature = "from_raw_os", since = "1.1.0")]
 impl FromRawSocket for net::TcpListener {
+    #[inline]
     unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener {
         let sock = sys::net::Socket::from_inner(sock);
         net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock))
@@ -194,6 +202,7 @@ unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener {
 }
 #[stable(feature = "from_raw_os", since = "1.1.0")]
 impl FromRawSocket for net::UdpSocket {
+    #[inline]
     unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket {
         let sock = sys::net::Socket::from_inner(sock);
         net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock))
@@ -202,6 +211,7 @@ unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket {
 
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawSocket for net::TcpStream {
+    #[inline]
     fn into_raw_socket(self) -> RawSocket {
         self.into_inner().into_socket().into_inner()
     }
@@ -209,6 +219,7 @@ fn into_raw_socket(self) -> RawSocket {
 
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawSocket for net::TcpListener {
+    #[inline]
     fn into_raw_socket(self) -> RawSocket {
         self.into_inner().into_socket().into_inner()
     }
@@ -216,6 +227,7 @@ fn into_raw_socket(self) -> RawSocket {
 
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawSocket for net::UdpSocket {
+    #[inline]
     fn into_raw_socket(self) -> RawSocket {
         self.into_inner().into_socket().into_inner()
     }
index 67412e1677937b7afea82a624d6f13581bade34b..67756b15531bf360b9d5bc8995e240f713a29cb6 100644 (file)
@@ -19,6 +19,7 @@ unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio {
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl AsRawHandle for process::Child {
+    #[inline]
     fn as_raw_handle(&self) -> RawHandle {
         self.as_inner().handle().raw() as *mut _
     }
@@ -33,6 +34,7 @@ fn into_raw_handle(self) -> RawHandle {
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl AsRawHandle for process::ChildStdin {
+    #[inline]
     fn as_raw_handle(&self) -> RawHandle {
         self.as_inner().handle().raw() as *mut _
     }
@@ -40,6 +42,7 @@ fn as_raw_handle(&self) -> RawHandle {
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl AsRawHandle for process::ChildStdout {
+    #[inline]
     fn as_raw_handle(&self) -> RawHandle {
         self.as_inner().handle().raw() as *mut _
     }
@@ -47,6 +50,7 @@ fn as_raw_handle(&self) -> RawHandle {
 
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl AsRawHandle for process::ChildStderr {
+    #[inline]
     fn as_raw_handle(&self) -> RawHandle {
         self.as_inner().handle().raw() as *mut _
     }
index 41c29f5b950ef537ca78039eb3609112b16f5461..6bd02054f7150ba5e662a1810bf3f046952fc225 100644 (file)
@@ -8,6 +8,7 @@
 
 #[stable(feature = "thread_extensions", since = "1.9.0")]
 impl<T> AsRawHandle for thread::JoinHandle<T> {
+    #[inline]
     fn as_raw_handle(&self) -> RawHandle {
         self.as_inner().handle().raw() as *mut _
     }
@@ -15,6 +16,7 @@ fn as_raw_handle(&self) -> RawHandle {
 
 #[stable(feature = "thread_extensions", since = "1.9.0")]
 impl<T> IntoRawHandle for thread::JoinHandle<T> {
+    #[inline]
     fn into_raw_handle(self) -> RawHandle {
         self.into_inner().into_handle().into_raw() as *mut _
     }
index c6509db80c05be2a5cdb94cf89b15801db209cf1..8e6bd76f85f0614fc5a6b210fc1db19921708599 100644 (file)
@@ -802,7 +802,10 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
 
 #[cfg(target_vendor = "uwp")]
 pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
-    return Err(io::Error::new_const(io::ErrorKind::Other, &"hard link are not supported on UWP"));
+    return Err(io::Error::new_const(
+        io::ErrorKind::Unsupported,
+        &"hard link are not supported on UWP",
+    ));
 }
 
 pub fn stat(path: &Path) -> io::Result<FileAttr> {
index 0353c9811f15f79d38c76d2e077cfb1ea0586377..ddb6ac5f55c0d9684f5bc00a65a07d0072bef437 100644 (file)
     }
 }
 
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+    stack_overflow::init();
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+    net::cleanup();
+}
 
 pub fn decode_error_kind(errno: i32) -> ErrorKind {
     match errno as c::DWORD {
@@ -78,6 +87,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
         | c::ERROR_IPSEC_IKE_TIMED_OUT
         | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
         | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut,
+        c::ERROR_CALL_NOT_IMPLEMENTED => return ErrorKind::Unsupported,
         _ => {}
     }
 
index e50adcb28a4b7a30124f9bdde83b767ac27570e2..1ad13254c0846f2e27bfd0a4793b906211ef991f 100644 (file)
@@ -9,7 +9,7 @@
 use crate::sys;
 use crate::sys::c;
 use crate::sys_common::net;
-use crate::sys_common::{self, AsInner, FromInner, IntoInner};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::time::Duration;
 
 use libc::{c_int, c_long, c_ulong, c_void};
@@ -26,23 +26,28 @@ pub mod netc {
 
 pub struct Socket(c::SOCKET);
 
+static INIT: Once = Once::new();
+
 /// Checks whether the Windows socket interface has been started already, and
 /// if not, starts it.
 pub fn init() {
-    static START: Once = Once::new();
-
-    START.call_once(|| unsafe {
+    INIT.call_once(|| unsafe {
         let mut data: c::WSADATA = mem::zeroed();
         let ret = c::WSAStartup(
             0x202, // version 2.2
             &mut data,
         );
         assert_eq!(ret, 0);
+    });
+}
 
-        let _ = sys_common::at_exit(|| {
+pub fn cleanup() {
+    if INIT.is_completed() {
+        // only close the socket interface if it has actually been started
+        unsafe {
             c::WSACleanup();
-        });
-    });
+        }
+    }
 }
 
 /// Returns the last error from the Windows socket interface.
@@ -370,7 +375,7 @@ fn set_no_inherit(&self) -> io::Result<()> {
 
     #[cfg(target_vendor = "uwp")]
     fn set_no_inherit(&self) -> io::Result<()> {
-        Err(io::Error::new_const(io::ErrorKind::Other, &"Unavailable on UWP"))
+        Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP"))
     }
 
     pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
index 187ad4e66c3efcaedf613623ac714e2eccd5fdd4..39efb778207fc5386729b34ce9d4d8fecd7a6e7c 100644 (file)
@@ -37,5 +37,3 @@ pub unsafe fn init() {
     // Set the thread stack guarantee for the main thread.
     let _h = Handler::new();
 }
-
-pub unsafe fn cleanup() {}
index e7236cf359cd50b7b8ac791bd651585ad8002124..afdf7f566ae515eb38b3a3c13ef56839a53c5a4b 100644 (file)
@@ -9,5 +9,3 @@ pub fn new() -> Handler {
 }
 
 pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
diff --git a/library/std/src/sys_common/at_exit_imp.rs b/library/std/src/sys_common/at_exit_imp.rs
deleted file mode 100644 (file)
index 90d5d3a..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-//! Implementation of running at_exit routines
-//!
-//! Documentation can be found on the `rt::at_exit` function.
-
-use crate::mem;
-use crate::ptr;
-use crate::sys_common::mutex::StaticMutex;
-
-type Queue = Vec<Box<dyn FnOnce()>>;
-
-// NB these are specifically not types from `std::sync` as they currently rely
-// on poisoning and this module needs to operate at a lower level than requiring
-// the thread infrastructure to be in place (useful on the borders of
-// initialization/destruction).
-// It is UB to attempt to acquire this mutex reentrantly!
-static LOCK: StaticMutex = StaticMutex::new();
-static mut QUEUE: *mut Queue = ptr::null_mut();
-
-const DONE: *mut Queue = 1_usize as *mut _;
-
-// The maximum number of times the cleanup routines will be run. While running
-// the at_exit closures new ones may be registered, and this count is the number
-// of times the new closures will be allowed to register successfully. After
-// this number of iterations all new registrations will return `false`.
-const ITERS: usize = 10;
-
-unsafe fn init() -> bool {
-    if QUEUE.is_null() {
-        let state: Box<Queue> = box Vec::new();
-        QUEUE = Box::into_raw(state);
-    } else if QUEUE == DONE {
-        // can't re-init after a cleanup
-        return false;
-    }
-
-    true
-}
-
-pub fn cleanup() {
-    for i in 1..=ITERS {
-        unsafe {
-            let queue = {
-                let _guard = LOCK.lock();
-                mem::replace(&mut QUEUE, if i == ITERS { DONE } else { ptr::null_mut() })
-            };
-
-            // make sure we're not recursively cleaning up
-            assert!(queue != DONE);
-
-            // If we never called init, not need to cleanup!
-            if !queue.is_null() {
-                let queue: Box<Queue> = Box::from_raw(queue);
-                for to_run in *queue {
-                    // We are not holding any lock, so reentrancy is fine.
-                    to_run();
-                }
-            }
-        }
-    }
-}
-
-pub fn push(f: Box<dyn FnOnce()>) -> bool {
-    unsafe {
-        let _guard = LOCK.lock();
-        if init() {
-            // We are just moving `f` around, not calling it.
-            // There is no possibility of reentrancy here.
-            (*QUEUE).push(f);
-            true
-        } else {
-            false
-        }
-    }
-}
index 23a3a0e907dcf7ebc5aef1c68b0b83e7e2516cec..7fa6977f2af26dc04c10891c479b084bb19cc340 100644 (file)
 #[cfg(test)]
 mod tests;
 
-use crate::sync::Once;
-use crate::sys;
-
-macro_rules! rtabort {
-    ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
-}
-
-macro_rules! rtassert {
-    ($e:expr) => {
-        if !$e {
-            rtabort!(concat!("assertion failed: ", stringify!($e)));
-        }
-    };
-}
-
-#[allow(unused_macros)] // not used on all platforms
-macro_rules! rtunwrap {
-    ($ok:ident, $e:expr) => {
-        match $e {
-            $ok(v) => v,
-            ref err => {
-                let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
-                rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
-            }
-        }
-    };
-}
-
-pub mod at_exit_imp;
 pub mod backtrace;
 pub mod bytestring;
 pub mod condvar;
@@ -59,9 +30,10 @@ macro_rules! rtunwrap {
 // when generating documentation.
 #[cfg(any(doc, not(windows)))]
 pub mod os_str_bytes;
-pub mod poison;
 pub mod process;
 pub mod remutex;
+#[macro_use]
+pub mod rt;
 pub mod rwlock;
 pub mod thread;
 pub mod thread_info;
@@ -109,30 +81,6 @@ pub trait FromInner<Inner> {
     fn from_inner(inner: Inner) -> Self;
 }
 
-/// Enqueues a procedure to run when the main thread exits.
-///
-/// Currently these closures are only run once the main *Rust* thread exits.
-/// Once the `at_exit` handlers begin running, more may be enqueued, but not
-/// infinitely so. Eventually a handler registration will be forced to fail.
-///
-/// Returns `Ok` if the handler was successfully registered, meaning that the
-/// closure will be run once the main thread exits. Returns `Err` to indicate
-/// that the closure could not be registered, meaning that it is not scheduled
-/// to be run.
-pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
-    if at_exit_imp::push(Box::new(f)) { Ok(()) } else { Err(()) }
-}
-
-/// One-time runtime cleanup.
-pub fn cleanup() {
-    static CLEANUP: Once = Once::new();
-    CLEANUP.call_once(|| unsafe {
-        sys::args::cleanup();
-        sys::stack_overflow::cleanup();
-        at_exit_imp::cleanup();
-    });
-}
-
 // Computes (value*numer)/denom without overflow, as long as both
 // (numer*denom) and the overall result fit into i64 (which is the case
 // for our time conversions).
diff --git a/library/std/src/sys_common/poison.rs b/library/std/src/sys_common/poison.rs
deleted file mode 100644 (file)
index 2ab2c70..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-use crate::error::Error;
-use crate::fmt;
-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,
-}
-
-// Note that the Ordering uses to access the `failed` field of `Flag` below is
-// always `Relaxed`, and that's because this isn't actually protecting any data,
-// it's just a flag whether we've panicked or not.
-//
-// The actual location that this matters is when a mutex is **locked** which is
-// where we have external synchronization ensuring that we see memory
-// reads/writes to this flag.
-//
-// As a result, if it matters, we should see the correct value for `failed` in
-// all cases.
-
-impl Flag {
-    pub const fn new() -> Flag {
-        Flag { failed: AtomicBool::new(false) }
-    }
-
-    #[inline]
-    pub fn borrow(&self) -> LockResult<Guard> {
-        let ret = Guard { panicking: thread::panicking() };
-        if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
-    }
-
-    #[inline]
-    pub fn done(&self, guard: &Guard) {
-        if !guard.panicking && thread::panicking() {
-            self.failed.store(true, Ordering::Relaxed);
-        }
-    }
-
-    #[inline]
-    pub fn get(&self) -> bool {
-        self.failed.load(Ordering::Relaxed)
-    }
-}
-
-pub struct Guard {
-    panicking: bool,
-}
-
-/// A type of error which can be returned whenever a lock is acquired.
-///
-/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock
-/// is held. The precise semantics for when a lock is poisoned is documented on
-/// each lock, but once a lock is poisoned then all future acquisitions will
-/// return this error.
-///
-/// # Examples
-///
-/// ```
-/// use std::sync::{Arc, Mutex};
-/// use std::thread;
-///
-/// let mutex = Arc::new(Mutex::new(1));
-///
-/// // poison the mutex
-/// let c_mutex = Arc::clone(&mutex);
-/// let _ = thread::spawn(move || {
-///     let mut data = c_mutex.lock().unwrap();
-///     *data = 2;
-///     panic!();
-/// }).join();
-///
-/// match mutex.lock() {
-///     Ok(_) => unreachable!(),
-///     Err(p_err) => {
-///         let data = p_err.get_ref();
-///         println!("recovered: {}", data);
-///     }
-/// };
-/// ```
-#[stable(feature = "rust1", since = "1.0.0")]
-pub struct PoisonError<T> {
-    guard: T,
-}
-
-/// An enumeration of possible errors associated with a [`TryLockResult`] which
-/// 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`].
-///
-/// [`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
-    /// the lock.
-    #[stable(feature = "rust1", since = "1.0.0")]
-    Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError<T>),
-    /// The lock could not be acquired at this time because the operation would
-    /// otherwise block.
-    #[stable(feature = "rust1", since = "1.0.0")]
-    WouldBlock,
-}
-
-/// A type alias for the result of a lock method which can be poisoned.
-///
-/// The [`Ok`] variant of this result indicates that the primitive was not
-/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates
-/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries
-/// the associated guard, and it can be acquired through the [`into_inner`]
-/// method.
-///
-/// [`into_inner`]: PoisonError::into_inner
-#[stable(feature = "rust1", since = "1.0.0")]
-pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
-
-/// A type alias for the result of a nonblocking locking method.
-///
-/// 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.
-#[stable(feature = "rust1", since = "1.0.0")]
-pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> fmt::Debug for PoisonError<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        "PoisonError { inner: .. }".fmt(f)
-    }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> fmt::Display for PoisonError<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        "poisoned lock: another task failed inside".fmt(f)
-    }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Error for PoisonError<T> {
-    #[allow(deprecated)]
-    fn description(&self) -> &str {
-        "poisoned lock: another task failed inside"
-    }
-}
-
-impl<T> PoisonError<T> {
-    /// Creates a `PoisonError`.
-    ///
-    /// This is generally created by methods like [`Mutex::lock`] or [`RwLock::read`].
-    #[stable(feature = "sync_poison", since = "1.2.0")]
-    pub fn new(guard: T) -> PoisonError<T> {
-        PoisonError { guard }
-    }
-
-    /// Consumes this error indicating that a lock is poisoned, returning the
-    /// underlying guard to allow access regardless.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::collections::HashSet;
-    /// use std::sync::{Arc, Mutex};
-    /// use std::thread;
-    ///
-    /// let mutex = Arc::new(Mutex::new(HashSet::new()));
-    ///
-    /// // poison the mutex
-    /// let c_mutex = Arc::clone(&mutex);
-    /// let _ = thread::spawn(move || {
-    ///     let mut data = c_mutex.lock().unwrap();
-    ///     data.insert(10);
-    ///     panic!();
-    /// }).join();
-    ///
-    /// let p_err = mutex.lock().unwrap_err();
-    /// let data = p_err.into_inner();
-    /// println!("recovered {} items", data.len());
-    /// ```
-    #[stable(feature = "sync_poison", since = "1.2.0")]
-    pub fn into_inner(self) -> T {
-        self.guard
-    }
-
-    /// Reaches into this error indicating that a lock is poisoned, returning a
-    /// reference to the underlying guard to allow access regardless.
-    #[stable(feature = "sync_poison", since = "1.2.0")]
-    pub fn get_ref(&self) -> &T {
-        &self.guard
-    }
-
-    /// Reaches into this error indicating that a lock is poisoned, returning a
-    /// mutable reference to the underlying guard to allow access regardless.
-    #[stable(feature = "sync_poison", since = "1.2.0")]
-    pub fn get_mut(&mut self) -> &mut T {
-        &mut self.guard
-    }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> From<PoisonError<T>> for TryLockError<T> {
-    fn from(err: PoisonError<T>) -> TryLockError<T> {
-        TryLockError::Poisoned(err)
-    }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> fmt::Debug for TryLockError<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
-            TryLockError::WouldBlock => "WouldBlock".fmt(f),
-        }
-    }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> fmt::Display for TryLockError<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
-            TryLockError::WouldBlock => "try_lock failed because the operation would block",
-        }
-        .fmt(f)
-    }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Error for TryLockError<T> {
-    #[allow(deprecated, deprecated_in_future)]
-    fn description(&self) -> &str {
-        match *self {
-            TryLockError::Poisoned(ref p) => p.description(),
-            TryLockError::WouldBlock => "try_lock failed because the operation would block",
-        }
-    }
-
-    #[allow(deprecated)]
-    fn cause(&self) -> Option<&dyn Error> {
-        match *self {
-            TryLockError::Poisoned(ref p) => Some(p),
-            _ => None,
-        }
-    }
-}
-
-pub fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
-where
-    F: FnOnce(T) -> U,
-{
-    match result {
-        Ok(t) => Ok(f(t)),
-        Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))),
-    }
-}
diff --git a/library/std/src/sys_common/rt.rs b/library/std/src/sys_common/rt.rs
new file mode 100644 (file)
index 0000000..c70f2ec
--- /dev/null
@@ -0,0 +1,64 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::sync::Once;
+use crate::sys;
+use crate::sys_common::thread_info;
+use crate::thread::Thread;
+
+// One-time runtime initialization.
+// Runs before `main`.
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+#[cfg_attr(test, allow(dead_code))]
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+    unsafe {
+        sys::init(argc, argv);
+
+        let main_guard = sys::thread::guard::init();
+        // Next, set up the current Thread with the guard information we just
+        // created. Note that this isn't necessary in general for new threads,
+        // but we just do this to name the main thread and to give it correct
+        // info about the stack bounds.
+        let thread = Thread::new(Some("main".to_owned()));
+        thread_info::set(main_guard, thread);
+    }
+}
+
+// One-time runtime cleanup.
+// Runs after `main` or at program exit.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+#[cfg_attr(test, allow(dead_code))]
+pub fn cleanup() {
+    static CLEANUP: Once = Once::new();
+    CLEANUP.call_once(|| unsafe {
+        // Flush stdout and disable buffering.
+        crate::io::cleanup();
+        // SAFETY: Only called once during runtime cleanup.
+        sys::cleanup();
+    });
+}
+
+macro_rules! rtabort {
+    ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
+}
+
+macro_rules! rtassert {
+    ($e:expr) => {
+        if !$e {
+            rtabort!(concat!("assertion failed: ", stringify!($e)));
+        }
+    };
+}
+
+#[allow(unused_macros)] // not used on all platforms
+macro_rules! rtunwrap {
+    ($ok:ident, $e:expr) => {
+        match $e {
+            $ok(v) => v,
+            ref err => {
+                let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
+                rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
+            }
+        }
+    };
+}
index 70b31b19f824cb16eda7030a235fc7de88d3f4c5..3705d641a1be646ab38436b8aaed032a6ef3e312 100644 (file)
@@ -86,62 +86,3 @@ pub unsafe fn destroy(&self) {
         self.0.destroy()
     }
 }
-
-// the cfg annotations only exist due to dead code warnings. the code itself is portable
-#[cfg(unix)]
-pub struct StaticRWLock(RWLock);
-
-#[cfg(unix)]
-impl StaticRWLock {
-    pub const fn new() -> StaticRWLock {
-        StaticRWLock(RWLock::new())
-    }
-
-    /// Acquires shared access to the underlying lock, blocking the current
-    /// thread to do so.
-    ///
-    /// The lock is automatically unlocked when the returned guard is dropped.
-    #[inline]
-    pub fn read_with_guard(&'static self) -> RWLockReadGuard {
-        // SAFETY: All methods require static references, therefore self
-        // cannot be moved between invocations.
-        unsafe {
-            self.0.read();
-        }
-        RWLockReadGuard(&self.0)
-    }
-
-    /// Acquires write access to the underlying lock, blocking the current thread
-    /// to do so.
-    ///
-    /// The lock is automatically unlocked when the returned guard is dropped.
-    #[inline]
-    pub fn write_with_guard(&'static self) -> RWLockWriteGuard {
-        // SAFETY: All methods require static references, therefore self
-        // cannot be moved between invocations.
-        unsafe {
-            self.0.write();
-        }
-        RWLockWriteGuard(&self.0)
-    }
-}
-
-#[cfg(unix)]
-pub struct RWLockReadGuard(&'static RWLock);
-
-#[cfg(unix)]
-impl Drop for RWLockReadGuard {
-    fn drop(&mut self) {
-        unsafe { self.0.read_unlock() }
-    }
-}
-
-#[cfg(unix)]
-pub struct RWLockWriteGuard(&'static RWLock);
-
-#[cfg(unix)]
-impl Drop for RWLockWriteGuard {
-    fn drop(&mut self) {
-        unsafe { self.0.write_unlock() }
-    }
-}
index dd438858c37cd4c234ac48fe3a971445da9370fe..abd5b7784834c77154c3667a91d93137a4c69c23 100644 (file)
@@ -100,7 +100,7 @@ pub struct LocalKey<T: 'static> {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl<T: 'static> fmt::Debug for LocalKey<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("LocalKey { .. }")
+        f.debug_struct("LocalKey").finish_non_exhaustive()
     }
 }
 
@@ -133,6 +133,15 @@ macro_rules! thread_local {
     // empty (base case for the recursion)
     () => {};
 
+    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => (
+        $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, const $init);
+        $crate::thread_local!($($rest)*);
+    );
+
+    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) => (
+        $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, const $init);
+    );
+
     // process multiple declarations
     ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
         $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
@@ -151,6 +160,101 @@ macro_rules! thread_local {
 #[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
 #[allow_internal_unsafe]
 macro_rules! __thread_local_inner {
+    // used to generate the `LocalKey` value for const-initialized thread locals
+    (@key $t:ty, const $init:expr) => {{
+        unsafe fn __getit() -> $crate::option::Option<&'static $t> {
+            const _REQUIRE_UNSTABLE: () = $crate::thread::require_unstable_const_init_thread_local();
+
+            // wasm without atomics maps directly to `static mut`, and dtors
+            // aren't implemented because thread dtors aren't really a thing
+            // on wasm right now
+            //
+            // FIXME(#84224) this should come after the `target_thread_local`
+            // block.
+            #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))]
+            {
+                static mut VAL: $t = $init;
+                Some(&VAL)
+            }
+
+            // If the platform has support for `#[thread_local]`, use it.
+            #[cfg(all(
+                target_thread_local,
+                not(all(target_arch = "wasm32", not(target_feature = "atomics"))),
+            ))]
+            {
+                // If a dtor isn't needed we can do something "very raw" and
+                // just get going.
+                if !$crate::mem::needs_drop::<$t>() {
+                    #[thread_local]
+                    static mut VAL: $t = $init;
+                    unsafe {
+                        return Some(&VAL)
+                    }
+                }
+
+                #[thread_local]
+                static mut VAL: $t = $init;
+                // 0 == dtor not registered
+                // 1 == dtor registered, dtor not run
+                // 2 == dtor registered and is running or has run
+                #[thread_local]
+                static mut STATE: u8 = 0;
+
+                unsafe extern "C" fn destroy(ptr: *mut u8) {
+                    let ptr = ptr as *mut $t;
+
+                    unsafe {
+                        debug_assert_eq!(STATE, 1);
+                        STATE = 2;
+                        $crate::ptr::drop_in_place(ptr);
+                    }
+                }
+
+                unsafe {
+                    match STATE {
+                        // 0 == we haven't registered a destructor, so do
+                        //   so now.
+                        0 => {
+                            $crate::thread::__FastLocalKeyInner::<$t>::register_dtor(
+                                $crate::ptr::addr_of_mut!(VAL) as *mut u8,
+                                destroy,
+                            );
+                            STATE = 1;
+                            Some(&VAL)
+                        }
+                        // 1 == the destructor is registered and the value
+                        //   is valid, so return the pointer.
+                        1 => Some(&VAL),
+                        // otherwise the destructor has already run, so we
+                        // can't give access.
+                        _ => None,
+                    }
+                }
+            }
+
+            // On platforms without `#[thread_local]` we fall back to the
+            // same implementation as below for os thread locals.
+            #[cfg(all(
+                not(target_thread_local),
+                not(all(target_arch = "wasm32", not(target_feature = "atomics"))),
+            ))]
+            {
+                #[inline]
+                const fn __init() -> $t { $init }
+                static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
+                    $crate::thread::__OsLocalKeyInner::new();
+                #[allow(unused_unsafe)]
+                unsafe { __KEY.get(__init) }
+            }
+        }
+
+        unsafe {
+            $crate::thread::LocalKey::new(__getit)
+        }
+    }};
+
+    // used to generate the `LocalKey` value for `thread_local!`
     (@key $t:ty, $init:expr) => {
         {
             #[inline]
@@ -188,9 +292,9 @@ unsafe fn __getit() -> $crate::option::Option<&'static $t> {
             }
         }
     };
-    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
+    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
         $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
-            $crate::__thread_local_inner!(@key $t, $init);
+            $crate::__thread_local_inner!(@key $t, $($init)*);
     }
 }
 
@@ -368,7 +472,7 @@ unsafe impl<T> Sync for Key<T> {}
 
     impl<T> fmt::Debug for Key<T> {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.pad("Key { .. }")
+            f.debug_struct("Key").finish_non_exhaustive()
         }
     }
 
@@ -433,7 +537,7 @@ pub struct Key<T> {
 
     impl<T> fmt::Debug for Key<T> {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.pad("Key { .. }")
+            f.debug_struct("Key").finish_non_exhaustive()
         }
     }
 
@@ -442,6 +546,15 @@ pub const fn new() -> Key<T> {
             Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
         }
 
+        // note that this is just a publically-callable function only for the
+        // const-initialized form of thread locals, basically a way to call the
+        // free `register_dtor` function defined elsewhere in libstd.
+        pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+            unsafe {
+                register_dtor(a, dtor);
+            }
+        }
+
         pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
             // SAFETY: See the definitions of `LazyKeyInner::get` and
             // `try_initialize` for more informations.
@@ -538,7 +651,7 @@ pub struct Key<T> {
 
     impl<T> fmt::Debug for Key<T> {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.pad("Key { .. }")
+            f.debug_struct("Key").finish_non_exhaustive()
         }
     }
 
index 4fb0a0890826ebed02b802a0552d0741fcd15f28..80e6798d847b1d2faed5b2bbbb1493357d60e835 100644 (file)
@@ -1,6 +1,6 @@
 use crate::cell::{Cell, UnsafeCell};
 use crate::sync::mpsc::{channel, Sender};
-use crate::thread;
+use crate::thread::{self, LocalKey};
 use crate::thread_local;
 
 struct Foo(Sender<()>);
@@ -15,74 +15,90 @@ fn drop(&mut self) {
 #[test]
 fn smoke_no_dtor() {
     thread_local!(static FOO: Cell<i32> = Cell::new(1));
+    run(&FOO);
+    thread_local!(static FOO2: Cell<i32> = const { Cell::new(1) });
+    run(&FOO2);
 
-    FOO.with(|f| {
-        assert_eq!(f.get(), 1);
-        f.set(2);
-    });
-    let (tx, rx) = channel();
-    let _t = thread::spawn(move || {
-        FOO.with(|f| {
+    fn run(key: &'static LocalKey<Cell<i32>>) {
+        key.with(|f| {
             assert_eq!(f.get(), 1);
+            f.set(2);
         });
-        tx.send(()).unwrap();
-    });
-    rx.recv().unwrap();
+        let t = thread::spawn(move || {
+            key.with(|f| {
+                assert_eq!(f.get(), 1);
+            });
+        });
+        t.join().unwrap();
 
-    FOO.with(|f| {
-        assert_eq!(f.get(), 2);
-    });
+        key.with(|f| {
+            assert_eq!(f.get(), 2);
+        });
+    }
 }
 
 #[test]
 fn states() {
-    struct Foo;
+    struct Foo(&'static LocalKey<Foo>);
     impl Drop for Foo {
         fn drop(&mut self) {
-            assert!(FOO.try_with(|_| ()).is_err());
+            assert!(self.0.try_with(|_| ()).is_err());
         }
     }
-    thread_local!(static FOO: Foo = Foo);
 
-    thread::spawn(|| {
-        assert!(FOO.try_with(|_| ()).is_ok());
-    })
-    .join()
-    .ok()
-    .expect("thread panicked");
+    thread_local!(static FOO: Foo = Foo(&FOO));
+    run(&FOO);
+    thread_local!(static FOO2: Foo = const { Foo(&FOO2) });
+    run(&FOO2);
+
+    fn run(foo: &'static LocalKey<Foo>) {
+        thread::spawn(move || {
+            assert!(foo.try_with(|_| ()).is_ok());
+        })
+        .join()
+        .unwrap();
+    }
 }
 
 #[test]
 fn smoke_dtor() {
     thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
-
-    let (tx, rx) = channel();
-    let _t = thread::spawn(move || unsafe {
-        let mut tx = Some(tx);
-        FOO.with(|f| {
-            *f.get() = Some(Foo(tx.take().unwrap()));
+    run(&FOO);
+    thread_local!(static FOO2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) });
+    run(&FOO2);
+
+    fn run(key: &'static LocalKey<UnsafeCell<Option<Foo>>>) {
+        let (tx, rx) = channel();
+        let t = thread::spawn(move || unsafe {
+            let mut tx = Some(tx);
+            key.with(|f| {
+                *f.get() = Some(Foo(tx.take().unwrap()));
+            });
         });
-    });
-    rx.recv().unwrap();
+        rx.recv().unwrap();
+        t.join().unwrap();
+    }
 }
 
 #[test]
 fn circular() {
-    struct S1;
-    struct S2;
+    struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>);
+    struct S2(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>);
     thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
     thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None));
-    static mut HITS: u32 = 0;
+    thread_local!(static K3: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
+    thread_local!(static K4: UnsafeCell<Option<S2>> = const { UnsafeCell::new(None) });
+    static mut HITS: usize = 0;
 
     impl Drop for S1 {
         fn drop(&mut self) {
             unsafe {
                 HITS += 1;
-                if K2.try_with(|_| ()).is_err() {
+                if self.1.try_with(|_| ()).is_err() {
                     assert_eq!(HITS, 3);
                 } else {
                     if HITS == 1 {
-                        K2.with(|s| *s.get() = Some(S2));
+                        self.1.with(|s| *s.get() = Some(S2(self.0, self.1)));
                     } else {
                         assert_eq!(HITS, 3);
                     }
@@ -94,38 +110,54 @@ impl Drop for S2 {
         fn drop(&mut self) {
             unsafe {
                 HITS += 1;
-                assert!(K1.try_with(|_| ()).is_ok());
+                assert!(self.0.try_with(|_| ()).is_ok());
                 assert_eq!(HITS, 2);
-                K1.with(|s| *s.get() = Some(S1));
+                self.0.with(|s| *s.get() = Some(S1(self.0, self.1)));
             }
         }
     }
 
     thread::spawn(move || {
-        drop(S1);
+        drop(S1(&K1, &K2));
+    })
+    .join()
+    .unwrap();
+
+    unsafe {
+        HITS = 0;
+    }
+
+    thread::spawn(move || {
+        drop(S1(&K3, &K4));
     })
     .join()
-    .ok()
-    .expect("thread panicked");
+    .unwrap();
 }
 
 #[test]
 fn self_referential() {
-    struct S1;
+    struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>);
+
     thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
+    thread_local!(static K2: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
 
     impl Drop for S1 {
         fn drop(&mut self) {
-            assert!(K1.try_with(|_| ()).is_err());
+            assert!(self.0.try_with(|_| ()).is_err());
         }
     }
 
     thread::spawn(move || unsafe {
-        K1.with(|s| *s.get() = Some(S1));
+        K1.with(|s| *s.get() = Some(S1(&K1)));
     })
     .join()
-    .ok()
-    .expect("thread panicked");
+    .unwrap();
+
+    thread::spawn(move || unsafe {
+        K2.with(|s| *s.get() = Some(S1(&K2)));
+    })
+    .join()
+    .unwrap();
 }
 
 // Note that this test will deadlock if TLS destructors aren't run (this
@@ -152,3 +184,26 @@ fn drop(&mut self) {
     });
     rx.recv().unwrap();
 }
+
+#[test]
+fn dtors_in_dtors_in_dtors_const_init() {
+    struct S1(Sender<()>);
+    thread_local!(static K1: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
+    thread_local!(static K2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) });
+
+    impl Drop for S1 {
+        fn drop(&mut self) {
+            let S1(ref tx) = *self;
+            unsafe {
+                let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
+            }
+        }
+    }
+
+    let (tx, rx) = channel();
+    let _t = thread::spawn(move || unsafe {
+        let mut tx = Some(tx);
+        K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
+    });
+    rx.recv().unwrap();
+}
index ffdf4be1584574df92104cf77514bc2564aa921b..30d8c2a1b6fa01eff5308567e268043647dd471c 100644 (file)
 #[doc(hidden)]
 pub use self::local::statik::Key as __StaticLocalKeyInner;
 
+// This is only used to make thread locals with `const { .. }` initialization
+// expressions unstable. If and/or when that syntax is stabilized with thread
+// locals this will simply be removed.
+#[doc(hidden)]
+#[unstable(feature = "thread_local_const_init", issue = "84223")]
+pub const fn require_unstable_const_init_thread_local() {}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Builder
 ////////////////////////////////////////////////////////////////////////////////
@@ -1406,7 +1413,7 @@ fn into_inner(self) -> imp::Thread {
 #[stable(feature = "std_debug", since = "1.16.0")]
 impl<T> fmt::Debug for JoinHandle<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("JoinHandle { .. }")
+        f.debug_struct("JoinHandle").finish_non_exhaustive()
     }
 }
 
index 169154187f2500004904fd69f2da0a336faea120..7869ba2c04178b60a28148e1c2ff296fada7745c 100644 (file)
@@ -1,6 +1,4 @@
 //! Benchmarking module.
-pub use std::hint::black_box;
-
 use super::{
     event::CompletedTest,
     options::BenchMode,
 use std::sync::{Arc, Mutex};
 use std::time::{Duration, Instant};
 
+/// An identity function that *__hints__* to the compiler to be maximally pessimistic about what
+/// `black_box` could do.
+///
+/// See [`std::hint::black_box`] for details.
+#[inline(always)]
+pub fn black_box<T>(dummy: T) -> T {
+    std::hint::black_box(dummy)
+}
+
 /// Manager of the benchmarking runs.
 ///
 /// This is fed into functions marked with `#[bench]` to allow for
index 2e0864f303cc93dea6ce01aabf90eea6029ff788..9adc099aaa56618812e3dda62bd14ef7ac6d8e20 100644 (file)
@@ -24,6 +24,7 @@
 #![feature(rustc_private)]
 #![feature(nll)]
 #![feature(available_concurrency)]
+#![feature(bench_black_box)]
 #![feature(internal_output_capture)]
 #![feature(panic_unwind)]
 #![feature(staged_api)]
index 38ebe0e52083d8dab856b5451e4f7f95295e2262..aee3c8324bc111041f0280c804e39c0454adb4e1 100644 (file)
@@ -74,7 +74,7 @@ fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
 
         let mut tarball = Tarball::new(builder, "rust-docs", &host.triple);
         tarball.set_product_name("Rust Documentation");
-        tarball.add_dir(&builder.doc_out(host), dest);
+        tarball.add_bulk_dir(&builder.doc_out(host), dest);
         tarball.add_file(&builder.src.join("src/doc/robots.txt"), dest, 0o644);
         Some(tarball.generate())
     }
@@ -107,7 +107,7 @@ fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
 
         let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple);
         tarball.set_product_name("Rustc Documentation");
-        tarball.add_dir(&builder.compiler_doc_out(host), "share/doc/rust/html/rustc");
+        tarball.add_bulk_dir(&builder.compiler_doc_out(host), "share/doc/rust/html/rustc");
         Some(tarball.generate())
     }
 }
index dc96fd819d5588020cab91144f694049586a15b0..a32b92ef1af83d0da472cad66b3e9a6ce0141d79 100644 (file)
@@ -461,15 +461,6 @@ fn run(self, builder: &Builder<'_>) {
         // create correct links between crates because rustdoc depends on the
         // existence of the output directories to know if it should be a local
         // or remote link.
-        //
-        // There's also a mild hack here where we build the first crate in this
-        // list, core, twice. This is currently necessary to make sure that
-        // cargo's cached rustc/rustdoc versions are up to date which means
-        // cargo won't delete the out_dir we create for the stampfile.
-        // Essentially any crate could go into the first slot here as it's
-        // output directory will be deleted by us (as cargo will purge the stamp
-        // file during the first slot's run), and core is relatively fast to
-        // build so works OK to fill this 'dummy' slot.
         let krates = ["core", "alloc", "std", "proc_macro", "test"];
         for krate in &krates {
             run_cargo_rustdoc_for(krate);
@@ -479,12 +470,16 @@ fn run(self, builder: &Builder<'_>) {
         // Look for library/std, library/core etc in the `x.py doc` arguments and
         // open the corresponding rendered docs.
         for path in builder.paths.iter().map(components_simplified) {
-            if path.get(0) == Some(&"library") {
-                let requested_crate = &path[1];
-                if krates.contains(&requested_crate) {
-                    let index = out.join(requested_crate).join("index.html");
-                    open(builder, &index);
-                }
+            let requested_crate = if path.get(0) == Some(&"library") {
+                &path[1]
+            } else if !path.is_empty() {
+                &path[0]
+            } else {
+                continue;
+            };
+            if krates.contains(&requested_crate) {
+                let index = out.join(requested_crate).join("index.html");
+                open(builder, &index);
             }
         }
     }
index 68e7dc80067268abe28ae010d47a88f4edfd2c72..13ee909afd5e408897a32d1a23395dd0efe5629e 100644 (file)
 use crate::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::config::{Config, TargetSelection};
 
+#[cfg(target_os = "illumos")]
+const SHELL: &str = "bash";
+#[cfg(not(target_os = "illumos"))]
+const SHELL: &str = "sh";
+
 fn install_sh(
     builder: &Builder<'_>,
     package: &str,
@@ -37,7 +42,7 @@ fn install_sh(
     let empty_dir = builder.out.join("tmp/empty_dir");
     t!(fs::create_dir_all(&empty_dir));
 
-    let mut cmd = Command::new("sh");
+    let mut cmd = Command::new(SHELL);
     cmd.current_dir(&empty_dir)
         .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
         .arg(format!("--prefix={}", prepare_dir(prefix)))
index c06ceb80c6ae028e425f4f07c3ae3a9083462516..bde0a96f03013d22aeb30a80e8a600a15f252ebc 100644 (file)
@@ -814,6 +814,9 @@ fn supported_sanitizers(
         "x86_64-unknown-linux-gnu" => {
             common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
         }
+        "x86_64-unknown-linux-musl" => {
+            common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
+        }
         _ => Vec::new(),
     }
 }
index b02d7e062a524e4076b9d1d52113d5b6a8e4d7b4..9ff5c2327e0f77a66ac9e72a6ed61b123776ac75 100644 (file)
@@ -99,6 +99,7 @@ pub(crate) struct Tarball<'a> {
     temp_dir: PathBuf,
     image_dir: PathBuf,
     overlay_dir: PathBuf,
+    bulk_dirs: Vec<PathBuf>,
 
     include_target_in_component_name: bool,
     is_preview: bool,
@@ -137,6 +138,7 @@ fn new_inner(builder: &'a Builder<'a>, component: &str, target: Option<String>)
             temp_dir,
             image_dir,
             overlay_dir,
+            bulk_dirs: Vec::new(),
 
             include_target_in_component_name: false,
             is_preview: false,
@@ -201,6 +203,11 @@ pub(crate) fn add_dir(&self, src: impl AsRef<Path>, dest: impl AsRef<Path>) {
         self.builder.cp_r(src.as_ref(), &dest);
     }
 
+    pub(crate) fn add_bulk_dir(&mut self, src: impl AsRef<Path>, dest: impl AsRef<Path>) {
+        self.bulk_dirs.push(dest.as_ref().to_path_buf());
+        self.add_dir(src, dest);
+    }
+
     pub(crate) fn generate(self) -> GeneratedTarball {
         let mut component_name = self.component.clone();
         if self.is_preview {
@@ -221,6 +228,16 @@ pub(crate) fn generate(self) -> GeneratedTarball {
                 .arg("--image-dir")
                 .arg(&this.image_dir)
                 .arg(format!("--component-name={}", &component_name));
+
+            if let Some((dir, dirs)) = this.bulk_dirs.split_first() {
+                let mut arg = dir.as_os_str().to_os_string();
+                for dir in dirs {
+                    arg.push(",");
+                    arg.push(dir);
+                }
+                cmd.arg("--bulk-dirs").arg(&arg);
+            }
+
             this.non_bare_args(cmd);
         })
     }
index dbb4be550dd70072efaaa4b525c901892ffc75e4..7124ee3aeb53b6506ffc287a19f6f0da1aa41cfe 100644 (file)
@@ -644,7 +644,7 @@ CT_EXPAT_PATCH_GLOBAL=y
 CT_EXPAT_PATCH_ORDER="global"
 CT_EXPAT_V_2_2=y
 # CT_EXPAT_NO_VERSIONS is not set
-CT_EXPAT_VERSION="2.2.6"
+CT_EXPAT_VERSION="2.3.0"
 CT_EXPAT_MIRRORS="http://downloads.sourceforge.net/project/expat/expat/${CT_EXPAT_VERSION}"
 CT_EXPAT_ARCHIVE_FILENAME="@{pkg_name}-@{version}"
 CT_EXPAT_ARCHIVE_DIRNAME="@{pkg_name}-@{version}"
index 95cf1183fc17a0a52300fbfbc40ed9c7ef8385d7..ad2a8c771de9caed4c7a9106d0d51420e1b2aa77 100755 (executable)
@@ -12,7 +12,7 @@ RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \
 
 # Download and build a single-file stress test benchmark on perf.rust-lang.org.
 function pgo_perf_benchmark {
-    local PERF=e095f5021bf01cf3800f50b3a9f14a9683eb3e4e
+    local PERF=9442def56a39d742bf27ebcc3e0614cf117e1bc2
     local github_prefix=https://raw.githubusercontent.com/rust-lang/rustc-perf/$PERF
     local name=$1
     curl -o /tmp/$name.rs $github_prefix/collector/benchmarks/$name/src/lib.rs
index af5834525fa791ca228892fd553a8847e95351a4..b269aab98142d5241ef2b481dba9be1619ec5e24 100644 (file)
@@ -13,6 +13,7 @@
 - [JSON Output](json.md)
 - [Tests](tests/index.md)
 - [Platform Support](platform-support.md)
+- [Target Tier Policy](target-tier-policy.md)
 - [Targets](targets/index.md)
     - [Built-in Targets](targets/built-in.md)
     - [Custom Targets](targets/custom.md)
index f352746d3fbe9bc67ccea9e14706a082d52009c4..7f57d476aa9fbfa03af5968acac0dcb1c497a26a 100644 (file)
@@ -7,7 +7,8 @@
 </style>
 
 Support for different platforms are organized into three tiers, each with a
-different set of guarantees.
+different set of guarantees. For more information on the policies for targets
+at each tier, see the [Target Tier Policy](target-tier-policy.md).
 
 Platforms are identified by their "target triple" which is the string to
 inform the compiler what kind of output should be produced. The columns in the
@@ -179,7 +180,7 @@ target | std | host | notes
 `i386-apple-ios` | ✓ |  | 32-bit x86 iOS
 `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.7+, Lion+)
 `i686-pc-windows-msvc` | ✓ |  | 32-bit Windows XP support
-`i686-unknown-uefi` | ? |  | 32-bit UEFI
+`i686-unknown-uefi` | * |  | 32-bit UEFI
 `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku
 `i686-unknown-netbsd` | ✓ | ✓ | NetBSD/i386 with SSE2
 `i686-unknown-openbsd` | ✓ | ✓ | 32-bit OpenBSD
@@ -228,7 +229,7 @@ target | std | host | notes
 `x86_64-unknown-none-hermitkernel` | ? |  | HermitCore kernel
 `x86_64-unknown-l4re-uclibc` | ? |  |
 `x86_64-unknown-openbsd` | ✓ | ✓ | 64-bit OpenBSD
-`x86_64-unknown-uefi` | ? |  |
+`x86_64-unknown-uefi` | * |  | 64-bit UEFI
 `x86_64-uwp-windows-gnu` | ✓ |  |
 `x86_64-uwp-windows-msvc` | ✓ |  |
 `x86_64-wrs-vxworks` | ? |  |
diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md
new file mode 100644 (file)
index 0000000..463f560
--- /dev/null
@@ -0,0 +1,639 @@
+# Target Tier Policy
+
+Rust provides three tiers of target support:
+
+- Rust provides no guarantees about tier 3 targets; they exist in the codebase,
+  but may or may not build.
+- Rust's continuous integration checks that tier 2 targets will always build,
+  but they may or may not pass tests.
+- Rust's continuous integration checks that tier 1 targets will always build
+  and pass tests.
+
+Adding a new tier 3 target imposes minimal requirements; we focus primarily on
+avoiding disruption to other ongoing Rust development.
+
+Tier 2 and tier 1 targets place work on Rust project developers as a whole, to
+avoid breaking the target. The broader Rust community may also feel more
+inclined to support higher-tier targets in their crates (though they are not
+obligated to do so). Thus, these tiers require commensurate and ongoing efforts
+from the maintainers of the target, to demonstrate value and to minimize any
+disruptions to ongoing Rust development.
+
+This policy defines the requirements for accepting a proposed target at a given
+level of support.
+
+Each tier builds on all the requirements from the previous tier, unless
+overridden by a stronger requirement. Targets at tier 2 and tier 1 may also
+provide *host tools* (such as `rustc` and `cargo`); each of those tiers
+includes a set of supplementary requirements that must be met if supplying host
+tools for the target. A target at tier 2 or tier 1 is not required to supply
+host tools, but if it does, it must meet the corresponding additional
+requirements for host tools.
+
+The policy for each tier also documents the Rust governance teams that must
+approve the addition of any target at that tier. Those teams are responsible
+for reviewing and evaluating the target, based on these requirements and their
+own judgment. Those teams may apply additional requirements, including
+subjective requirements, such as to deal with issues not foreseen by this
+policy. (Such requirements may subsequently motivate additions to this policy.)
+
+While these criteria attempt to document the policy, that policy still involves
+human judgment. Targets must fulfill the spirit of the requirements as well, as
+determined by the judgment of the approving teams. Reviewers and team members
+evaluating targets and target-specific patches should always use their own best
+judgment regarding the quality of work, and the suitability of a target for the
+Rust project. Neither this policy nor any decisions made regarding targets
+shall create any binding agreement or estoppel by any party.
+
+Before filing an issue or pull request (PR) to introduce or promote a target,
+the target should already meet the corresponding tier requirements. This does
+not preclude an existing target's maintainers using issues (on the Rust
+repository or otherwise) to track requirements that have not yet been met, as
+appropriate; however, before officially proposing the introduction or promotion
+of a target, it should meet all of the necessary requirements. A target
+proposal is encouraged to quote the corresponding requirements verbatim as part
+of explaining how the target meets those requirements.
+
+For a list of all supported targets and their corresponding tiers ("tier 3",
+"tier 2", "tier 2 with host tools", "tier 1", or "tier 1 with host tools"), see
+[platform support](platform-support.md).
+
+Note that a target must have already received approval for the next lower tier,
+and spent a reasonable amount of time at that tier, before making a proposal
+for promotion to the next higher tier; this is true even if a target meets the
+requirements for several tiers at once. This policy leaves the precise
+interpretation of "reasonable amount of time" up to the approving teams; those
+teams may scale the amount of time required based on their confidence in the
+target and its demonstrated track record at its current tier. At a minimum,
+multiple stable releases of Rust should typically occur between promotions of a
+target.
+
+The availability or tier of a target in stable Rust is not a hard stability
+guarantee about the future availability or tier of that target. Higher-level
+target tiers are an increasing commitment to the support of a target, and we
+will take that commitment and potential disruptions into account when
+evaluating the potential demotion or removal of a target that has been part of
+a stable release. The promotion or demotion of a target will not generally
+affect existing stable releases, only current development and future releases.
+
+In this policy, the words "must" and "must not" specify absolute requirements
+that a target must meet to qualify for a tier. The words "should" and "should
+not" specify requirements that apply in almost all cases, but for which the
+approving teams may grant an exception for good reason. The word "may"
+indicates something entirely optional, and does not indicate guidance or
+recommendations. This language is based on [IETF RFC
+2119](https://tools.ietf.org/html/rfc2119).
+
+## Tier 3 target policy
+
+At this tier, the Rust project provides no official support for a target, so we
+place minimal requirements on the introduction of targets.
+
+A proposed new tier 3 target must be reviewed and approved by a member of the
+compiler team based on these requirements. The reviewer may choose to gauge
+broader compiler team consensus via a Major Change Proposal (MCP).
+
+A proposed target or target-specific patch that substantially changes code
+shared with other targets (not just target-specific code) must be reviewed and
+approved by the appropriate team for that shared code before acceptance.
+
+- A tier 3 target must have a designated developer or developers (the "target
+  maintainers") on record to be CCed when issues arise regarding the target.
+  (The mechanism to track and CC such developers may evolve over time.)
+- Targets must use naming consistent with any existing targets; for instance, a
+  target for the same CPU or OS as an existing Rust target should use the same
+  name for that CPU or OS. Targets should normally use the same names and
+  naming conventions as used elsewhere in the broader ecosystem beyond Rust
+  (such as in other toolchains), unless they have a very good reason to
+  diverge. Changing the name of a target can be highly disruptive, especially
+  once the target reaches a higher tier, so getting the name right is important
+  even for a tier 3 target.
+  - Target names should not introduce undue confusion or ambiguity unless
+    absolutely necessary to maintain ecosystem compatibility. For example, if
+    the name of the target makes people extremely likely to form incorrect
+    beliefs about what it targets, the name should be changed or augmented to
+    disambiguate it.
+- Tier 3 targets may have unusual requirements to build or use, but must not
+  create legal issues or impose onerous legal terms for the Rust project or for
+  Rust developers or users.
+  - The target must not introduce license incompatibilities.
+  - Anything added to the Rust repository must be under the standard Rust
+    license (`MIT OR Apache-2.0`).
+  - The target must not cause the Rust tools or libraries built for any other
+    host (even when supporting cross-compilation to the target) to depend
+    on any new dependency less permissive than the Rust licensing policy. This
+    applies whether the dependency is a Rust crate that would require adding
+    new license exceptions (as specified by the `tidy` tool in the
+    rust-lang/rust repository), or whether the dependency is a native library
+    or binary. In other words, the introduction of the target must not cause a
+    user installing or running a version of Rust or the Rust tools to be
+    subject to any new license requirements.
+  - If the target supports building host tools (such as `rustc` or `cargo`),
+    those host tools must not depend on proprietary (non-FOSS) libraries, other
+    than ordinary runtime libraries supplied by the platform and commonly used
+    by other binaries built for the target. For instance, `rustc` built for the
+    target may depend on a common proprietary C runtime library or console
+    output library, but must not depend on a proprietary code generation
+    library or code optimization library. Rust's license permits such
+    combinations, but the Rust project has no interest in maintaining such
+    combinations within the scope of Rust itself, even at tier 3.
+  - Targets should not require proprietary (non-FOSS) components to link a
+    functional binary or library.
+  - "onerous" here is an intentionally subjective term. At a minimum, "onerous"
+    legal/licensing terms include but are *not* limited to: non-disclosure
+    requirements, non-compete requirements, contributor license agreements
+    (CLAs) or equivalent, "non-commercial"/"research-only"/etc terms,
+    requirements conditional on the employer or employment of any particular
+    Rust developers, revocable terms, any requirements that create liability
+    for the Rust project or its developers or users, or any requirements that
+    adversely affect the livelihood or prospects of the Rust project or its
+    developers or users.
+- Neither this policy nor any decisions made regarding targets shall create any
+  binding agreement or estoppel by any party. If any member of an approving
+  Rust team serves as one of the maintainers of a target, or has any legal or
+  employment requirement (explicit or implicit) that might affect their
+  decisions regarding a target, they must recuse themselves from any approval
+  decisions regarding the target's tier status, though they may otherwise
+  participate in discussions.
+  - This requirement does not prevent part or all of this policy from being
+    cited in an explicit contract or work agreement (e.g. to implement or
+    maintain support for a target). This requirement exists to ensure that a
+    developer or team responsible for reviewing and approving a target does not
+    face any legal threats or obligations that would prevent them from freely
+    exercising their judgment in such approval, even if such judgment involves
+    subjective matters or goes beyond the letter of these requirements.
+- Tier 3 targets should attempt to implement as much of the standard libraries
+  as possible and appropriate (`core` for most targets, `alloc` for targets
+  that can support dynamic memory allocation, `std` for targets with an
+  operating system or equivalent layer of system-provided functionality), but
+  may leave some code unimplemented (either unavailable or stubbed out as
+  appropriate), whether because the target makes it impossible to implement or
+  challenging to implement. The authors of pull requests are not obligated to
+  avoid calling any portions of the standard library on the basis of a tier 3
+  target not implementing those portions.
+- The target must provide documentation for the Rust community explaining how
+  to build for the target, using cross-compilation if possible. If the target
+  supports running tests (even if they do not pass), the documentation must
+  explain how to run tests for the target, using emulation if possible or
+  dedicated hardware if necessary.
+- Tier 3 targets must not impose burden on the authors of pull requests, or
+  other developers in the community, to maintain the target. In particular,
+  do not post comments (automated or manual) on a PR that derail or suggest a
+  block on the PR based on a tier 3 target. Do not send automated messages or
+  notifications (via any medium, including via `@`) to a PR author or others
+  involved with a PR regarding a tier 3 target, unless they have opted into
+  such messages.
+  - Backlinks such as those generated by the issue/PR tracker when linking to
+    an issue or PR are not considered a violation of this policy, within
+    reason. However, such messages (even on a separate repository) must not
+    generate notifications to anyone involved with a PR who has not requested
+    such notifications.
+- Patches adding or updating tier 3 targets must not break any existing tier 2
+  or tier 1 target, and must not knowingly break another tier 3 target without
+  approval of either the compiler team or the maintainers of the other tier 3
+  target.
+  - In particular, this may come up when working on closely related targets,
+    such as variations of the same architecture with different features. Avoid
+    introducing unconditional uses of features that another variation of the
+    target may not have; use conditional compilation or runtime detection, as
+    appropriate, to let each target run code supported by that target.
+
+If a tier 3 target stops meeting these requirements, or the target maintainers
+no longer have interest or time, or the target shows no signs of activity and
+has not built for some time, or removing the target would improve the quality
+of the Rust codebase, we may post a PR to remove it; any such PR will be CCed
+to the target maintainers (and potentially other people who have previously
+worked on the target), to check potential interest in improving the situation.
+
+## Tier 2 target policy
+
+At this tier, the Rust project guarantees that a target builds, and will reject
+patches that fail to build on a target. Thus, we place requirements that ensure
+the target will not block forward progress of the Rust project.
+
+A proposed new tier 2 target must be reviewed and approved by the compiler team
+based on these requirements. Such review and approval may occur via a Major
+Change Proposal (MCP).
+
+In addition, the infrastructure team must approve the integration of the target
+into Continuous Integration (CI), and the tier 2 CI-related requirements. This
+review and approval may take place in a PR adding the target to CI, or simply
+by an infrastructure team member reporting the outcome of a team discussion.
+
+- A tier 2 target must have value to people other than its maintainers. (It may
+  still be a niche target, but it must not be exclusively useful for an
+  inherently closed group.)
+- A tier 2 target must have a designated team of developers (the "target
+  maintainers") available to consult on target-specific build-breaking issues,
+  or if necessary to develop target-specific language or library implementation
+  details. This team must have at least 2 developers.
+  - The target maintainers should not only fix target-specific issues, but
+    should use any such issue as an opportunity to educate the Rust community
+    about portability to their target, and enhance documentation of the target.
+- The target must not place undue burden on Rust developers not specifically
+  concerned with that target. Rust developers are expected to not gratuitously
+  break a tier 2 target, but are not expected to become experts in every tier 2
+  target, and are not expected to provide target-specific implementations for
+  every tier 2 target.
+- The target must provide documentation for the Rust community explaining how
+  to build for the target using cross-compilation, and explaining how to run
+  tests for the target. If at all possible, this documentation should show how
+  to run Rust programs and tests for the target using emulation, to allow
+  anyone to do so. If the target cannot be feasibly emulated, the documentation
+  should explain how to obtain and work with physical hardware, cloud systems,
+  or equivalent.
+- The target must document its baseline expectations for the features or
+  versions of CPUs, operating systems, libraries, runtime environments, and
+  similar.
+- If introducing a new tier 2 or higher target that is identical to an existing
+  Rust target except for the baseline expectations for the features or versions
+  of CPUs, operating systems, libraries, runtime environments, and similar,
+  then the proposed target must document to the satisfaction of the approving
+  teams why the specific difference in baseline expectations provides
+  sufficient value to justify a separate target.
+  - Note that in some cases, based on the usage of existing targets within the
+    Rust community, Rust developers or a target's maintainers may wish to
+    modify the baseline expectations of a target, or split an existing target
+    into multiple targets with different baseline expectations. A proposal to
+    do so will be treated similarly to the analogous promotion, demotion, or
+    removal of a target, according to this policy, with the same team approvals
+    required.
+    - For instance, if an OS version has become obsolete and unsupported, a
+      target for that OS may raise its baseline expectations for OS version
+      (treated as though removing a target corresponding to the older
+      versions), or a target for that OS may split out support for older OS
+      versions into a lower-tier target (treated as though demoting a target
+      corresponding to the older versions, and requiring justification for a
+      new target at a lower tier for the older OS versions).
+- Tier 2 targets must not leave any significant portions of `core` or the
+  standard library unimplemented or stubbed out, unless they cannot possibly be
+  supported on the target.
+  - The right approach to handling a missing feature from a target may depend
+    on whether the target seems likely to develop the feature in the future. In
+    some cases, a target may be co-developed along with Rust support, and Rust
+    may gain new features on the target as that target gains the capabilities
+    to support those features.
+  - As an exception, a target identical to an existing tier 1 target except for
+    lower baseline expectations for the OS, CPU, or similar, may propose to
+    qualify as tier 2 (but not higher) without support for `std` if the target
+    will primarily be used in `no_std` applications, to reduce the support
+    burden for the standard library. In this case, evaluation of the proposed
+    target's value will take this limitation into account.
+- The code generation backend for the target should not have deficiencies that
+  invalidate Rust safety properties, as evaluated by the Rust compiler team.
+  (This requirement does not apply to arbitrary security enhancements or
+  mitigations provided by code generation backends, only to those properties
+  needed to ensure safe Rust code cannot cause undefined behavior or other
+  unsoundness.) If this requirement does not hold, the target must clearly and
+  prominently document any such limitations as part of the target's entry in
+  the target tier list, and ideally also via a failing test in the testsuite.
+  The Rust compiler team must be satisfied with the balance between these
+  limitations and the difficulty of implementing the necessary features.
+  - For example, if Rust relies on a specific code generation feature to ensure
+    that safe code cannot overflow the stack, the code generation for the
+    target should support that feature.
+  - If the Rust compiler introduces new safety properties (such as via new
+    capabilities of a compiler backend), the Rust compiler team will determine
+    if they consider those new safety properties a best-effort improvement for
+    specific targets, or a required property for all Rust targets. In the
+    latter case, the compiler team may require the maintainers of existing
+    targets to either implement and confirm support for the property or update
+    the target tier list with documentation of the missing property.
+- If the target supports C code, and the target has an interoperable calling
+  convention for C code, the Rust target must support that C calling convention
+  for the platform via `extern "C"`. The C calling convention does not need to
+  be the default Rust calling convention for the target, however.
+- The target must build reliably in CI, for all components that Rust's CI
+  considers mandatory.
+- The approving teams may additionally require that a subset of tests pass in
+  CI, such as enough to build a functional "hello world" program, `./x.py test
+  --no-run`, or equivalent "smoke tests". In particular, this requirement may
+  apply if the target builds host tools, or if the tests in question provide
+  substantial value via early detection of critical problems.
+- Building the target in CI must not take substantially longer than the current
+  slowest target in CI, and should not substantially raise the maintenance
+  burden of the CI infrastructure. This requirement is subjective, to be
+  evaluated by the infrastructure team, and will take the community importance
+  of the target into account.
+- Tier 2 targets should, if at all possible, support cross-compiling. Tier 2
+  targets should not require using the target as the host for builds, even if
+  the target supports host tools.
+- In addition to the legal requirements for all targets (specified in the tier
+  3 requirements), because a tier 2 target typically involves the Rust project
+  building and supplying various compiled binaries, incorporating the target
+  and redistributing any resulting compiled binaries (e.g. built libraries,
+  host tools if any) must not impose any onerous license requirements on any
+  members of the Rust project, including infrastructure team members and those
+  operating CI systems. This is a subjective requirement, to be evaluated by
+  the approving teams.
+  - As an exception to this, if the target's primary purpose is to build
+    components for a Free and Open Source Software (FOSS) project licensed
+    under "copyleft" terms (terms which require licensing other code under
+    compatible FOSS terms), such as kernel modules or plugins, then the
+    standard libraries for the target may potentially be subject to copyleft
+    terms, as long as such terms are satisfied by Rust's existing practices of
+    providing full corresponding source code. Note that anything added to the
+    Rust repository itself must still use Rust's standard license terms.
+- Tier 2 targets must not impose burden on the authors of pull requests, or
+  other developers in the community, to ensure that tests pass for the target.
+  In particular, do not post comments (automated or manual) on a PR that derail
+  or suggest a block on the PR based on tests failing for the target. Do not
+  send automated messages or notifications (via any medium, including via `@`)
+  to a PR author or others involved with a PR regarding the PR breaking tests
+  on a tier 2 target, unless they have opted into such messages.
+  - Backlinks such as those generated by the issue/PR tracker when linking to
+    an issue or PR are not considered a violation of this policy, within
+    reason. However, such messages (even on a separate repository) must not
+    generate notifications to anyone involved with a PR who has not requested
+    such notifications.
+- The target maintainers should regularly run the testsuite for the target, and
+  should fix any test failures in a reasonably timely fashion.
+- All requirements for tier 3 apply.
+
+A tier 2 target may be demoted or removed if it no longer meets these
+requirements. Any proposal for demotion or removal will be CCed to the target
+maintainers, and will be communicated widely to the Rust community before being
+dropped from a stable release. (The amount of time between such communication
+and the next stable release may depend on the nature and severity of the failed
+requirement, the timing of its discovery, whether the target has been part of a
+stable release yet, and whether the demotion or removal can be a planned and
+scheduled action.)
+
+In some circumstances, especially if the target maintainers do not respond in a
+timely fashion, Rust teams may land pull requests that temporarily disable some
+targets in the nightly compiler, in order to implement a feature not yet
+supported by those targets. (As an example, this happened when introducing the
+128-bit types `u128` and `i128`.) Such a pull request will include notification
+and coordination with the maintainers of such targets, and will ideally happen
+towards the beginning of a new development cycle to give maintainers time to
+update their targets. The maintainers of such targets will then be expected to
+implement the corresponding target-specific support in order to re-enable the
+target. If the maintainers of such targets cannot provide such support in time
+for the next stable release, this may result in demoting or removing the
+targets.
+
+### Tier 2 with host tools
+
+Some tier 2 targets may additionally have binaries built to run on them as a
+host (such as `rustc` and `cargo`). This allows the target to be used as a
+development platform, not just a compilation target.
+
+A proposed new tier 2 target with host tools must be reviewed and approved by
+the compiler team based on these requirements. Such review and approval may
+occur via a Major Change Proposal (MCP).
+
+In addition, the infrastructure team must approve the integration of the
+target's host tools into Continuous Integration (CI), and the CI-related
+requirements for host tools. This review and approval may take place in a PR
+adding the target's host tools to CI, or simply by an infrastructure team
+member reporting the outcome of a team discussion.
+
+- Depending on the target, its capabilities, its performance, and the
+  likelihood of use for any given tool, the host tools provided for a tier 2
+  target may include only `rustc` and `cargo`, or may include additional tools
+  such as `clippy` and `rustfmt`.
+- Approval of host tools will take into account the additional time required to
+  build the host tools, and the substantial additional storage required for the
+  host tools.
+- The host tools must have direct value to people other than the target's
+  maintainers. (It may still be a niche target, but the host tools must not be
+  exclusively useful for an inherently closed group.) This requirement will be
+  evaluated independently from the corresponding tier 2 requirement.
+  - The requirement to provide "direct value" means that it does not suffice to
+    argue that having host tools will help the target's maintainers more easily
+    provide the target to others. The tools themselves must provide value to
+    others.
+- There must be a reasonable expectation that the host tools will be used, for
+  purposes other than to prove that they can be used.
+- The host tools must build and run reliably in CI (for all components that
+  Rust's CI considers mandatory), though they may or may not pass tests.
+- Building host tools for the target must not take substantially longer than
+  building host tools for other targets, and should not substantially raise the
+  maintenance burden of the CI infrastructure.
+- The host tools must provide a substantively similar experience as on other
+  targets, subject to reasonable target limitations.
+  - Adding a substantively different interface to an existing tool, or a
+    target-specific interface to the functionality of an existing tool,
+    requires design and implementation approval (e.g. RFC/MCP) from the
+    appropriate approving teams for that tool.
+    - Such an interface should have a design that could potentially work for
+      other targets with similar properties.
+    - This should happen separately from the review and approval of the target,
+      to simplify the target review and approval processes, and to simplify the
+      review and approval processes for the proposed new interface.
+  - By way of example, a target that runs within a sandbox may need to modify
+    the handling of files, tool invocation, and similar to meet the
+    expectations and conventions of the sandbox, but must not introduce a
+    separate "sandboxed compilation" interface separate from the CLI interface
+    without going through the normal approval process for such an interface.
+    Such an interface should take into account potential other targets with
+    similar sandboxes.
+- If the host tools for the platform would normally be expected to be signed or
+  equivalent (e.g. if running unsigned binaries or similar involves a
+  "developer mode" or an additional prompt), it must be possible for the Rust
+  project's automated builds to apply the appropriate signature process,
+  without any manual intervention by either Rust developers, target
+  maintainers, or a third party. This process must meet the approval of the
+  infrastructure team.
+  - This process may require one-time or semi-regular manual steps by the
+    infrastructure team, such as registration or renewal of a signing key. Any
+    such manual process must meet the approval of the infrastructure team.
+  - This process may require the execution of a legal agreement with the
+    signature provider. Such a legal agreement may be revocable, and may
+    potentially require a nominal fee, but must not be otherwise onerous. Any
+    such legal agreement must meet the approval of the infrastructure team.
+    (The infrastructure team is not expected or required to sign binding legal
+    agreements on behalf of the Rust project; this review and approval exists
+    to ensure no terms are onerous or cause problems for infrastructure,
+    especially if such terms may impose requirements or obligations on people
+    who have access to target-specific infrastructure.)
+  - Changes to this process, or to any legal agreements involved, may
+    cause a target to stop meeting this requirement.
+  - This process involved must be available under substantially similar
+    non-onerous terms to the general public. Making it available exclusively to
+    the Rust project does not suffice.
+  - This requirement exists to ensure that Rust builds, including nightly
+    builds, can meet the necessary requirements to allow users to smoothly run
+    the host tools.
+- Providing host tools does not exempt a target from requirements to support
+  cross-compilation if at all possible.
+- All requirements for tier 2 apply.
+
+A target may be promoted directly from tier 3 to tier 2 with host tools if it
+meets all the necessary requirements, but doing so may introduce substantial
+additional complexity. If in doubt, the target should qualify for tier 2
+without host tools first.
+
+## Tier 1 target policy
+
+At this tier, the Rust project guarantees that a target builds and passes all
+tests, and will reject patches that fail to build or pass the testsuite on a
+target. We hold tier 1 targets to our highest standard of requirements.
+
+A proposed new tier 1 target must be reviewed and approved by the compiler team
+based on these requirements. In addition, the release team must approve the
+viability and value of supporting the target. For a tier 1 target, this will
+typically take place via a full RFC proposing the target, to be jointly
+reviewed and approved by the compiler team and release team.
+
+In addition, the infrastructure team must approve the integration of the target
+into Continuous Integration (CI), and the tier 1 CI-related requirements. This
+review and approval may take place in a PR adding the target to CI, by an
+infrastructure team member reporting the outcome of a team discussion, or by
+including the infrastructure team in the RFC proposing the target.
+
+- Tier 1 targets must have substantial, widespread interest within the
+  developer community, and must serve the ongoing needs of multiple production
+  users of Rust across multiple organizations or projects. These requirements
+  are subjective, and determined by consensus of the approving teams. A tier 1
+  target may be demoted or removed if it becomes obsolete or no longer meets
+  this requirement.
+- The target maintainer team must include at least 3 developers.
+- The target must build and pass tests reliably in CI, for all components that
+  Rust's CI considers mandatory.
+  - The target must not disable an excessive number of tests or pieces of tests
+    in the testsuite in order to do so. This is a subjective requirement.
+  - If the target does not have host tools support, or if the target has low
+    performance, the infrastructure team may choose to have CI cross-compile
+    the testsuite from another platform, and then run the compiled tests
+    either natively or via accurate emulation. However, the approving teams may
+    take such performance considerations into account when determining the
+    viability of the target or of its host tools.
+- The target must provide as much of the Rust standard library as is feasible
+  and appropriate to provide. For instance, if the target can support dynamic
+  memory allocation, it must provide an implementation of `alloc` and the
+  associated data structures.
+- Building the target and running the testsuite for the target must not take
+  substantially longer than other targets, and should not substantially raise
+  the maintenance burden of the CI infrastructure.
+  - In particular, if building the target takes a reasonable amount of time,
+    but the target cannot run the testsuite in a timely fashion due to low
+    performance of either native code or accurate emulation, that alone may
+    prevent the target from qualifying as tier 1.
+- If running the testsuite requires additional infrastructure (such as physical
+  systems running the target), the target maintainers must arrange to provide
+  such resources to the Rust project, to the satisfaction and approval of the
+  Rust infrastructure team.
+  - Such resources may be provided via cloud systems, via emulation, or via
+    physical hardware.
+  - If the target requires the use of emulation to meet any of the tier
+    requirements, the approving teams for those requirements must have high
+    confidence in the accuracy of the emulation, such that discrepancies
+    between emulation and native operation that affect test results will
+    constitute a high-priority bug in either the emulation or the
+    implementation of the target.
+  - If it is not possible to run the target via emulation, these resources must
+    additionally be sufficient for the Rust infrastructure team to make them
+    available for access by Rust team members, for the purposes of development
+    and testing. (Note that the responsibility for doing target-specific
+    development to keep the target well maintained remains with the target
+    maintainers. This requirement ensures that it is possible for other
+    Rust developers to test the target, but does not obligate other Rust
+    developers to make target-specific fixes.)
+  - Resources provided for CI and similar infrastructure must be available for
+    continuous exclusive use by the Rust project. Resources provided
+    for access by Rust team members for development and testing must be
+    available on an exclusive basis when in use, but need not be available on a
+    continuous basis when not in use.
+- Tier 1 targets must not have a hard requirement for signed, verified, or
+  otherwise "approved" binaries. Developers must be able to build, run, and
+  test binaries for the target on systems they control, or provide such
+  binaries for others to run. (Doing so may require enabling some appropriate
+  "developer mode" on such systems, but must not require the payment of any
+  additional fee or other consideration, or agreement to any onerous legal
+  agreements.)
+  - The Rust project may decide to supply appropriately signed binaries if
+    doing so provides a smoother experience for developers using the target,
+    and a tier 2 target with host tools already requires providing appropriate
+    mechanisms that enable our infrastructure to provide such signed binaries.
+    However, this additional tier 1 requirement ensures that Rust developers
+    can develop and test Rust software for the target (including Rust itself),
+    and that development or testing for the target is not limited.
+- All requirements for tier 2 apply.
+
+A tier 1 target may be demoted if it no longer meets these requirements but
+still meets the requirements for a lower tier. Any proposal for demotion of a
+tier 1 target requires a full RFC process, with approval by the compiler and
+release teams. Any such proposal will be communicated widely to the Rust
+community, both when initially proposed and before being dropped from a stable
+release. A tier 1 target is highly unlikely to be directly removed without
+first being demoted to tier 2 or tier 3. (The amount of time between such
+communication and the next stable release may depend on the nature and severity
+of the failed requirement, the timing of its discovery, whether the target has
+been part of a stable release yet, and whether the demotion or removal can be a
+planned and scheduled action.)
+
+Raising the baseline expectations of a tier 1 target (such as the minimum CPU
+features or OS version required) requires the approval of the compiler and
+release teams, and should be widely communicated as well, but does not
+necessarily require a full RFC.
+
+### Tier 1 with host tools
+
+Some tier 1 targets may additionally have binaries built to run on them as a
+host (such as `rustc` and `cargo`). This allows the target to be used as a
+development platform, not just a compilation target.
+
+A proposed new tier 1 target with host tools must be reviewed and approved by
+the compiler team based on these requirements. In addition, the release team
+must approve the viability and value of supporting host tools for the target.
+For a tier 1 target, this will typically take place via a full RFC proposing
+the target, to be jointly reviewed and approved by the compiler team and
+release team.
+
+In addition, the infrastructure team must approve the integration of the
+target's host tools into Continuous Integration (CI), and the CI-related
+requirements for host tools. This review and approval may take place in a PR
+adding the target's host tools to CI, by an infrastructure team member
+reporting the outcome of a team discussion, or by including the infrastructure
+team in the RFC proposing the target.
+
+- Tier 1 targets with host tools should typically include all of the additional
+  tools such as `clippy` and `rustfmt`, unless there is a target-specific
+  reason why a tool cannot possibly make sense for the target.
+  - Unlike with tier 2, for tier 1 we will not exclude specific tools on the
+    sole basis of them being less likely to be used; rather, we'll take that
+    into account when considering whether the target should be at tier 1 with
+    host tools. In general, on any tier 1 target with host tools, people
+    should be able to expect to find and install all the same components that
+    they would for any other tier 1 target with host tools.
+- Approval of host tools will take into account the additional time required to
+  build the host tools, and the substantial additional storage required for the
+  host tools.
+- Host tools for the target must have substantial, widespread interest within
+  the developer community, and must serve the ongoing needs of multiple
+  production users of Rust across multiple organizations or projects. These
+  requirements are subjective, and determined by consensus of the approving
+  teams. This requirement will be evaluated independently from the
+  corresponding tier 1 requirement; it is possible for a target to have
+  sufficient interest for cross-compilation, but not have sufficient interest
+  for native compilation. The host tools may be dropped if they no longer meet
+  this requirement, even if the target otherwise qualifies as tier 1.
+- The host tools must build, run, and pass tests reliably in CI, for all
+  components that Rust's CI considers mandatory.
+  - The target must not disable an excessive number of tests or pieces of tests
+    in the testsuite in order to do so. This is a subjective requirement.
+- Building the host tools and running the testsuite for the host tools must not
+  take substantially longer than other targets, and should not substantially raise
+  the maintenance burden of the CI infrastructure.
+  - In particular, if building the target's host tools takes a reasonable
+    amount of time, but the target cannot run the testsuite in a timely fashion
+    due to low performance of either native code or accurate emulation, that
+    alone may prevent the target from qualifying as tier 1 with host tools.
+- Providing host tools does not exempt a target from requirements to support
+  cross-compilation if at all possible.
+- All requirements for tier 2 targets with host tools apply.
+- All requirements for tier 1 apply.
+
+A target seeking promotion to tier 1 with host tools should typically either be
+tier 2 with host tools or tier 1 without host tools, to reduce the number of
+requirements to simultaneously review and approve.
+
+In addition to the general process for demoting a tier 1 target, a tier 1
+target with host tools may be demoted (including having its host tools dropped,
+or being demoted to tier 2 with host tools) if it no longer meets these
+requirements but still meets the requirements for a lower tier. Any proposal
+for demotion of a tier 1 target (with or without host tools) requires a full
+RFC process, with approval by the compiler and release teams. Any such proposal
+will be communicated widely to the Rust community, both when initially proposed
+and before being dropped from a stable release.
index 22780804610b6365821d0bb67ad621912db541fd..86bedb51538b5344f8e340f87818e10dfd3f0d5d 100644 (file)
@@ -191,7 +191,7 @@ mechanisms of the compiler. This is often mapped to GCC's personality function
 which do not trigger a panic can be assured that this function is never
 called. The language item's name is `eh_personality`.
 
-[unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs
+[unwind]: https://github.com/rust-lang/rust/blob/master/library/panic_unwind/src/gcc.rs
 
 The second function, `rust_begin_panic`, is also used by the failure mechanisms of the
 compiler. When a panic happens, this controls the message that's displayed on
diff --git a/src/doc/unstable-book/src/language-features/non-ascii-idents.md b/src/doc/unstable-book/src/language-features/non-ascii-idents.md
deleted file mode 100644 (file)
index 847f25e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-# `non_ascii_idents`
-
-The tracking issue for this feature is: [#55467]
-
-[#55467]: https://github.com/rust-lang/rust/issues/55467
-
-------------------------
-
-The `non_ascii_idents` feature adds support for non-ASCII identifiers.
-
-## Examples
-
-```rust
-#![feature(non_ascii_idents)]
-
-const ε: f64 = 0.00001f64;
-const Π: f64 = 3.14f64;
-```
-
-## Changes to the language reference
-
-> **<sup>Lexer:<sup>**\
-> IDENTIFIER :\
-> &nbsp;&nbsp; &nbsp;&nbsp; XID_start XID_continue<sup>\*</sup>\
-> &nbsp;&nbsp; | `_` XID_continue<sup>+</sup>
-
-An identifier is any nonempty Unicode string of the following form:
-
-Either
-
-   * The first character has property [`XID_start`]
-   * The remaining characters have property [`XID_continue`]
-
-Or
-
-   * The first character is `_`
-   * The identifier is more than one character, `_` alone is not an identifier
-   * The remaining characters have property [`XID_continue`]
-
-that does _not_ occur in the set of [strict keywords].
-
-> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the
-> character ranges used to form the more familiar C and Java language-family
-> identifiers.
-
-[`XID_start`]:  http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i=
-[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i=
-[strict keywords]: ../../reference/keywords.md#strict-keywords
index d778a507425286cfdb10ba0c36ca626bb5fb9fc7..b816b9d718876aa3ed76d99982fc8ea8815986d1 100644 (file)
@@ -8,7 +8,7 @@ edition = "2018"
 path = "lib.rs"
 
 [dependencies]
-arrayvec = { version = "0.5.1", default-features = false }
+arrayvec = { version = "0.7", default-features = false }
 pulldown-cmark = { version = "0.8", default-features = false }
 minifier = "0.0.39"
 rayon = { version = "0.3.0", package = "rustc-rayon" }
index 9052e5954200f0753708f9bdff88f06b90234570..92eb6214f79fed7133458e74b080eb98fe115fd6 100644 (file)
@@ -110,12 +110,12 @@ fn generate_for_trait(
         };
 
         Some(Item {
-            span: Span::dummy(),
             name: None,
             attrs: Default::default(),
             visibility: Inherited,
             def_id: self.cx.next_def_id(item_def_id.krate),
             kind: box ImplItem(Impl {
+                span: Span::dummy(),
                 unsafety: hir::Unsafety::Normal,
                 generics: new_generics,
                 provided_trait_methods: Default::default(),
@@ -126,6 +126,7 @@ fn generate_for_trait(
                 synthetic: true,
                 blanket_impl: None,
             }),
+            cfg: None,
         })
     }
 
index f7e08d10401888085b8dea0c14840a5e695d69eb..3a14a1d23f2d4186602b741c340e0806846db583 100644 (file)
@@ -100,12 +100,12 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                     .collect();
 
                 impls.push(Item {
-                    span: self.cx.tcx.def_span(impl_def_id).clean(self.cx),
                     name: None,
                     attrs: Default::default(),
                     visibility: Inherited,
                     def_id: self.cx.next_def_id(impl_def_id.krate),
                     kind: box ImplItem(Impl {
+                        span: self.cx.tcx.def_span(impl_def_id).clean(self.cx),
                         unsafety: hir::Unsafety::Normal,
                         generics: (
                             self.cx.tcx.generics_of(impl_def_id),
@@ -128,6 +128,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                         synthetic: false,
                         blanket_impl: Some(trait_ref.self_ty().clean(self.cx)),
                     }),
+                    cfg: None,
                 });
             }
         }
index 277ec91f15ed7205e737a183fdb393aadd0d2f61..3e89c1ac4c514dc8f543791104ffad2c42dcc5ab 100644 (file)
@@ -1,21 +1,21 @@
 //! Support for inlining external documentation into the current AST.
 
 use std::iter::once;
+use std::sync::Arc;
 
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
+use rustc_hir::def_id::DefId;
 use rustc_hir::Mutability;
 use rustc_metadata::creader::LoadedMacro;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_mir::const_eval::is_min_const_fn;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 
-use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind};
+use crate::clean::{self, Attributes, AttributesExt, GetDefId, ToSource};
 use crate::core::DocContext;
 use crate::formats::item_type::ItemType;
 
 
     let kind = match res {
         Res::Def(DefKind::Trait, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Trait);
+            record_extern_fqn(cx, did, ItemType::Trait);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::TraitItem(build_external_trait(cx, did))
         }
         Res::Def(DefKind::Fn, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Function);
+            record_extern_fqn(cx, did, ItemType::Function);
             clean::FunctionItem(build_external_function(cx, did))
         }
         Res::Def(DefKind::Struct, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Struct);
+            record_extern_fqn(cx, did, ItemType::Struct);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::StructItem(build_struct(cx, did))
         }
         Res::Def(DefKind::Union, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Union);
+            record_extern_fqn(cx, did, ItemType::Union);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::UnionItem(build_union(cx, did))
         }
         Res::Def(DefKind::TyAlias, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Typedef);
+            record_extern_fqn(cx, did, ItemType::Typedef);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::TypedefItem(build_type_alias(cx, did), false)
         }
         Res::Def(DefKind::Enum, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Enum);
+            record_extern_fqn(cx, did, ItemType::Enum);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::EnumItem(build_enum(cx, did))
         }
         Res::Def(DefKind::ForeignTy, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Foreign);
+            record_extern_fqn(cx, did, ItemType::ForeignType);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::ForeignTypeItem
         }
         // their constructors.
         Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => return Some(Vec::new()),
         Res::Def(DefKind::Mod, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Module);
+            record_extern_fqn(cx, did, ItemType::Module);
             clean::ModuleItem(build_module(cx, did, visited))
         }
         Res::Def(DefKind::Static, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Static);
+            record_extern_fqn(cx, did, ItemType::Static);
             clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did)))
         }
         Res::Def(DefKind::Const, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Const);
+            record_extern_fqn(cx, did, ItemType::Constant);
             clean::ConstantItem(build_const(cx, did))
         }
         Res::Def(DefKind::Macro(kind), did) => {
             let mac = build_macro(cx, did, name);
 
             let type_kind = match kind {
-                MacroKind::Bang => TypeKind::Macro,
-                MacroKind::Attr => TypeKind::Attr,
-                MacroKind::Derive => TypeKind::Derive,
+                MacroKind::Bang => ItemType::Macro,
+                MacroKind::Attr => ItemType::ProcAttribute,
+                MacroKind::Derive => ItemType::ProcDerive,
             };
             record_extern_fqn(cx, did, type_kind);
             mac
         _ => return None,
     };
 
-    let target_attrs = load_attrs(cx, did);
-    let attrs = box merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone);
-
+    let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs_clone);
     cx.inlined.insert(did);
-    let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx);
-    ret.push(clean::Item { attrs, ..what_rustc_thinks });
+    ret.push(clean::Item::from_def_id_and_attrs_and_parts(
+        did,
+        Some(name),
+        kind,
+        box attrs,
+        cx,
+        cfg,
+    ));
     Some(ret)
 }
 
 ///
 /// These names are used later on by HTML rendering to generate things like
 /// source links back to the original item.
-crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: clean::TypeKind) {
+crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) {
     let crate_name = cx.tcx.crate_name(did.krate).to_string();
 
     let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
         let s = elem.data.to_string();
         if !s.is_empty() { Some(s) } else { None }
     });
-    let fqn = if let clean::TypeKind::Macro = kind {
+    let fqn = if let ItemType::Macro = kind {
         // Check to see if it is a macro 2.0 or built-in macro
         if matches!(
             cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())),
@@ -210,7 +214,7 @@ fn build_external_function(cx: &mut DocContext<'_>, did: DefId) -> clean::Functi
     let sig = cx.tcx.fn_sig(did);
 
     let constness =
-        if is_min_const_fn(cx.tcx, did) { hir::Constness::Const } else { hir::Constness::NotConst };
+        if cx.tcx.is_const_fn_raw(did) { hir::Constness::Const } else { hir::Constness::NotConst };
     let asyncness = cx.tcx.asyncness(did);
     let predicates = cx.tcx.predicates_of(did);
     let (generics, decl) = clean::enter_impl_trait(cx, |cx| {
@@ -289,22 +293,24 @@ fn merge_attrs(
     parent_module: Option<DefId>,
     old_attrs: Attrs<'_>,
     new_attrs: Option<Attrs<'_>>,
-) -> clean::Attributes {
+) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) {
     // NOTE: If we have additional attributes (from a re-export),
     // always insert them first. This ensure that re-export
     // doc comments show up before the original doc comments
     // when we render them.
     if let Some(inner) = new_attrs {
-        if let Some(new_id) = parent_module {
-            let diag = cx.sess().diagnostic();
-            Attributes::from_ast(diag, old_attrs, Some((inner, new_id)))
-        } else {
-            let mut both = inner.to_vec();
-            both.extend_from_slice(old_attrs);
-            both.clean(cx)
-        }
+        let mut both = inner.to_vec();
+        both.extend_from_slice(old_attrs);
+        (
+            if let Some(new_id) = parent_module {
+                Attributes::from_ast(old_attrs, Some((inner, new_id)))
+            } else {
+                Attributes::from_ast(&both, None)
+            },
+            both.cfg(cx.sess().diagnostic()),
+        )
     } else {
-        old_attrs.clean(cx)
+        (old_attrs.clean(cx), old_attrs.cfg(cx.sess().diagnostic()))
     }
 }
 
@@ -415,13 +421,14 @@ fn merge_attrs(
 
     debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
 
-    let attrs = box merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
-    debug!("merged_attrs={:?}", attrs);
+    let (merged_attrs, cfg) = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
+    debug!("merged_attrs={:?}", merged_attrs);
 
     ret.push(clean::Item::from_def_id_and_attrs_and_parts(
         did,
         None,
         clean::ImplItem(clean::Impl {
+            span: clean::types::rustc_span(did, cx.tcx),
             unsafety: hir::Unsafety::Normal,
             generics,
             provided_trait_methods: provided,
@@ -432,8 +439,9 @@ fn merge_attrs(
             synthetic: false,
             blanket_impl: None,
         }),
-        attrs,
+        box merged_attrs,
         cx,
+        cfg,
     ));
 }
 
@@ -459,8 +467,7 @@ fn build_module(
                 items.push(clean::Item {
                     name: None,
                     attrs: box clean::Attributes::default(),
-                    span: clean::Span::dummy(),
-                    def_id: DefId::local(CRATE_DEF_INDEX),
+                    def_id: cx.next_def_id(did.krate),
                     visibility: clean::Public,
                     kind: box clean::ImportItem(clean::Import::new_simple(
                         item.ident.name,
@@ -480,6 +487,7 @@ fn build_module(
                         },
                         true,
                     )),
+                    cfg: None,
                 });
             } else if let Some(i) = try_inline(cx, did, item.res, item.ident.name, None, visited) {
                 items.extend(i)
@@ -487,7 +495,8 @@ fn build_module(
         }
     }
 
-    clean::Module { items, is_crate: false }
+    let span = clean::Span::from_rustc_span(cx.tcx.def_span(did));
+    clean::Module { items, span }
 }
 
 crate fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String {
index 217e899001ef9b23fef5b6797b76bfee48c1a0d2..6563f398edb6fcea03e40511797b2ae54bfb80aa 100644 (file)
@@ -22,7 +22,7 @@
 use rustc_middle::ty::fold::TypeFolder;
 use rustc_middle::ty::subst::{InternalSubsts, Subst};
 use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt};
-use rustc_mir::const_eval::{is_const_fn, is_min_const_fn, is_unstable_const_fn};
+use rustc_mir::const_eval::{is_const_fn, is_unstable_const_fn};
 use rustc_span::hygiene::{AstPass, MacroKind};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{self, ExpnKind};
@@ -36,6 +36,7 @@
 
 use crate::core::{self, DocContext, ImplTraitParam};
 use crate::doctree;
+use crate::formats::item_type::ItemType;
 
 use utils::*;
 
@@ -83,124 +84,8 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Option<U> {
 }
 
 impl Clean<ExternalCrate> for CrateNum {
-    fn clean(&self, cx: &mut DocContext<'_>) -> ExternalCrate {
-        let tcx = cx.tcx;
-        let root = DefId { krate: *self, index: CRATE_DEF_INDEX };
-        let krate_span = tcx.def_span(root);
-        let krate_src = cx.sess().source_map().span_to_filename(krate_span);
-
-        // Collect all inner modules which are tagged as implementations of
-        // primitives.
-        //
-        // Note that this loop only searches the top-level items of the crate,
-        // and this is intentional. If we were to search the entire crate for an
-        // item tagged with `#[doc(primitive)]` then we would also have to
-        // search the entirety of external modules for items tagged
-        // `#[doc(primitive)]`, which is a pretty inefficient process (decoding
-        // all that metadata unconditionally).
-        //
-        // In order to keep the metadata load under control, the
-        // `#[doc(primitive)]` feature is explicitly designed to only allow the
-        // primitive tags to show up as the top level items in a crate.
-        //
-        // Also note that this does not attempt to deal with modules tagged
-        // duplicately for the same primitive. This is handled later on when
-        // rendering by delegating everything to a hash map.
-        let mut as_primitive = |res: Res| {
-            if let Res::Def(DefKind::Mod, def_id) = res {
-                let attrs = cx.tcx.get_attrs(def_id).clean(cx);
-                let mut prim = None;
-                for attr in attrs.lists(sym::doc) {
-                    if let Some(v) = attr.value_str() {
-                        if attr.has_name(sym::primitive) {
-                            prim = PrimitiveType::from_symbol(v);
-                            if prim.is_some() {
-                                break;
-                            }
-                            // FIXME: should warn on unknown primitives?
-                        }
-                    }
-                }
-                return prim.map(|p| (def_id, p));
-            }
-            None
-        };
-        let primitives = if root.is_local() {
-            tcx.hir()
-                .krate()
-                .item
-                .item_ids
-                .iter()
-                .filter_map(|&id| {
-                    let item = tcx.hir().item(id);
-                    match item.kind {
-                        hir::ItemKind::Mod(_) => {
-                            as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
-                        }
-                        hir::ItemKind::Use(ref path, hir::UseKind::Single)
-                            if item.vis.node.is_pub() =>
-                        {
-                            as_primitive(path.res).map(|(_, prim)| {
-                                // Pretend the primitive is local.
-                                (id.def_id.to_def_id(), prim)
-                            })
-                        }
-                        _ => None,
-                    }
-                })
-                .collect()
-        } else {
-            tcx.item_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect()
-        };
-
-        let mut as_keyword = |res: Res| {
-            if let Res::Def(DefKind::Mod, def_id) = res {
-                let attrs = tcx.get_attrs(def_id).clean(cx);
-                let mut keyword = None;
-                for attr in attrs.lists(sym::doc) {
-                    if attr.has_name(sym::keyword) {
-                        if let Some(v) = attr.value_str() {
-                            keyword = Some(v);
-                            break;
-                        }
-                    }
-                }
-                return keyword.map(|p| (def_id, p));
-            }
-            None
-        };
-        let keywords = if root.is_local() {
-            tcx.hir()
-                .krate()
-                .item
-                .item_ids
-                .iter()
-                .filter_map(|&id| {
-                    let item = tcx.hir().item(id);
-                    match item.kind {
-                        hir::ItemKind::Mod(_) => {
-                            as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
-                        }
-                        hir::ItemKind::Use(ref path, hir::UseKind::Single)
-                            if item.vis.node.is_pub() =>
-                        {
-                            as_keyword(path.res).map(|(_, prim)| (id.def_id.to_def_id(), prim))
-                        }
-                        _ => None,
-                    }
-                })
-                .collect()
-        } else {
-            tcx.item_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect()
-        };
-
-        ExternalCrate {
-            name: tcx.crate_name(*self),
-            src: krate_src,
-            attrs: tcx.get_attrs(root).clean(cx),
-            primitives,
-            keywords,
-        }
+    fn clean(&self, _cx: &mut DocContext<'_>) -> ExternalCrate {
+        ExternalCrate { crate_num: *self }
     }
 }
 
@@ -214,7 +99,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
 
         // determine if we should display the inner contents or
         // the outer `mod` item for the source code.
-        let span = {
+        let span = Span::from_rustc_span({
             let sm = cx.sess().source_map();
             let outer = sm.lookup_char_pos(self.where_outer.lo());
             let inner = sm.lookup_char_pos(self.where_inner.lo());
@@ -225,21 +110,20 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
                 // mod foo; (and a separate SourceFile for the contents)
                 self.where_inner
             }
-        };
+        });
 
-        let what_rustc_thinks = Item::from_hir_id_and_parts(
+        Item::from_hir_id_and_parts(
             self.id,
             Some(self.name),
-            ModuleItem(Module { is_crate: self.is_crate, items }),
+            ModuleItem(Module { items, span }),
             cx,
-        );
-        Item { span: span.clean(cx), ..what_rustc_thinks }
+        )
     }
 }
 
 impl Clean<Attributes> for [ast::Attribute] {
-    fn clean(&self, cx: &mut DocContext<'_>) -> Attributes {
-        Attributes::from_ast(cx.sess().diagnostic(), self, None)
+    fn clean(&self, _cx: &mut DocContext<'_>) -> Attributes {
+        Attributes::from_ast(self, None)
     }
 }
 
@@ -273,7 +157,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> GenericBound {
 impl Clean<Type> for (ty::TraitRef<'_>, &[TypeBinding]) {
     fn clean(&self, cx: &mut DocContext<'_>) -> Type {
         let (trait_ref, bounds) = *self;
-        inline::record_extern_fqn(cx, trait_ref.def_id, TypeKind::Trait);
+        inline::record_extern_fqn(cx, trait_ref.def_id, ItemType::Trait);
         let path = external_path(
             cx,
             cx.tcx.item_name(trait_ref.def_id),
@@ -965,7 +849,6 @@ fn clean(&self, cx: &mut DocContext<'_>) -> FnDecl {
             inputs: (self.0.inputs, self.1).clean(cx),
             output: self.0.output.clean(cx),
             c_variadic: self.0.c_variadic,
-            attrs: Attributes::default(),
         }
     }
 }
@@ -977,7 +860,6 @@ fn clean(&self, cx: &mut DocContext<'_>) -> FnDecl {
 
         FnDecl {
             output: Return(sig.skip_binder().output().clean(cx)),
-            attrs: Attributes::default(),
             c_variadic: sig.skip_binder().c_variadic,
             inputs: Arguments {
                 values: sig
@@ -1028,12 +910,6 @@ fn clean(&self, cx: &mut DocContext<'_>) -> PolyTrait {
     }
 }
 
-impl Clean<TypeKind> for hir::def::DefKind {
-    fn clean(&self, _: &mut DocContext<'_>) -> TypeKind {
-        (*self).into()
-    }
-}
-
 impl Clean<Item> for hir::TraitItem<'_> {
     fn clean(&self, cx: &mut DocContext<'_>) -> Item {
         let local_did = self.def_id.to_def_id();
@@ -1169,7 +1045,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
                     ty::TraitContainer(_) => self.defaultness.has_value(),
                 };
                 if provided {
-                    let constness = if is_min_const_fn(tcx, self.def_id) {
+                    let constness = if tcx.is_const_fn_raw(self.def_id) {
                         hir::Constness::Const
                     } else {
                         hir::Constness::NotConst
@@ -1568,16 +1444,16 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Type {
             ty::Adt(def, substs) => {
                 let did = def.did;
                 let kind = match def.adt_kind() {
-                    AdtKind::Struct => TypeKind::Struct,
-                    AdtKind::Union => TypeKind::Union,
-                    AdtKind::Enum => TypeKind::Enum,
+                    AdtKind::Struct => ItemType::Struct,
+                    AdtKind::Union => ItemType::Union,
+                    AdtKind::Enum => ItemType::Enum,
                 };
                 inline::record_extern_fqn(cx, did, kind);
                 let path = external_path(cx, cx.tcx.item_name(did), None, false, vec![], substs);
                 ResolvedPath { path, param_names: None, did, is_generic: false }
             }
             ty::Foreign(did) => {
-                inline::record_extern_fqn(cx, did, TypeKind::Foreign);
+                inline::record_extern_fqn(cx, did, ItemType::ForeignType);
                 let path = external_path(
                     cx,
                     cx.tcx.item_name(did),
@@ -1602,7 +1478,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Type {
                     _ => cx.tcx.intern_substs(&[]),
                 };
 
-                inline::record_extern_fqn(cx, did, TypeKind::Trait);
+                inline::record_extern_fqn(cx, did, ItemType::Trait);
 
                 let mut param_names = vec![];
                 if let Some(b) = reg.clean(cx) {
@@ -1612,7 +1488,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Type {
                     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);
+                    inline::record_extern_fqn(cx, did, ItemType::Trait);
                     let bound = GenericBound::TraitBound(
                         PolyTrait {
                             trait_: ResolvedPath {
@@ -2066,6 +1942,7 @@ fn clean_impl(impl_: &hir::Impl<'_>, hir_id: hir::HirId, cx: &mut DocContext<'_>
     });
     let mut make_item = |trait_: Option<Type>, for_: Type, items: Vec<Item>| {
         let kind = ImplItem(Impl {
+            span: types::rustc_span(tcx.hir().local_def_id(hir_id).to_def_id(), tcx),
             unsafety: impl_.unsafety,
             generics: impl_.generics.clean(cx),
             provided_trait_methods: provided.clone(),
@@ -2121,14 +1998,15 @@ fn clean_extern_crate(
             return items;
         }
     }
+
     // FIXME: using `from_def_id_and_kind` breaks `rustdoc/masked` for some reason
     vec![Item {
         name: Some(name),
         attrs: box attrs.clean(cx),
-        span: krate.span.clean(cx),
         def_id: crate_def_id,
         visibility: krate.vis.clean(cx),
         kind: box ExternCrateItem { src: orig_name },
+        cfg: attrs.cfg(cx.sess().diagnostic()),
     }]
 }
 
@@ -2287,14 +2165,14 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
             if matchers.len() <= 1 {
                 format!(
                     "{}macro {}{} {{\n    ...\n}}",
-                    vis.print_with_space(cx.tcx, def_id, &cx.cache),
+                    vis.to_src_with_space(cx.tcx, def_id),
                     name,
                     matchers.iter().map(|span| span.to_src(cx)).collect::<String>(),
                 )
             } else {
                 format!(
                     "{}macro {} {{\n{}}}",
-                    vis.print_with_space(cx.tcx, def_id, &cx.cache),
+                    vis.to_src_with_space(cx.tcx, def_id),
                     name,
                     matchers
                         .iter()
index f3c9b987eb02ab82bfdf618b8e6b89f2aa41823a..5e47144588b3f3b0b2f5dc7c9bb5516c4b21051a 100644 (file)
@@ -17,8 +17,8 @@
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, DefIndex};
+use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{BodyId, Mutability};
 use rustc_index::vec::IndexVec;
@@ -41,6 +41,7 @@
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::render::cache::ExternalLocation;
+use crate::html::render::Context;
 
 use self::FnRetTy::*;
 use self::ItemKind::*;
 
 #[derive(Clone, Debug)]
 crate struct ExternalCrate {
-    crate name: Symbol,
-    crate src: FileName,
-    crate attrs: Attributes,
-    crate primitives: ThinVec<(DefId, PrimitiveType)>,
-    crate keywords: ThinVec<(DefId, Symbol)>,
+    crate crate_num: CrateNum,
+}
+
+impl ExternalCrate {
+    #[inline]
+    fn def_id(&self) -> DefId {
+        DefId { krate: self.crate_num, index: CRATE_DEF_INDEX }
+    }
+
+    crate fn src(&self, tcx: TyCtxt<'_>) -> FileName {
+        let krate_span = tcx.def_span(self.def_id());
+        tcx.sess.source_map().span_to_filename(krate_span)
+    }
+
+    crate fn name(&self, tcx: TyCtxt<'_>) -> Symbol {
+        tcx.crate_name(self.crate_num)
+    }
+
+    crate fn keywords(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, Symbol)> {
+        let root = self.def_id();
+
+        let as_keyword = |res: Res| {
+            if let Res::Def(DefKind::Mod, def_id) = res {
+                let attrs = tcx.get_attrs(def_id);
+                let mut keyword = None;
+                for attr in attrs.lists(sym::doc) {
+                    if attr.has_name(sym::keyword) {
+                        if let Some(v) = attr.value_str() {
+                            keyword = Some(v);
+                            break;
+                        }
+                    }
+                }
+                return keyword.map(|p| (def_id, p));
+            }
+            None
+        };
+        if root.is_local() {
+            tcx.hir()
+                .krate()
+                .item
+                .item_ids
+                .iter()
+                .filter_map(|&id| {
+                    let item = tcx.hir().item(id);
+                    match item.kind {
+                        hir::ItemKind::Mod(_) => {
+                            as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
+                        }
+                        hir::ItemKind::Use(ref path, hir::UseKind::Single)
+                            if item.vis.node.is_pub() =>
+                        {
+                            as_keyword(path.res).map(|(_, prim)| (id.def_id.to_def_id(), prim))
+                        }
+                        _ => None,
+                    }
+                })
+                .collect()
+        } else {
+            tcx.item_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect()
+        }
+    }
+
+    crate fn primitives(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, PrimitiveType)> {
+        let root = self.def_id();
+
+        // Collect all inner modules which are tagged as implementations of
+        // primitives.
+        //
+        // Note that this loop only searches the top-level items of the crate,
+        // and this is intentional. If we were to search the entire crate for an
+        // item tagged with `#[doc(primitive)]` then we would also have to
+        // search the entirety of external modules for items tagged
+        // `#[doc(primitive)]`, which is a pretty inefficient process (decoding
+        // all that metadata unconditionally).
+        //
+        // In order to keep the metadata load under control, the
+        // `#[doc(primitive)]` feature is explicitly designed to only allow the
+        // primitive tags to show up as the top level items in a crate.
+        //
+        // Also note that this does not attempt to deal with modules tagged
+        // duplicately for the same primitive. This is handled later on when
+        // rendering by delegating everything to a hash map.
+        let as_primitive = |res: Res| {
+            if let Res::Def(DefKind::Mod, def_id) = res {
+                let attrs = tcx.get_attrs(def_id);
+                let mut prim = None;
+                for attr in attrs.lists(sym::doc) {
+                    if let Some(v) = attr.value_str() {
+                        if attr.has_name(sym::primitive) {
+                            prim = PrimitiveType::from_symbol(v);
+                            if prim.is_some() {
+                                break;
+                            }
+                            // FIXME: should warn on unknown primitives?
+                        }
+                    }
+                }
+                return prim.map(|p| (def_id, p));
+            }
+            None
+        };
+
+        if root.is_local() {
+            tcx.hir()
+                .krate()
+                .item
+                .item_ids
+                .iter()
+                .filter_map(|&id| {
+                    let item = tcx.hir().item(id);
+                    match item.kind {
+                        hir::ItemKind::Mod(_) => {
+                            as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
+                        }
+                        hir::ItemKind::Use(ref path, hir::UseKind::Single)
+                            if item.vis.node.is_pub() =>
+                        {
+                            as_primitive(path.res).map(|(_, prim)| {
+                                // Pretend the primitive is local.
+                                (id.def_id.to_def_id(), prim)
+                            })
+                        }
+                        _ => None,
+                    }
+                })
+                .collect()
+        } else {
+            tcx.item_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect()
+        }
+    }
 }
 
 /// Anything with a source location and set of attributes and, optionally, a
 /// directly to the AST's concept of an item; it's a strict superset.
 #[derive(Clone)]
 crate struct Item {
-    crate span: Span,
     /// The name of this item.
     /// Optional because not every item has a name, e.g. impls.
     crate name: Option<Symbol>,
     /// E.g., struct vs enum vs function.
     crate kind: Box<ItemKind>,
     crate def_id: DefId,
+
+    crate cfg: Option<Arc<Cfg>>,
 }
 
 // `Item` is used a lot. Make sure it doesn't unintentionally get bigger.
@@ -104,16 +232,26 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         let def_id: &dyn fmt::Debug = if self.is_fake() { &"**FAKE**" } else { &self.def_id };
 
         fmt.debug_struct("Item")
-            .field("source", &self.span)
             .field("name", &self.name)
             .field("attrs", &self.attrs)
             .field("kind", &self.kind)
             .field("visibility", &self.visibility)
             .field("def_id", def_id)
+            .field("cfg", &self.cfg)
             .finish()
     }
 }
 
+crate fn rustc_span(def_id: DefId, tcx: TyCtxt<'_>) -> Span {
+    Span::from_rustc_span(def_id.as_local().map_or_else(
+        || tcx.def_span(def_id),
+        |local| {
+            let hir = tcx.hir();
+            hir.span_with_body(hir.local_def_id_to_hir_id(local))
+        },
+    ))
+}
+
 impl Item {
     crate fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx Stability> {
         if self.is_fake() { None } else { tcx.lookup_stability(self.def_id) }
@@ -127,6 +265,30 @@ impl Item {
         if self.is_fake() { None } else { tcx.lookup_deprecation(self.def_id) }
     }
 
+    crate fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool {
+        if self.is_fake() { false } else { tcx.get_attrs(self.def_id).inner_docs() }
+    }
+
+    crate fn span(&self, tcx: TyCtxt<'_>) -> Span {
+        let kind = match &*self.kind {
+            ItemKind::StrippedItem(k) => k,
+            _ => &*self.kind,
+        };
+        if let ItemKind::ModuleItem(Module { span, .. }) | ItemKind::ImplItem(Impl { span, .. }) =
+            kind
+        {
+            *span
+        } else if self.is_fake() {
+            Span::dummy()
+        } else {
+            rustc_span(self.def_id, tcx)
+        }
+    }
+
+    crate fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span {
+        crate::passes::span_of_attrs(&self.attrs).unwrap_or_else(|| self.span(tcx).inner())
+    }
+
     /// Finds the `doc` attribute as a NameValue and returns the corresponding
     /// value found.
     crate fn doc_value(&self) -> Option<String> {
@@ -150,12 +312,15 @@ pub fn from_def_id_and_parts(
         kind: ItemKind,
         cx: &mut DocContext<'_>,
     ) -> Item {
+        let ast_attrs = cx.tcx.get_attrs(def_id);
+
         Self::from_def_id_and_attrs_and_parts(
             def_id,
             name,
             kind,
-            box cx.tcx.get_attrs(def_id).clean(cx),
+            box ast_attrs.clean(cx),
             cx,
+            ast_attrs.cfg(cx.sess().diagnostic()),
         )
     }
 
@@ -165,25 +330,17 @@ pub fn from_def_id_and_attrs_and_parts(
         kind: ItemKind,
         attrs: Box<Attributes>,
         cx: &mut DocContext<'_>,
+        cfg: Option<Arc<Cfg>>,
     ) -> Item {
         debug!("name={:?}, def_id={:?}", name, def_id);
 
-        // `span_if_local()` lies about functions and only gives the span of the function signature
-        let span = def_id.as_local().map_or_else(
-            || cx.tcx.def_span(def_id),
-            |local| {
-                let hir = cx.tcx.hir();
-                hir.span_with_body(hir.local_def_id_to_hir_id(local))
-            },
-        );
-
         Item {
             def_id,
             kind: box kind,
             name,
-            span: span.clean(cx),
             attrs,
             visibility: cx.tcx.visibility(def_id).clean(cx),
+            cfg,
         }
     }
 
@@ -193,19 +350,18 @@ pub fn from_def_id_and_attrs_and_parts(
         self.attrs.collapsed_doc_value()
     }
 
-    crate fn links(&self, cache: &Cache) -> Vec<RenderedLink> {
+    crate fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> {
         use crate::html::format::href;
-        use crate::html::render::CURRENT_DEPTH;
 
-        cache
+        cx.cache()
             .intra_doc_links
             .get(&self.def_id)
             .map_or(&[][..], |v| v.as_slice())
             .iter()
-            .filter_map(|ItemLink { link: s, link_text, did, fragment }| {
+            .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| {
                 match *did {
                     Some(did) => {
-                        if let Some((mut href, ..)) = href(did, cache) {
+                        if let Some((mut href, ..)) = href(did, cx) {
                             if let Some(ref fragment) = *fragment {
                                 href.push('#');
                                 href.push_str(fragment);
@@ -219,16 +375,26 @@ pub fn from_def_id_and_attrs_and_parts(
                             None
                         }
                     }
+                    // FIXME(83083): using fragments as a side-channel for
+                    // primitive names is very unfortunate
                     None => {
+                        let relative_to = &cx.current;
                         if let Some(ref fragment) = *fragment {
-                            let url = match cache.extern_locations.get(&self.def_id.krate) {
+                            let url = match cx.cache().extern_locations.get(&self.def_id.krate) {
                                 Some(&(_, _, ExternalLocation::Local)) => {
-                                    let depth = CURRENT_DEPTH.with(|l| l.get());
-                                    "../".repeat(depth)
+                                    if relative_to[0] == "std" {
+                                        let depth = relative_to.len() - 1;
+                                        "../".repeat(depth)
+                                    } else {
+                                        let depth = relative_to.len();
+                                        format!("{}std/", "../".repeat(depth))
+                                    }
+                                }
+                                Some(&(_, _, ExternalLocation::Remote(ref s))) => {
+                                    format!("{}/std/", s.trim_end_matches('/'))
                                 }
-                                Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(),
                                 Some(&(_, _, ExternalLocation::Unknown)) | None => format!(
-                                    "https://doc.rust-lang.org/{}",
+                                    "https://doc.rust-lang.org/{}/std/",
                                     crate::doc_rust_lang_org_channel(),
                                 ),
                             };
@@ -238,9 +404,8 @@ pub fn from_def_id_and_attrs_and_parts(
                                 original_text: s.clone(),
                                 new_text: link_text.clone(),
                                 href: format!(
-                                    "{}{}std/primitive.{}.html{}",
+                                    "{}primitive.{}.html{}",
                                     url,
-                                    if !url.ends_with('/') { "/" } else { "" },
                                     &fragment[..tail],
                                     &fragment[tail..]
                                 ),
@@ -255,12 +420,9 @@ pub fn from_def_id_and_attrs_and_parts(
     }
 
     crate fn is_crate(&self) -> bool {
-        matches!(
-            *self.kind,
-            StrippedItem(box ModuleItem(Module { is_crate: true, .. }))
-                | ModuleItem(Module { is_crate: true, .. })
-        )
+        self.is_mod() && self.def_id.index == CRATE_DEF_INDEX
     }
+
     crate fn is_mod(&self) -> bool {
         self.type_() == ItemType::Module
     }
@@ -472,7 +634,7 @@ impl ItemKind {
 #[derive(Clone, Debug)]
 crate struct Module {
     crate items: Vec<Item>,
-    crate is_crate: bool,
+    crate span: Span,
 }
 
 crate struct ListAttributesIter<'a> {
@@ -512,12 +674,72 @@ fn size_hint(&self) -> (usize, Option<usize>) {
 crate trait AttributesExt {
     /// Finds an attribute as List and returns the list of attributes nested inside.
     fn lists(&self, name: Symbol) -> ListAttributesIter<'_>;
+
+    fn span(&self) -> Option<rustc_span::Span>;
+
+    fn inner_docs(&self) -> bool;
+
+    fn other_attrs(&self) -> Vec<ast::Attribute>;
+
+    fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option<Arc<Cfg>>;
 }
 
 impl AttributesExt for [ast::Attribute] {
     fn lists(&self, name: Symbol) -> ListAttributesIter<'_> {
         ListAttributesIter { attrs: self.iter(), current_list: Vec::new().into_iter(), name }
     }
+
+    /// Return the span of the first doc-comment, if it exists.
+    fn span(&self) -> Option<rustc_span::Span> {
+        self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span)
+    }
+
+    /// Returns whether the first doc-comment is an inner attribute.
+    ///
+    //// If there are no doc-comments, return true.
+    /// FIXME(#78591): Support both inner and outer attributes on the same item.
+    fn inner_docs(&self) -> bool {
+        self.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == AttrStyle::Inner)
+    }
+
+    fn other_attrs(&self) -> Vec<ast::Attribute> {
+        self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect()
+    }
+
+    fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option<Arc<Cfg>> {
+        let mut cfg = Cfg::True;
+
+        for attr in self.iter() {
+            if attr.doc_str().is_none() && attr.has_name(sym::doc) {
+                if let Some(mi) = attr.meta() {
+                    if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
+                        // Extracted #[doc(cfg(...))]
+                        match Cfg::parse(cfg_mi) {
+                            Ok(new_cfg) => cfg &= new_cfg,
+                            Err(e) => diagnostic.span_err(e.span, e.msg),
+                        }
+                    }
+                }
+            }
+        }
+
+        for attr in self.lists(sym::target_feature) {
+            if attr.has_name(sym::enable) {
+                if let Some(feat) = attr.value_str() {
+                    let meta = attr::mk_name_value_item_str(
+                        Ident::with_dummy_span(sym::target_feature),
+                        feat,
+                        DUMMY_SP,
+                    );
+                    if let Ok(feat_cfg) = Cfg::parse(&meta) {
+                        cfg &= feat_cfg;
+                    }
+                }
+            }
+        }
+
+        if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }
+    }
 }
 
 crate trait NestedAttributesExt {
@@ -626,9 +848,6 @@ fn from_iter<T>(iter: T) -> Self
 crate struct Attributes {
     crate doc_strings: Vec<DocFragment>,
     crate other_attrs: Vec<ast::Attribute>,
-    crate cfg: Option<Arc<Cfg>>,
-    crate span: Option<rustc_span::Span>,
-    crate inner_docs: bool,
 }
 
 #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
@@ -660,6 +879,10 @@ pub struct RenderedLink {
 }
 
 impl Attributes {
+    crate fn lists(&self, name: Symbol) -> ListAttributesIter<'_> {
+        self.other_attrs.lists(name)
+    }
+
     /// Extracts the content from an attribute `#[doc(cfg(content))]`.
     crate fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> {
         use rustc_ast::NestedMetaItem::MetaItem;
@@ -739,13 +962,10 @@ impl Attributes {
     }
 
     crate fn from_ast(
-        diagnostic: &::rustc_errors::Handler,
         attrs: &[ast::Attribute],
         additional_attrs: Option<(&[ast::Attribute], DefId)>,
     ) -> Attributes {
         let mut doc_strings: Vec<DocFragment> = vec![];
-        let mut sp = None;
-        let mut cfg = Cfg::True;
         let mut doc_line = 0;
 
         fn update_need_backline(doc_strings: &mut Vec<DocFragment>, frag: &DocFragment) {
@@ -789,21 +1009,11 @@ fn update_need_backline(doc_strings: &mut Vec<DocFragment>, frag: &DocFragment)
 
                 doc_strings.push(frag);
 
-                if sp.is_none() {
-                    sp = Some(attr.span);
-                }
                 None
             } else {
                 if attr.has_name(sym::doc) {
                     if let Some(mi) = attr.meta() {
-                        if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
-                            // Extracted #[doc(cfg(...))]
-                            match Cfg::parse(cfg_mi) {
-                                Ok(new_cfg) => cfg &= new_cfg,
-                                Err(e) => diagnostic.span_err(e.span, e.msg),
-                            }
-                        } else if let Some((filename, contents)) = Attributes::extract_include(&mi)
-                        {
+                        if let Some((filename, contents)) = Attributes::extract_include(&mi) {
                             let line = doc_line;
                             doc_line += contents.as_str().lines().count();
                             let frag = DocFragment {
@@ -833,35 +1043,7 @@ fn update_need_backline(doc_strings: &mut Vec<DocFragment>, frag: &DocFragment)
             .filter_map(clean_attr)
             .collect();
 
-        // treat #[target_feature(enable = "feat")] attributes as if they were
-        // #[doc(cfg(target_feature = "feat"))] attributes as well
-        for attr in attrs.lists(sym::target_feature) {
-            if attr.has_name(sym::enable) {
-                if let Some(feat) = attr.value_str() {
-                    let meta = attr::mk_name_value_item_str(
-                        Ident::with_dummy_span(sym::target_feature),
-                        feat,
-                        DUMMY_SP,
-                    );
-                    if let Ok(feat_cfg) = Cfg::parse(&meta) {
-                        cfg &= feat_cfg;
-                    }
-                }
-            }
-        }
-
-        let inner_docs = attrs
-            .iter()
-            .find(|a| a.doc_str().is_some())
-            .map_or(true, |a| a.style == AttrStyle::Inner);
-
-        Attributes {
-            doc_strings,
-            other_attrs,
-            cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) },
-            span: sp,
-            inner_docs,
-        }
+        Attributes { doc_strings, other_attrs }
     }
 
     /// Finds the `doc` attribute as a NameValue and returns the corresponding
@@ -927,8 +1109,6 @@ fn update_need_backline(doc_strings: &mut Vec<DocFragment>, frag: &DocFragment)
 impl PartialEq for Attributes {
     fn eq(&self, rhs: &Self) -> bool {
         self.doc_strings == rhs.doc_strings
-            && self.cfg == rhs.cfg
-            && self.span == rhs.span
             && self
                 .other_attrs
                 .iter()
@@ -942,20 +1122,12 @@ impl Eq for Attributes {}
 impl Hash for Attributes {
     fn hash<H: Hasher>(&self, hasher: &mut H) {
         self.doc_strings.hash(hasher);
-        self.cfg.hash(hasher);
-        self.span.hash(hasher);
         for attr in &self.other_attrs {
             attr.id.hash(hasher);
         }
     }
 }
 
-impl AttributesExt for Attributes {
-    fn lists(&self, name: Symbol) -> ListAttributesIter<'_> {
-        self.other_attrs.lists(name)
-    }
-}
-
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 crate enum GenericBound {
     TraitBound(PolyTrait, hir::TraitBoundModifier),
@@ -967,7 +1139,7 @@ impl GenericBound {
         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);
+        inline::record_extern_fqn(cx, did, ItemType::Trait);
         GenericBound::TraitBound(
             PolyTrait {
                 trait_: ResolvedPath { path, param_names: None, did, is_generic: false },
@@ -1118,7 +1290,6 @@ impl GenericParamDef {
     crate inputs: Arguments,
     crate output: FnRetTy,
     crate c_variadic: bool,
-    crate attrs: Attributes,
 }
 
 impl FnDecl {
@@ -1306,62 +1477,6 @@ fn def_id_full(&self, cache: &Cache) -> Option<DefId> {
     Never,
 }
 
-#[derive(Clone, PartialEq, Eq, Hash, Copy, Debug)]
-crate enum TypeKind {
-    Enum,
-    Function,
-    Module,
-    Const,
-    Static,
-    Struct,
-    Union,
-    Trait,
-    Typedef,
-    Foreign,
-    Macro,
-    Attr,
-    Derive,
-    TraitAlias,
-    Primitive,
-}
-
-impl From<hir::def::DefKind> for TypeKind {
-    fn from(other: hir::def::DefKind) -> Self {
-        match other {
-            hir::def::DefKind::Enum => Self::Enum,
-            hir::def::DefKind::Fn => Self::Function,
-            hir::def::DefKind::Mod => Self::Module,
-            hir::def::DefKind::Const => Self::Const,
-            hir::def::DefKind::Static => Self::Static,
-            hir::def::DefKind::Struct => Self::Struct,
-            hir::def::DefKind::Union => Self::Union,
-            hir::def::DefKind::Trait => Self::Trait,
-            hir::def::DefKind::TyAlias => Self::Typedef,
-            hir::def::DefKind::TraitAlias => Self::TraitAlias,
-            hir::def::DefKind::Macro(_) => Self::Macro,
-            hir::def::DefKind::ForeignTy
-            | hir::def::DefKind::Variant
-            | hir::def::DefKind::AssocTy
-            | hir::def::DefKind::TyParam
-            | hir::def::DefKind::ConstParam
-            | hir::def::DefKind::Ctor(..)
-            | hir::def::DefKind::AssocFn
-            | hir::def::DefKind::AssocConst
-            | hir::def::DefKind::ExternCrate
-            | hir::def::DefKind::Use
-            | hir::def::DefKind::ForeignMod
-            | hir::def::DefKind::AnonConst
-            | hir::def::DefKind::OpaqueTy
-            | hir::def::DefKind::Field
-            | hir::def::DefKind::LifetimeParam
-            | hir::def::DefKind::GlobalAsm
-            | hir::def::DefKind::Impl
-            | hir::def::DefKind::Closure
-            | hir::def::DefKind::Generator => Self::Foreign,
-        }
-    }
-}
-
 crate trait GetDefId {
     /// Use this method to get the [`DefId`] of a [`clean`] AST node.
     /// This will return [`None`] when called on a primitive [`clean::Type`].
@@ -1607,18 +1722,18 @@ impl PrimitiveType {
         }
     }
 
-    crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec<[DefId; 4]> {
+    crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec<DefId, 4> {
         Self::all_impls(tcx).get(self).expect("missing impl for primitive type")
     }
 
-    crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap<PrimitiveType, ArrayVec<[DefId; 4]>> {
-        static CELL: OnceCell<FxHashMap<PrimitiveType, ArrayVec<[DefId; 4]>>> = OnceCell::new();
+    crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap<PrimitiveType, ArrayVec<DefId, 4>> {
+        static CELL: OnceCell<FxHashMap<PrimitiveType, ArrayVec<DefId, 4>>> = OnceCell::new();
 
         CELL.get_or_init(move || {
             use self::PrimitiveType::*;
 
             let single = |a: Option<DefId>| a.into_iter().collect();
-            let both = |a: Option<DefId>, b: Option<DefId>| -> ArrayVec<_> {
+            let both = |a: Option<DefId>, b: Option<DefId>| -> ArrayVec<_, 4> {
                 a.into_iter().chain(b).collect()
             };
 
@@ -1847,7 +1962,7 @@ impl Visibility {
 
 /// Small wrapper around [`rustc_span::Span]` that adds helper methods
 /// and enforces calling [`rustc_span::Span::source_callsite()`].
-#[derive(Clone, Debug)]
+#[derive(Copy, Clone, Debug)]
 crate struct Span(rustc_span::Span);
 
 impl Span {
@@ -2032,6 +2147,7 @@ impl Constant {
 
 #[derive(Clone, Debug)]
 crate struct Impl {
+    crate span: Span,
     crate unsafety: hir::Unsafety,
     crate generics: Generics,
     crate provided_trait_methods: FxHashSet<Symbol>,
index 1e79bd0912884d3b922f0dc1891552f503c5c01e..7df8b442e5accdb7d79288c68e89705f414919ae 100644 (file)
@@ -1,11 +1,11 @@
 use crate::clean::auto_trait::AutoTraitFinder;
 use crate::clean::blanket_impl::BlanketImplFinder;
 use crate::clean::{
-    inline, Clean, Crate, ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item,
-    ItemKind, Lifetime, MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type,
-    TypeBinding, TypeKind,
+    inline, Clean, Crate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime,
+    MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding,
 };
 use crate::core::DocContext;
+use crate::formats::item_type::ItemType;
 
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
         _ => unreachable!(),
     }
 
-    let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx);
+    let local_crate = LOCAL_CRATE.clean(cx);
+    let src = local_crate.src(cx.tcx);
+    let name = local_crate.name(cx.tcx);
+    let primitives = local_crate.primitives(cx.tcx);
+    let keywords = local_crate.keywords(cx.tcx);
     {
         let m = match *module.kind {
             ItemKind::ModuleItem(ref mut m) => m,
@@ -401,7 +405,7 @@ fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: &'tcx ty::Const<'tc
             return Generic(kw::SelfUpper);
         }
         Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => {
-            return Generic(Symbol::intern(&format!("{:#}", path.print(&cx.cache, cx.tcx))));
+            return Generic(Symbol::intern(&path.whole_name()));
         }
         Res::SelfTy(..) | Res::Def(DefKind::TyParam | DefKind::AssocTy, _) => true,
         _ => false,
@@ -431,29 +435,29 @@ fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: &'tcx ty::Const<'tc
     debug!("register_res({:?})", res);
 
     let (did, kind) = match res {
-        Res::Def(DefKind::Fn, i) => (i, TypeKind::Function),
-        Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef),
-        Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum),
-        Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait),
+        Res::Def(DefKind::Fn, i) => (i, ItemType::Function),
+        Res::Def(DefKind::TyAlias, i) => (i, ItemType::Typedef),
+        Res::Def(DefKind::Enum, i) => (i, ItemType::Enum),
+        Res::Def(DefKind::Trait, i) => (i, ItemType::Trait),
         Res::Def(DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst, i) => {
-            (cx.tcx.parent(i).unwrap(), TypeKind::Trait)
+            (cx.tcx.parent(i).unwrap(), ItemType::Trait)
         }
-        Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct),
-        Res::Def(DefKind::Union, i) => (i, TypeKind::Union),
-        Res::Def(DefKind::Mod, i) => (i, TypeKind::Module),
-        Res::Def(DefKind::ForeignTy, i) => (i, TypeKind::Foreign),
-        Res::Def(DefKind::Const, i) => (i, TypeKind::Const),
-        Res::Def(DefKind::Static, i) => (i, TypeKind::Static),
+        Res::Def(DefKind::Struct, i) => (i, ItemType::Struct),
+        Res::Def(DefKind::Union, i) => (i, ItemType::Union),
+        Res::Def(DefKind::Mod, i) => (i, ItemType::Module),
+        Res::Def(DefKind::ForeignTy, i) => (i, ItemType::ForeignType),
+        Res::Def(DefKind::Const, i) => (i, ItemType::Constant),
+        Res::Def(DefKind::Static, i) => (i, ItemType::Static),
         Res::Def(DefKind::Variant, i) => {
-            (cx.tcx.parent(i).expect("cannot get parent def id"), TypeKind::Enum)
+            (cx.tcx.parent(i).expect("cannot get parent def id"), ItemType::Enum)
         }
         Res::Def(DefKind::Macro(mac_kind), i) => match mac_kind {
-            MacroKind::Bang => (i, TypeKind::Macro),
-            MacroKind::Attr => (i, TypeKind::Attr),
-            MacroKind::Derive => (i, TypeKind::Derive),
+            MacroKind::Bang => (i, ItemType::Macro),
+            MacroKind::Attr => (i, ItemType::ProcAttribute),
+            MacroKind::Derive => (i, ItemType::ProcDerive),
         },
-        Res::Def(DefKind::TraitAlias, i) => (i, TypeKind::TraitAlias),
-        Res::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait),
+        Res::Def(DefKind::TraitAlias, i) => (i, ItemType::TraitAlias),
+        Res::SelfTy(Some(def_id), _) => (def_id, ItemType::Trait),
         Res::SelfTy(_, Some((impl_def_id, _))) => return impl_def_id,
         _ => return res.def_id(),
     };
@@ -461,7 +465,7 @@ fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: &'tcx ty::Const<'tc
         return did;
     }
     inline::record_extern_fqn(cx, did, kind);
-    if let TypeKind::Trait = kind {
+    if let ItemType::Trait = kind {
         inline::record_extern_trait(cx, did);
     }
     did
index be7bff1a29c2b4d51e45cbc9afc3b4007e8c8f36..212aac0e5b43c97733ee2a0dd38f66d79a640397 100644 (file)
@@ -32,7 +32,7 @@
 
 use crate::clean;
 use crate::clean::inline::build_external_trait;
-use crate::clean::{AttributesExt, TraitWithExtraInfo, MAX_DEF_IDX};
+use crate::clean::{TraitWithExtraInfo, MAX_DEF_IDX};
 use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
 use crate::formats::cache::Cache;
 use crate::passes::{self, Condition::*, ConditionalPass};
index 6f6ed0eb68413c8d9a2de99ad4b1d9017c3d166d..466d1b65406cd24ffd63aef83f2eba65264dd9a6 100644 (file)
@@ -26,7 +26,7 @@
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::{Arc, Mutex};
 
-use crate::clean::Attributes;
+use crate::clean::{types::AttributesExt, Attributes};
 use crate::config::Options;
 use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
 use crate::lint::init_lints;
@@ -1092,9 +1092,10 @@ fn visit_testable<F: FnOnce(&mut Self)>(
         sp: Span,
         nested: F,
     ) {
-        let attrs = self.tcx.hir().attrs(hir_id);
-        let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs, None);
-        if let Some(ref cfg) = attrs.cfg {
+        let ast_attrs = self.tcx.hir().attrs(hir_id);
+        let mut attrs = Attributes::from_ast(ast_attrs, None);
+
+        if let Some(ref cfg) = ast_attrs.cfg(self.sess.diagnostic()) {
             if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
                 return;
             }
@@ -1110,8 +1111,8 @@ fn visit_testable<F: FnOnce(&mut Self)>(
         // 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
+            let span = ast_attrs
+                .span()
                 .map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
                 .unwrap_or(DUMMY_SP);
             self.collector.set_position(span);
index 189624c0d809c35ea7df84971e415271629db459..d3f4353a58b7b60aac7a1925cfd3e153b1de5fcd 100644 (file)
@@ -14,7 +14,6 @@
     crate items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
     crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
     crate macros: Vec<(&'hir hir::MacroDef<'hir>, Option<Symbol>)>,
-    crate is_crate: bool,
 }
 
 impl Module<'hir> {
@@ -28,7 +27,6 @@ impl Module<'hir> {
             items: Vec::new(),
             foreigns: Vec::new(),
             macros: Vec::new(),
-            is_crate: false,
         }
     }
 }
index 376fef6568af7601d8f417f86edeb030298a777d..37d11d4ed47c445a5fbbe0c904b63e2aab80903c 100644 (file)
@@ -81,7 +81,7 @@ fn fold_item_recur(&mut self, mut item: Item) -> Item {
 
     fn fold_mod(&mut self, m: Module) -> Module {
         Module {
-            is_crate: m.is_crate,
+            span: m.span,
             items: m.items.into_iter().filter_map(|i| self.fold_item(i)).collect(),
         }
     }
index b2b895cc6726e150ca2fde05d928ba41beb5a6e4..8f8bca64e1497fcbbe097851f72452355f7a91c8 100644 (file)
@@ -155,19 +155,22 @@ impl Cache {
         // Cache where all our extern crates are located
         // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code
         for &(n, ref e) in &krate.externs {
-            let src_root = match e.src {
+            let src_root = match e.src(tcx) {
                 FileName::Real(ref p) => match p.local_path().parent() {
                     Some(p) => p.to_path_buf(),
                     None => PathBuf::new(),
                 },
                 _ => PathBuf::new(),
             };
-            let extern_url = extern_html_root_urls.get(&*e.name.as_str()).map(|u| &**u);
-            self.extern_locations
-                .insert(n, (e.name, src_root, extern_location(e, extern_url, &dst)));
-
+            let name = e.name(tcx);
+            let extern_url = extern_html_root_urls.get(&*name.as_str()).map(|u| &**u);
             let did = DefId { krate: n, index: CRATE_DEF_INDEX };
-            self.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
+            self.extern_locations.insert(
+                n,
+                (name, src_root, extern_location(e, extern_url, tcx.get_attrs(did), &dst, tcx)),
+            );
+
+            self.external_paths.insert(did, (vec![name.to_string()], ItemType::Module));
         }
 
         // Cache where all known primitives have their documentation located.
@@ -175,7 +178,7 @@ impl Cache {
         // Favor linking to as local extern as possible, so iterate all crates in
         // reverse topological order.
         for &(_, ref e) in krate.externs.iter().rev() {
-            for &(def_id, prim) in &e.primitives {
+            for &(def_id, prim) in &e.primitives(tcx) {
                 self.primitive_locations.insert(prim, def_id);
             }
         }
index 460d4b907c09a7f6f0a16ca5e1e52406d6736ab6..955de57dc0e5f3ea9a3b231869ef33bb7fbb3706 100644 (file)
@@ -4,6 +4,7 @@
 
 use serde::{Serialize, Serializer};
 
+use rustc_hir::def::DefKind;
 use rustc_span::hygiene::MacroKind;
 
 use crate::clean;
@@ -19,7 +20,7 @@
 /// module headings. If you are adding to this enum and want to ensure that the sidebar also prints
 /// a heading, edit the listing in `html/render.rs`, function `sidebar_module`. This uses an
 /// ordering based on a helper function inside `item_module`, in the same file.
-#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord)]
+#[derive(Copy, PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)]
 crate enum ItemType {
     Module = 0,
     ExternCrate = 1,
@@ -102,24 +103,43 @@ fn from(item: &'a clean::Item) -> ItemType {
     }
 }
 
-impl From<clean::TypeKind> for ItemType {
-    fn from(kind: clean::TypeKind) -> ItemType {
-        match kind {
-            clean::TypeKind::Struct => ItemType::Struct,
-            clean::TypeKind::Union => ItemType::Union,
-            clean::TypeKind::Enum => ItemType::Enum,
-            clean::TypeKind::Function => ItemType::Function,
-            clean::TypeKind::Trait => ItemType::Trait,
-            clean::TypeKind::Module => ItemType::Module,
-            clean::TypeKind::Static => ItemType::Static,
-            clean::TypeKind::Const => ItemType::Constant,
-            clean::TypeKind::Typedef => ItemType::Typedef,
-            clean::TypeKind::Foreign => ItemType::ForeignType,
-            clean::TypeKind::Macro => ItemType::Macro,
-            clean::TypeKind::Attr => ItemType::ProcAttribute,
-            clean::TypeKind::Derive => ItemType::ProcDerive,
-            clean::TypeKind::TraitAlias => ItemType::TraitAlias,
-            clean::TypeKind::Primitive => ItemType::Primitive,
+impl From<DefKind> for ItemType {
+    fn from(other: DefKind) -> Self {
+        match other {
+            DefKind::Enum => Self::Enum,
+            DefKind::Fn => Self::Function,
+            DefKind::Mod => Self::Module,
+            DefKind::Const => Self::Constant,
+            DefKind::Static => Self::Static,
+            DefKind::Struct => Self::Struct,
+            DefKind::Union => Self::Union,
+            DefKind::Trait => Self::Trait,
+            DefKind::TyAlias => Self::Typedef,
+            DefKind::TraitAlias => Self::TraitAlias,
+            DefKind::Macro(kind) => match kind {
+                MacroKind::Bang => ItemType::Macro,
+                MacroKind::Attr => ItemType::ProcAttribute,
+                MacroKind::Derive => ItemType::ProcDerive,
+            },
+            DefKind::ForeignTy
+            | DefKind::Variant
+            | DefKind::AssocTy
+            | DefKind::TyParam
+            | DefKind::ConstParam
+            | DefKind::Ctor(..)
+            | DefKind::AssocFn
+            | DefKind::AssocConst
+            | DefKind::ExternCrate
+            | DefKind::Use
+            | DefKind::ForeignMod
+            | DefKind::AnonConst
+            | DefKind::OpaqueTy
+            | DefKind::Field
+            | DefKind::LifetimeParam
+            | DefKind::GlobalAsm
+            | DefKind::Impl
+            | DefKind::Closure
+            | DefKind::Generator => Self::ForeignType,
         }
     }
 }
index ae97cd64fb5fbe5010f7878f2d77ee3a66a48113..b8ef3384c59083aa54b58222555a39b1bbd71e27 100644 (file)
@@ -1,5 +1,5 @@
 use rustc_middle::ty::TyCtxt;
-use rustc_span::{edition::Edition, Symbol};
+use rustc_span::Symbol;
 
 use crate::clean;
 use crate::config::RenderOptions;
@@ -23,7 +23,6 @@
     fn init(
         krate: clean::Crate,
         options: RenderOptions,
-        edition: Edition,
         cache: Cache,
         tcx: TyCtxt<'tcx>,
     ) -> Result<(Self, clean::Crate), Error>;
@@ -35,19 +34,15 @@ fn init(
     fn item(&mut self, item: clean::Item) -> Result<(), Error>;
 
     /// Renders a module (should not handle recursing into children).
-    fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error>;
+    fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error>;
 
     /// Runs after recursively rendering all sub-items of a module.
-    fn mod_item_out(&mut self, item_name: &str) -> Result<(), Error>;
+    fn mod_item_out(&mut self) -> Result<(), Error> {
+        Ok(())
+    }
 
     /// Post processing hook for cleanup and dumping output to files.
-    ///
-    /// A handler is available if the renderer wants to report errors.
-    fn after_krate(
-        &mut self,
-        crate_name: Symbol,
-        diag: &rustc_errors::Handler,
-    ) -> Result<(), Error>;
+    fn after_krate(&mut self) -> Result<(), Error>;
 
     fn cache(&self) -> &Cache;
 }
@@ -57,8 +52,6 @@ fn after_krate(
     krate: clean::Crate,
     options: RenderOptions,
     cache: Cache,
-    diag: &rustc_errors::Handler,
-    edition: Edition,
     tcx: TyCtxt<'tcx>,
 ) -> Result<(), Error> {
     let prof = &tcx.sess.prof;
@@ -66,14 +59,13 @@ fn after_krate(
     let emit_crate = options.should_emit_crate();
     let (mut format_renderer, krate) = prof
         .extra_verbose_generic_activity("create_renderer", T::descr())
-        .run(|| T::init(krate, options, edition, cache, tcx))?;
+        .run(|| T::init(krate, options, cache, tcx))?;
 
     if !emit_crate {
         return Ok(());
     }
 
     // Render the crate documentation
-    let crate_name = krate.name;
     let mut work = vec![(format_renderer.make_child_renderer(), krate.module)];
 
     let unknown = Symbol::intern("<unknown item>");
@@ -81,13 +73,10 @@ fn after_krate(
         if item.is_mod() && T::RUN_ON_MODULE {
             // modules are special because they add a namespace. We also need to
             // recurse into the items of the module as well.
-            let name = item.name.as_ref().unwrap().to_string();
-            if name.is_empty() {
-                panic!("Unexpected module with empty name");
-            }
-            let _timer = prof.generic_activity_with_arg("render_mod_item", name.as_str());
+            let _timer =
+                prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string());
 
-            cx.mod_item_in(&item, &name)?;
+            cx.mod_item_in(&item)?;
             let module = match *item.kind {
                 clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m,
                 _ => unreachable!(),
@@ -97,7 +86,7 @@ fn after_krate(
                 work.push((cx.make_child_renderer(), it));
             }
 
-            cx.mod_item_out(&name)?;
+            cx.mod_item_out()?;
         // FIXME: checking `item.name.is_some()` is very implicit and leads to lots of special
         // cases. Use an explicit match instead.
         } else if item.name.is_some() && !item.is_extern_crate() {
@@ -106,5 +95,5 @@ fn after_krate(
         }
     }
     prof.extra_verbose_generic_activity("renderer_after_krate", T::descr())
-        .run(|| format_renderer.after_krate(crate_name, diag))
+        .run(|| format_renderer.after_krate())
 }
index a004ee5054ed68baacb27f118788c5890685b1af..45412f55c1572df8383c9180031064eaf0e7aa40 100644 (file)
@@ -7,6 +7,7 @@
 
 use std::cell::Cell;
 use std::fmt;
+use std::iter;
 
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_target::spec::abi::Abi;
 
 use crate::clean::{self, utils::find_nearest_parent_module, PrimitiveType};
-use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
 use crate::html::render::cache::ExternalLocation;
-use crate::html::render::CURRENT_DEPTH;
+use crate::html::render::Context;
 
 crate trait Print {
     fn print(self, buffer: &mut Buffer);
@@ -82,6 +82,10 @@ impl Buffer {
         self.buffer.push_str(s);
     }
 
+    crate fn push_buffer(&mut self, other: Buffer) {
+        self.buffer.push_str(&other.buffer);
+    }
+
     // Intended for consumption by write! and writeln! (std::fmt) but without
     // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
     // import).
@@ -125,19 +129,18 @@ fn comma_sep<T: fmt::Display>(items: impl Iterator<Item = T>) -> impl fmt::Displ
 
 crate fn print_generic_bounds<'a, 'tcx: 'a>(
     bounds: &'a [clean::GenericBound],
-    cache: &'a Cache,
-    tcx: TyCtxt<'tcx>,
+    cx: &'a Context<'tcx>,
 ) -> impl fmt::Display + 'a + Captures<'tcx> {
     display_fn(move |f| {
         let mut bounds_dup = FxHashSet::default();
 
         for (i, bound) in
-            bounds.iter().filter(|b| bounds_dup.insert(b.print(cache, tcx).to_string())).enumerate()
+            bounds.iter().filter(|b| bounds_dup.insert(b.print(cx).to_string())).enumerate()
         {
             if i > 0 {
                 f.write_str(" + ")?;
             }
-            fmt::Display::fmt(&bound.print(cache, tcx), f)?;
+            fmt::Display::fmt(&bound.print(cx), f)?;
         }
         Ok(())
     })
@@ -146,8 +149,7 @@ fn comma_sep<T: fmt::Display>(items: impl Iterator<Item = T>) -> impl fmt::Displ
 impl clean::GenericParamDef {
     crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| match self.kind {
             clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name),
@@ -156,17 +158,17 @@ impl clean::GenericParamDef {
 
                 if !bounds.is_empty() {
                     if f.alternate() {
-                        write!(f, ": {:#}", print_generic_bounds(bounds, cache, tcx))?;
+                        write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
                     } else {
-                        write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cache, tcx))?;
+                        write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
                     }
                 }
 
                 if let Some(ref ty) = default {
                     if f.alternate() {
-                        write!(f, " = {:#}", ty.print(cache, tcx))?;
+                        write!(f, " = {:#}", ty.print(cx))?;
                     } else {
-                        write!(f, "&nbsp;=&nbsp;{}", ty.print(cache, tcx))?;
+                        write!(f, "&nbsp;=&nbsp;{}", ty.print(cx))?;
                     }
                 }
 
@@ -174,9 +176,9 @@ impl clean::GenericParamDef {
             }
             clean::GenericParamDefKind::Const { ref ty, .. } => {
                 if f.alternate() {
-                    write!(f, "const {}: {:#}", self.name, ty.print(cache, tcx))
+                    write!(f, "const {}: {:#}", self.name, ty.print(cx))
                 } else {
-                    write!(f, "const {}:&nbsp;{}", self.name, ty.print(cache, tcx))
+                    write!(f, "const {}:&nbsp;{}", self.name, ty.print(cx))
                 }
             }
         })
@@ -186,8 +188,7 @@ impl clean::GenericParamDef {
 impl clean::Generics {
     crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| {
             let real_params =
@@ -196,9 +197,9 @@ impl clean::Generics {
                 return Ok(());
             }
             if f.alternate() {
-                write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cache, tcx))))
+                write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cx))))
             } else {
-                write!(f, "&lt;{}&gt;", comma_sep(real_params.iter().map(|g| g.print(cache, tcx))))
+                write!(f, "&lt;{}&gt;", comma_sep(real_params.iter().map(|g| g.print(cx))))
             }
         })
     }
@@ -209,8 +210,7 @@ impl clean::Generics {
 /// * Whether the where-clause needs to add a comma and newline after the last bound.
 crate fn print_where_clause<'a, 'tcx: 'a>(
     gens: &'a clean::Generics,
-    cache: &'a Cache,
-    tcx: TyCtxt<'tcx>,
+    cx: &'a Context<'tcx>,
     indent: usize,
     end_newline: bool,
 ) -> impl fmt::Display + 'a + Captures<'tcx> {
@@ -241,14 +241,14 @@ impl clean::Generics {
                     if f.alternate() {
                         clause.push_str(&format!(
                             "{:#}: {:#}",
-                            ty.print(cache, tcx),
-                            print_generic_bounds(bounds, cache, tcx)
+                            ty.print(cx),
+                            print_generic_bounds(bounds, cx)
                         ));
                     } else {
                         clause.push_str(&format!(
                             "{}: {}",
-                            ty.print(cache, tcx),
-                            print_generic_bounds(bounds, cache, tcx)
+                            ty.print(cx),
+                            print_generic_bounds(bounds, cx)
                         ));
                     }
                 }
@@ -258,24 +258,16 @@ impl clean::Generics {
                         lifetime.print(),
                         bounds
                             .iter()
-                            .map(|b| b.print(cache, tcx).to_string())
+                            .map(|b| b.print(cx).to_string())
                             .collect::<Vec<_>>()
                             .join(" + ")
                     ));
                 }
                 clean::WherePredicate::EqPredicate { lhs, rhs } => {
                     if f.alternate() {
-                        clause.push_str(&format!(
-                            "{:#} == {:#}",
-                            lhs.print(cache, tcx),
-                            rhs.print(cache, tcx),
-                        ));
+                        clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),));
                     } else {
-                        clause.push_str(&format!(
-                            "{} == {}",
-                            lhs.print(cache, tcx),
-                            rhs.print(cache, tcx),
-                        ));
+                        clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),));
                     }
                 }
             }
@@ -327,8 +319,7 @@ impl clean::Constant {
 impl clean::PolyTrait {
     fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| {
             if !self.generic_params.is_empty() {
@@ -336,20 +327,20 @@ fn print<'a, 'tcx: 'a>(
                     write!(
                         f,
                         "for<{:#}> ",
-                        comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx)))
+                        comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
                     )?;
                 } else {
                     write!(
                         f,
                         "for&lt;{}&gt; ",
-                        comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx)))
+                        comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
                     )?;
                 }
             }
             if f.alternate() {
-                write!(f, "{:#}", self.trait_.print(cache, tcx))
+                write!(f, "{:#}", self.trait_.print(cx))
             } else {
-                write!(f, "{}", self.trait_.print(cache, tcx))
+                write!(f, "{}", self.trait_.print(cx))
             }
         })
     }
@@ -358,8 +349,7 @@ fn print<'a, 'tcx: 'a>(
 impl clean::GenericBound {
     crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| match self {
             clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
@@ -370,9 +360,9 @@ impl clean::GenericBound {
                     hir::TraitBoundModifier::MaybeConst => "?const",
                 };
                 if f.alternate() {
-                    write!(f, "{}{:#}", modifier_str, ty.print(cache, tcx))
+                    write!(f, "{}{:#}", modifier_str, ty.print(cx))
                 } else {
-                    write!(f, "{}{}", modifier_str, ty.print(cache, tcx))
+                    write!(f, "{}{}", modifier_str, ty.print(cx))
                 }
             }
         })
@@ -382,8 +372,7 @@ impl clean::GenericBound {
 impl clean::GenericArgs {
     fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| {
             match self {
@@ -401,9 +390,9 @@ fn print<'a, 'tcx: 'a>(
                             }
                             comma = true;
                             if f.alternate() {
-                                write!(f, "{:#}", arg.print(cache, tcx))?;
+                                write!(f, "{:#}", arg.print(cx))?;
                             } else {
-                                write!(f, "{}", arg.print(cache, tcx))?;
+                                write!(f, "{}", arg.print(cx))?;
                             }
                         }
                         for binding in bindings {
@@ -412,9 +401,9 @@ fn print<'a, 'tcx: 'a>(
                             }
                             comma = true;
                             if f.alternate() {
-                                write!(f, "{:#}", binding.print(cache, tcx))?;
+                                write!(f, "{:#}", binding.print(cx))?;
                             } else {
-                                write!(f, "{}", binding.print(cache, tcx))?;
+                                write!(f, "{}", binding.print(cx))?;
                             }
                         }
                         if f.alternate() {
@@ -433,17 +422,17 @@ fn print<'a, 'tcx: 'a>(
                         }
                         comma = true;
                         if f.alternate() {
-                            write!(f, "{:#}", ty.print(cache, tcx))?;
+                            write!(f, "{:#}", ty.print(cx))?;
                         } else {
-                            write!(f, "{}", ty.print(cache, tcx))?;
+                            write!(f, "{}", ty.print(cx))?;
                         }
                     }
                     f.write_str(")")?;
                     if let Some(ref ty) = *output {
                         if f.alternate() {
-                            write!(f, " -> {:#}", ty.print(cache, tcx))?;
+                            write!(f, " -> {:#}", ty.print(cx))?;
                         } else {
-                            write!(f, " -&gt; {}", ty.print(cache, tcx))?;
+                            write!(f, " -&gt; {}", ty.print(cx))?;
                         }
                     }
                 }
@@ -453,98 +442,89 @@ fn print<'a, 'tcx: 'a>(
     }
 }
 
-impl clean::PathSegment {
-    crate fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
-    ) -> impl fmt::Display + 'a + Captures<'tcx> {
-        display_fn(move |f| {
-            if f.alternate() {
-                write!(f, "{}{:#}", self.name, self.args.print(cache, tcx))
-            } else {
-                write!(f, "{}{}", self.name, self.args.print(cache, tcx))
-            }
-        })
+crate fn href(did: DefId, cx: &Context<'_>) -> Option<(String, ItemType, Vec<String>)> {
+    let cache = &cx.cache();
+    let relative_to = &cx.current;
+    fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
+        if shortty == ItemType::Module { &fqp[..] } else { &fqp[..fqp.len() - 1] }
     }
-}
 
-impl clean::Path {
-    crate fn print<'a, 'tcx: 'a>(
-        &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
-    ) -> impl fmt::Display + 'a + Captures<'tcx> {
-        display_fn(move |f| {
-            if self.global {
-                f.write_str("::")?
-            }
-
-            for (i, seg) in self.segments.iter().enumerate() {
-                if i > 0 {
-                    f.write_str("::")?
-                }
-                if f.alternate() {
-                    write!(f, "{:#}", seg.print(cache, tcx))?;
-                } else {
-                    write!(f, "{}", seg.print(cache, tcx))?;
-                }
-            }
-            Ok(())
-        })
-    }
-}
-
-crate fn href(did: DefId, cache: &Cache) -> Option<(String, ItemType, Vec<String>)> {
     if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
         return None;
     }
 
-    let depth = CURRENT_DEPTH.with(|l| l.get());
-    let (fqp, shortty, mut url) = match cache.paths.get(&did) {
-        Some(&(ref fqp, shortty)) => (fqp, shortty, "../".repeat(depth)),
+    let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
+        Some(&(ref fqp, shortty)) => (fqp, shortty, {
+            let module_fqp = to_module_fqp(shortty, fqp);
+            href_relative_parts(module_fqp, relative_to)
+        }),
         None => {
             let &(ref fqp, shortty) = cache.external_paths.get(&did)?;
+            let module_fqp = to_module_fqp(shortty, fqp);
             (
                 fqp,
                 shortty,
                 match cache.extern_locations[&did.krate] {
-                    (.., ExternalLocation::Remote(ref s)) => s.to_string(),
-                    (.., ExternalLocation::Local) => "../".repeat(depth),
+                    (.., ExternalLocation::Remote(ref s)) => {
+                        let s = s.trim_end_matches('/');
+                        let mut s = vec![&s[..]];
+                        s.extend(module_fqp[..].iter().map(String::as_str));
+                        s
+                    }
+                    (.., ExternalLocation::Local) => href_relative_parts(module_fqp, relative_to),
                     (.., ExternalLocation::Unknown) => return None,
                 },
             )
         }
     };
-    for component in &fqp[..fqp.len() - 1] {
-        url.push_str(component);
-        url.push('/');
-    }
+    let last = &fqp.last().unwrap()[..];
+    let filename;
     match shortty {
         ItemType::Module => {
-            url.push_str(fqp.last().unwrap());
-            url.push_str("/index.html");
+            url_parts.push("index.html");
         }
         _ => {
-            url.push_str(shortty.as_str());
-            url.push('.');
-            url.push_str(fqp.last().unwrap());
-            url.push_str(".html");
+            filename = format!("{}.{}.html", shortty.as_str(), last);
+            url_parts.push(&filename);
         }
     }
-    Some((url, shortty, fqp.to_vec()))
+    Some((url_parts.join("/"), shortty, fqp.to_vec()))
+}
+
+/// Both paths should only be modules.
+/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
+/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
+crate fn href_relative_parts<'a>(fqp: &'a [String], relative_to_fqp: &'a [String]) -> Vec<&'a str> {
+    for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
+        // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
+        if f != r {
+            let dissimilar_part_count = relative_to_fqp.len() - i;
+            let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
+            return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
+        }
+    }
+    // e.g. linking to std::sync::atomic from std::sync
+    if relative_to_fqp.len() < fqp.len() {
+        fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
+    // e.g. linking to std::sync from std::sync::atomic
+    } else if fqp.len() < relative_to_fqp.len() {
+        let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
+        iter::repeat("..").take(dissimilar_part_count).collect()
+    // linking to the same module
+    } else {
+        Vec::new()
+    }
 }
 
 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
 /// rendering function with the necessary arguments for linking to a local path.
-fn resolved_path<'a, 'tcx: 'a>(
+fn resolved_path<'a, 'cx: 'a>(
     w: &mut fmt::Formatter<'_>,
     did: DefId,
     path: &clean::Path,
     print_all: bool,
     use_absolute: bool,
-    cache: &Cache,
-    tcx: TyCtxt<'tcx>,
+    cx: &'cx Context<'_>,
 ) -> fmt::Result {
     let last = path.segments.last().unwrap();
 
@@ -554,22 +534,22 @@ fn resolved_path<'a, 'tcx: 'a>(
         }
     }
     if w.alternate() {
-        write!(w, "{}{:#}", &last.name, last.args.print(cache, tcx))?;
+        write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
     } else {
         let path = if use_absolute {
-            if let Some((_, _, fqp)) = href(did, cache) {
+            if let Some((_, _, fqp)) = href(did, cx) {
                 format!(
                     "{}::{}",
                     fqp[..fqp.len() - 1].join("::"),
-                    anchor(did, fqp.last().unwrap(), cache)
+                    anchor(did, fqp.last().unwrap(), cx)
                 )
             } else {
                 last.name.to_string()
             }
         } else {
-            anchor(did, &*last.name.as_str(), cache).to_string()
+            anchor(did, &*last.name.as_str(), cx).to_string()
         };
-        write!(w, "{}{}", path, last.args.print(cache, tcx))?;
+        write!(w, "{}{}", path, last.args.print(cx))?;
     }
     Ok(())
 }
@@ -578,13 +558,14 @@ fn primitive_link(
     f: &mut fmt::Formatter<'_>,
     prim: clean::PrimitiveType,
     name: &str,
-    m: &Cache,
+    cx: &Context<'_>,
 ) -> fmt::Result {
+    let m = &cx.cache();
     let mut needs_termination = false;
     if !f.alternate() {
         match m.primitive_locations.get(&prim) {
             Some(&def_id) if def_id.is_local() => {
-                let len = CURRENT_DEPTH.with(|s| s.get());
+                let len = cx.current.len();
                 let len = if len == 0 { 0 } else { len - 1 };
                 write!(
                     f,
@@ -595,20 +576,28 @@ fn primitive_link(
                 needs_termination = true;
             }
             Some(&def_id) => {
+                let cname_str;
                 let loc = match m.extern_locations[&def_id.krate] {
-                    (ref cname, _, ExternalLocation::Remote(ref s)) => Some((cname, s.to_string())),
+                    (ref cname, _, ExternalLocation::Remote(ref s)) => {
+                        cname_str = cname.as_str();
+                        Some(vec![s.trim_end_matches('/'), &cname_str[..]])
+                    }
                     (ref cname, _, ExternalLocation::Local) => {
-                        let len = CURRENT_DEPTH.with(|s| s.get());
-                        Some((cname, "../".repeat(len)))
+                        cname_str = cname.as_str();
+                        Some(if cx.current.first().map(|x| &x[..]) == Some(&cname_str[..]) {
+                            iter::repeat("..").take(cx.current.len() - 1).collect()
+                        } else {
+                            let cname = iter::once(&cname_str[..]);
+                            iter::repeat("..").take(cx.current.len()).chain(cname).collect()
+                        })
                     }
                     (.., ExternalLocation::Unknown) => None,
                 };
-                if let Some((cname, root)) = loc {
+                if let Some(loc) = loc {
                     write!(
                         f,
-                        "<a class=\"primitive\" href=\"{}{}/primitive.{}.html\">",
-                        root,
-                        cname,
+                        "<a class=\"primitive\" href=\"{}/primitive.{}.html\">",
+                        loc.join("/"),
                         prim.to_url_str()
                     )?;
                     needs_termination = true;
@@ -627,14 +616,13 @@ fn primitive_link(
 /// Helper to render type parameters
 fn tybounds<'a, 'tcx: 'a>(
     param_names: &'a Option<Vec<clean::GenericBound>>,
-    cache: &'a Cache,
-    tcx: TyCtxt<'tcx>,
+    cx: &'a Context<'tcx>,
 ) -> impl fmt::Display + 'a + Captures<'tcx> {
     display_fn(move |f| match *param_names {
         Some(ref params) => {
             for param in params {
                 write!(f, " + ")?;
-                fmt::Display::fmt(&param.print(cache, tcx), f)?;
+                fmt::Display::fmt(&param.print(cx), f)?;
             }
             Ok(())
         }
@@ -642,9 +630,14 @@ fn tybounds<'a, 'tcx: 'a>(
     })
 }
 
-crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Display + 'a {
+crate fn anchor<'a, 'cx: 'a>(
+    did: DefId,
+    text: &'a str,
+    cx: &'cx Context<'_>,
+) -> impl fmt::Display + 'a {
+    let parts = href(did, cx);
     display_fn(move |f| {
-        if let Some((url, short_ty, fqp)) = href(did, cache) {
+        if let Some((url, short_ty, fqp)) = parts {
             write!(
                 f,
                 r#"<a class="{}" href="{}" title="{} {}">{}</a>"#,
@@ -660,12 +653,11 @@ fn tybounds<'a, 'tcx: 'a>(
     })
 }
 
-fn fmt_type(
+fn fmt_type<'cx>(
     t: &clean::Type,
     f: &mut fmt::Formatter<'_>,
     use_absolute: bool,
-    cache: &Cache,
-    tcx: TyCtxt<'_>,
+    cx: &'cx Context<'_>,
 ) -> fmt::Result {
     debug!("fmt_type(t = {:?})", t);
 
@@ -676,69 +668,69 @@ fn fmt_type(
                 f.write_str("dyn ")?;
             }
             // Paths like `T::Output` and `Self::Output` should be rendered with all segments.
-            resolved_path(f, did, path, is_generic, use_absolute, cache, tcx)?;
-            fmt::Display::fmt(&tybounds(param_names, cache, tcx), f)
+            resolved_path(f, did, path, is_generic, use_absolute, cx)?;
+            fmt::Display::fmt(&tybounds(param_names, cx), f)
         }
         clean::Infer => write!(f, "_"),
-        clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cache),
+        clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cx),
         clean::BareFunction(ref decl) => {
             if f.alternate() {
                 write!(
                     f,
                     "{:#}{}{:#}fn{:#}",
-                    decl.print_hrtb_with_space(cache, tcx),
+                    decl.print_hrtb_with_space(cx),
                     decl.unsafety.print_with_space(),
                     print_abi_with_space(decl.abi),
-                    decl.decl.print(cache, tcx),
+                    decl.decl.print(cx),
                 )
             } else {
                 write!(
                     f,
                     "{}{}{}",
-                    decl.print_hrtb_with_space(cache, tcx),
+                    decl.print_hrtb_with_space(cx),
                     decl.unsafety.print_with_space(),
                     print_abi_with_space(decl.abi)
                 )?;
-                primitive_link(f, PrimitiveType::Fn, "fn", cache)?;
-                write!(f, "{}", decl.decl.print(cache, tcx))
+                primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
+                write!(f, "{}", decl.decl.print(cx))
             }
         }
         clean::Tuple(ref typs) => {
             match &typs[..] {
-                &[] => primitive_link(f, PrimitiveType::Unit, "()", cache),
+                &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
                 &[ref one] => {
-                    primitive_link(f, PrimitiveType::Tuple, "(", cache)?;
+                    primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
                     // Carry `f.alternate()` into this display w/o branching manually.
-                    fmt::Display::fmt(&one.print(cache, tcx), f)?;
-                    primitive_link(f, PrimitiveType::Tuple, ",)", cache)
+                    fmt::Display::fmt(&one.print(cx), f)?;
+                    primitive_link(f, PrimitiveType::Tuple, ",)", cx)
                 }
                 many => {
-                    primitive_link(f, PrimitiveType::Tuple, "(", cache)?;
+                    primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
                     for (i, item) in many.iter().enumerate() {
                         if i != 0 {
                             write!(f, ", ")?;
                         }
-                        fmt::Display::fmt(&item.print(cache, tcx), f)?;
+                        fmt::Display::fmt(&item.print(cx), f)?;
                     }
-                    primitive_link(f, PrimitiveType::Tuple, ")", cache)
+                    primitive_link(f, PrimitiveType::Tuple, ")", cx)
                 }
             }
         }
         clean::Slice(ref t) => {
-            primitive_link(f, PrimitiveType::Slice, "[", cache)?;
-            fmt::Display::fmt(&t.print(cache, tcx), f)?;
-            primitive_link(f, PrimitiveType::Slice, "]", cache)
+            primitive_link(f, PrimitiveType::Slice, "[", cx)?;
+            fmt::Display::fmt(&t.print(cx), f)?;
+            primitive_link(f, PrimitiveType::Slice, "]", cx)
         }
         clean::Array(ref t, ref n) => {
-            primitive_link(f, PrimitiveType::Array, "[", cache)?;
-            fmt::Display::fmt(&t.print(cache, tcx), f)?;
+            primitive_link(f, PrimitiveType::Array, "[", cx)?;
+            fmt::Display::fmt(&t.print(cx), f)?;
             if f.alternate() {
-                primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cache)
+                primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cx)
             } else {
-                primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cache)
+                primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx)
             }
         }
-        clean::Never => primitive_link(f, PrimitiveType::Never, "!", cache),
+        clean::Never => primitive_link(f, PrimitiveType::Never, "!", cx),
         clean::RawPointer(m, ref t) => {
             let m = match m {
                 hir::Mutability::Mut => "mut",
@@ -750,26 +742,21 @@ fn fmt_type(
                         primitive_link(
                             f,
                             clean::PrimitiveType::RawPointer,
-                            &format!("*{} {:#}", m, t.print(cache, tcx)),
-                            cache,
+                            &format!("*{} {:#}", m, t.print(cx)),
+                            cx,
                         )
                     } else {
                         primitive_link(
                             f,
                             clean::PrimitiveType::RawPointer,
-                            &format!("*{} {}", m, t.print(cache, tcx)),
-                            cache,
+                            &format!("*{} {}", m, t.print(cx)),
+                            cx,
                         )
                     }
                 }
                 _ => {
-                    primitive_link(
-                        f,
-                        clean::PrimitiveType::RawPointer,
-                        &format!("*{} ", m),
-                        cache,
-                    )?;
-                    fmt::Display::fmt(&t.print(cache, tcx), f)
+                    primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?;
+                    fmt::Display::fmt(&t.print(cx), f)
                 }
             }
         }
@@ -789,15 +776,15 @@ fn fmt_type(
                                 primitive_link(
                                     f,
                                     PrimitiveType::Slice,
-                                    &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cache, tcx)),
-                                    cache,
+                                    &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cx)),
+                                    cx,
                                 )
                             } else {
                                 primitive_link(
                                     f,
                                     PrimitiveType::Slice,
-                                    &format!("{}{}{}[{}]", amp, lt, m, bt.print(cache, tcx)),
-                                    cache,
+                                    &format!("{}{}{}[{}]", amp, lt, m, bt.print(cx)),
+                                    cx,
                                 )
                             }
                         }
@@ -806,20 +793,20 @@ fn fmt_type(
                                 f,
                                 PrimitiveType::Slice,
                                 &format!("{}{}{}[", amp, lt, m),
-                                cache,
+                                cx,
                             )?;
                             if f.alternate() {
-                                write!(f, "{:#}", bt.print(cache, tcx))?;
+                                write!(f, "{:#}", bt.print(cx))?;
                             } else {
-                                write!(f, "{}", bt.print(cache, tcx))?;
+                                write!(f, "{}", bt.print(cx))?;
                             }
-                            primitive_link(f, PrimitiveType::Slice, "]", cache)
+                            primitive_link(f, PrimitiveType::Slice, "]", cx)
                         }
                     }
                 }
                 clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => {
                     write!(f, "{}{}{}(", amp, lt, m)?;
-                    fmt_type(&ty, f, use_absolute, cache, tcx)?;
+                    fmt_type(&ty, f, use_absolute, cx)?;
                     write!(f, ")")
                 }
                 clean::Generic(..) => {
@@ -827,21 +814,21 @@ fn fmt_type(
                         f,
                         PrimitiveType::Reference,
                         &format!("{}{}{}", amp, lt, m),
-                        cache,
+                        cx,
                     )?;
-                    fmt_type(&ty, f, use_absolute, cache, tcx)
+                    fmt_type(&ty, f, use_absolute, cx)
                 }
                 _ => {
                     write!(f, "{}{}{}", amp, lt, m)?;
-                    fmt_type(&ty, f, use_absolute, cache, tcx)
+                    fmt_type(&ty, f, use_absolute, cx)
                 }
             }
         }
         clean::ImplTrait(ref bounds) => {
             if f.alternate() {
-                write!(f, "impl {:#}", print_generic_bounds(bounds, cache, tcx))
+                write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
             } else {
-                write!(f, "impl {}", print_generic_bounds(bounds, cache, tcx))
+                write!(f, "impl {}", print_generic_bounds(bounds, cx))
             }
         }
         clean::QPath { ref name, ref self_type, ref trait_ } => {
@@ -853,25 +840,15 @@ fn fmt_type(
             };
             if f.alternate() {
                 if should_show_cast {
-                    write!(
-                        f,
-                        "<{:#} as {:#}>::",
-                        self_type.print(cache, tcx),
-                        trait_.print(cache, tcx)
-                    )?
+                    write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
                 } else {
-                    write!(f, "{:#}::", self_type.print(cache, tcx))?
+                    write!(f, "{:#}::", self_type.print(cx))?
                 }
             } else {
                 if should_show_cast {
-                    write!(
-                        f,
-                        "&lt;{} as {}&gt;::",
-                        self_type.print(cache, tcx),
-                        trait_.print(cache, tcx)
-                    )?
+                    write!(f, "&lt;{} as {}&gt;::", self_type.print(cx), trait_.print(cx))?
                 } else {
-                    write!(f, "{}::", self_type.print(cache, tcx))?
+                    write!(f, "{}::", self_type.print(cx))?
                 }
             };
             match *trait_ {
@@ -886,7 +863,7 @@ fn fmt_type(
                 //        everything comes in as a fully resolved QPath (hard to
                 //        look at).
                 box clean::ResolvedPath { did, ref param_names, .. } => {
-                    match href(did, cache) {
+                    match href(did, cx) {
                         Some((ref url, _, ref path)) if !f.alternate() => {
                             write!(
                                 f,
@@ -914,42 +891,40 @@ fn fmt_type(
 impl clean::Type {
     crate fn print<'b, 'a: 'b, 'tcx: 'a>(
         &'a self,
-        cache: &'b Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'b + Captures<'tcx> {
-        display_fn(move |f| fmt_type(self, f, false, cache, tcx))
+        display_fn(move |f| fmt_type(self, f, false, cx))
     }
 }
 
 impl clean::Impl {
     crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
         use_absolute: bool,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| {
             if f.alternate() {
-                write!(f, "impl{:#} ", self.generics.print(cache, tcx))?;
+                write!(f, "impl{:#} ", self.generics.print(cx))?;
             } else {
-                write!(f, "impl{} ", self.generics.print(cache, tcx))?;
+                write!(f, "impl{} ", self.generics.print(cx))?;
             }
 
             if let Some(ref ty) = self.trait_ {
                 if self.negative_polarity {
                     write!(f, "!")?;
                 }
-                fmt::Display::fmt(&ty.print(cache, tcx), f)?;
+                fmt::Display::fmt(&ty.print(cx), f)?;
                 write!(f, " for ")?;
             }
 
             if let Some(ref ty) = self.blanket_impl {
-                fmt_type(ty, f, use_absolute, cache, tcx)?;
+                fmt_type(ty, f, use_absolute, cx)?;
             } else {
-                fmt_type(&self.for_, f, use_absolute, cache, tcx)?;
+                fmt_type(&self.for_, f, use_absolute, cx)?;
             }
 
-            fmt::Display::fmt(&print_where_clause(&self.generics, cache, tcx, 0, true), f)?;
+            fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?;
             Ok(())
         })
     }
@@ -958,8 +933,7 @@ impl clean::Impl {
 impl clean::Arguments {
     crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| {
             for (i, input) in self.values.iter().enumerate() {
@@ -967,9 +941,9 @@ impl clean::Arguments {
                     write!(f, "{}: ", input.name)?;
                 }
                 if f.alternate() {
-                    write!(f, "{:#}", input.type_.print(cache, tcx))?;
+                    write!(f, "{:#}", input.type_.print(cx))?;
                 } else {
-                    write!(f, "{}", input.type_.print(cache, tcx))?;
+                    write!(f, "{}", input.type_.print(cx))?;
                 }
                 if i + 1 < self.values.len() {
                     write!(f, ", ")?;
@@ -983,13 +957,14 @@ impl clean::Arguments {
 impl clean::FnRetTy {
     crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| match self {
             clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()),
-            clean::Return(ty) if f.alternate() => write!(f, " -> {:#}", ty.print(cache, tcx)),
-            clean::Return(ty) => write!(f, " -&gt; {}", ty.print(cache, tcx)),
+            clean::Return(ty) if f.alternate() => {
+                write!(f, " -> {:#}", ty.print(cx))
+            }
+            clean::Return(ty) => write!(f, " -&gt; {}", ty.print(cx)),
             clean::DefaultReturn => Ok(()),
         })
     }
@@ -998,16 +973,11 @@ impl clean::FnRetTy {
 impl clean::BareFunctionDecl {
     fn print_hrtb_with_space<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'a Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| {
             if !self.generic_params.is_empty() {
-                write!(
-                    f,
-                    "for<{}> ",
-                    comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx)))
-                )
+                write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cx))))
             } else {
                 Ok(())
             }
@@ -1018,8 +988,7 @@ fn print_hrtb_with_space<'a, 'tcx: 'a>(
 impl clean::FnDecl {
     crate fn print<'b, 'a: 'b, 'tcx: 'a>(
         &'a self,
-        cache: &'b Cache,
-        tcx: TyCtxt<'tcx>,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'b + Captures<'tcx> {
         display_fn(move |f| {
             let ellipsis = if self.c_variadic { ", ..." } else { "" };
@@ -1027,17 +996,17 @@ impl clean::FnDecl {
                 write!(
                     f,
                     "({args:#}{ellipsis}){arrow:#}",
-                    args = self.inputs.print(cache, tcx),
+                    args = self.inputs.print(cx),
                     ellipsis = ellipsis,
-                    arrow = self.output.print(cache, tcx)
+                    arrow = self.output.print(cx)
                 )
             } else {
                 write!(
                     f,
                     "({args}{ellipsis}){arrow}",
-                    args = self.inputs.print(cache, tcx),
+                    args = self.inputs.print(cx),
                     ellipsis = ellipsis,
-                    arrow = self.output.print(cache, tcx)
+                    arrow = self.output.print(cx)
                 )
             }
         })
@@ -1049,25 +1018,23 @@ impl clean::FnDecl {
     /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
     ///   necessary.
     /// * `asyncness`: Whether the function is async or not.
-    crate fn full_print<'b, 'a: 'b, 'tcx: 'a>(
+    crate fn full_print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'b Cache,
-        tcx: TyCtxt<'tcx>,
         header_len: usize,
         indent: usize,
         asyncness: hir::IsAsync,
-    ) -> impl fmt::Display + 'b + Captures<'tcx> {
-        display_fn(move |f| self.inner_full_print(cache, tcx, header_len, indent, asyncness, f))
+        cx: &'a Context<'tcx>,
+    ) -> impl fmt::Display + 'a + Captures<'tcx> {
+        display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx))
     }
 
     fn inner_full_print(
         &self,
-        cache: &Cache,
-        tcx: TyCtxt<'_>,
         header_len: usize,
         indent: usize,
         asyncness: hir::IsAsync,
         f: &mut fmt::Formatter<'_>,
+        cx: &Context<'_>,
     ) -> fmt::Result {
         let amp = if f.alternate() { "&" } else { "&amp;" };
         let mut args = String::new();
@@ -1102,11 +1069,11 @@ fn inner_full_print(
                     }
                     clean::SelfExplicit(ref typ) => {
                         if f.alternate() {
-                            args.push_str(&format!("self: {:#}", typ.print(cache, tcx)));
+                            args.push_str(&format!("self: {:#}", typ.print(cx)));
                         } else {
-                            args.push_str(&format!("self: {}", typ.print(cache, tcx)));
+                            args.push_str(&format!("self: {}", typ.print(cx)));
                         }
-                        args_plain.push_str(&format!("self: {:#}", typ.print(cache, tcx)));
+                        args_plain.push_str(&format!("self: {:#}", typ.print(cx)));
                     }
                 }
             } else {
@@ -1120,11 +1087,11 @@ fn inner_full_print(
                 }
 
                 if f.alternate() {
-                    args.push_str(&format!("{:#}", input.type_.print(cache, tcx)));
+                    args.push_str(&format!("{:#}", input.type_.print(cx)));
                 } else {
-                    args.push_str(&input.type_.print(cache, tcx).to_string());
+                    args.push_str(&input.type_.print(cx).to_string());
                 }
-                args_plain.push_str(&format!("{:#}", input.type_.print(cache, tcx)));
+                args_plain.push_str(&format!("{:#}", input.type_.print(cx)));
             }
             if i + 1 < self.inputs.values.len() {
                 args.push(',');
@@ -1142,19 +1109,11 @@ fn inner_full_print(
         let arrow_plain;
         let arrow = if let hir::IsAsync::Async = asyncness {
             let output = self.sugared_async_return_type();
-            arrow_plain = format!("{:#}", output.print(cache, tcx));
-            if f.alternate() {
-                arrow_plain.clone()
-            } else {
-                format!("{}", output.print(cache, tcx))
-            }
+            arrow_plain = format!("{:#}", output.print(cx));
+            if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) }
         } else {
-            arrow_plain = format!("{:#}", self.output.print(cache, tcx));
-            if f.alternate() {
-                arrow_plain.clone()
-            } else {
-                format!("{}", self.output.print(cache, tcx))
-            }
+            arrow_plain = format!("{:#}", self.output.print(cx));
+            if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }
         };
 
         let declaration_len = header_len + args_plain.len() + arrow_plain.len();
@@ -1182,12 +1141,9 @@ fn inner_full_print(
 impl clean::Visibility {
     crate fn print_with_space<'a, 'tcx: 'a>(
         self,
-        tcx: TyCtxt<'tcx>,
         item_did: DefId,
-        cache: &'a Cache,
+        cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
-        use rustc_span::symbol::kw;
-
         let to_print = match self {
             clean::Public => "pub ".to_owned(),
             clean::Inherited => String::new(),
@@ -1195,7 +1151,7 @@ impl clean::Visibility {
                 // FIXME(camelid): This may not work correctly if `item_did` is a module.
                 //                 However, rustdoc currently never displays a module's
                 //                 visibility, so it shouldn't matter.
-                let parent_module = find_nearest_parent_module(tcx, item_did);
+                let parent_module = find_nearest_parent_module(cx.tcx(), item_did);
 
                 if vis_did.index == CRATE_DEF_INDEX {
                     "pub(crate) ".to_owned()
@@ -1204,26 +1160,19 @@ impl clean::Visibility {
                     // is the same as no visibility modifier
                     String::new()
                 } else if parent_module
-                    .map(|parent| find_nearest_parent_module(tcx, parent))
+                    .map(|parent| find_nearest_parent_module(cx.tcx(), parent))
                     .flatten()
                     == Some(vis_did)
                 {
                     "pub(super) ".to_owned()
                 } else {
-                    let path = tcx.def_path(vis_did);
+                    let path = cx.tcx().def_path(vis_did);
                     debug!("path={:?}", path);
-                    let first_name =
-                        path.data[0].data.get_opt_name().expect("modules are always named");
                     // modified from `resolved_path()` to work with `DefPathData`
                     let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
-                    let anchor = anchor(vis_did, &last_name.as_str(), cache).to_string();
+                    let anchor = anchor(vis_did, &last_name.as_str(), cx).to_string();
 
-                    let mut s = "pub(".to_owned();
-                    if path.data.len() != 1
-                        || (first_name != kw::SelfLower && first_name != kw::Super)
-                    {
-                        s.push_str("in ");
-                    }
+                    let mut s = "pub(in ".to_owned();
                     for seg in &path.data[..path.data.len() - 1] {
                         s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap()));
                     }
@@ -1234,6 +1183,43 @@ impl clean::Visibility {
         };
         display_fn(move |f| f.write_str(&to_print))
     }
+
+    /// This function is the same as print_with_space, except that it renders no links.
+    /// It's used for macros' rendered source view, which is syntax highlighted and cannot have
+    /// any HTML in it.
+    crate fn to_src_with_space<'a, 'tcx: 'a>(
+        self,
+        tcx: TyCtxt<'tcx>,
+        item_did: DefId,
+    ) -> impl fmt::Display + 'a + Captures<'tcx> {
+        let to_print = match self {
+            clean::Public => "pub ".to_owned(),
+            clean::Inherited => String::new(),
+            clean::Visibility::Restricted(vis_did) => {
+                // FIXME(camelid): This may not work correctly if `item_did` is a module.
+                //                 However, rustdoc currently never displays a module's
+                //                 visibility, so it shouldn't matter.
+                let parent_module = find_nearest_parent_module(tcx, item_did);
+
+                if vis_did.index == CRATE_DEF_INDEX {
+                    "pub(crate) ".to_owned()
+                } else if parent_module == Some(vis_did) {
+                    // `pub(in foo)` where `foo` is the parent module
+                    // is the same as no visibility modifier
+                    String::new()
+                } else if parent_module
+                    .map(|parent| find_nearest_parent_module(tcx, parent))
+                    .flatten()
+                    == Some(vis_did)
+                {
+                    "pub(super) ".to_owned()
+                } else {
+                    format!("pub(in {}) ", tcx.def_path_str(vis_did))
+                }
+            }
+        };
+        display_fn(move |f| f.write_str(&to_print))
+    }
 }
 
 crate trait PrintWithSpace {
@@ -1277,24 +1263,23 @@ fn print_with_space(&self) -> &str {
 }
 
 impl clean::Import {
-    crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+    crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'b Cache,
-        tcx: TyCtxt<'tcx>,
-    ) -> impl fmt::Display + 'b + Captures<'tcx> {
+        cx: &'a Context<'tcx>,
+    ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| match self.kind {
             clean::ImportKind::Simple(name) => {
                 if name == self.source.path.last() {
-                    write!(f, "use {};", self.source.print(cache, tcx))
+                    write!(f, "use {};", self.source.print(cx))
                 } else {
-                    write!(f, "use {} as {};", self.source.print(cache, tcx), name)
+                    write!(f, "use {} as {};", self.source.print(cx), name)
                 }
             }
             clean::ImportKind::Glob => {
                 if self.source.path.segments.is_empty() {
                     write!(f, "use *;")
                 } else {
-                    write!(f, "use {}::*;", self.source.print(cache, tcx))
+                    write!(f, "use {}::*;", self.source.print(cx))
                 }
             }
         })
@@ -1302,20 +1287,19 @@ impl clean::Import {
 }
 
 impl clean::ImportSource {
-    crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+    crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'b Cache,
-        tcx: TyCtxt<'tcx>,
-    ) -> impl fmt::Display + 'b + Captures<'tcx> {
+        cx: &'a Context<'tcx>,
+    ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| match self.did {
-            Some(did) => resolved_path(f, did, &self.path, true, false, cache, tcx),
+            Some(did) => resolved_path(f, did, &self.path, true, false, cx),
             _ => {
                 for seg in &self.path.segments[..self.path.segments.len() - 1] {
                     write!(f, "{}::", seg.name)?;
                 }
                 let name = self.path.last_name();
                 if let hir::def::Res::PrimTy(p) = self.path.res {
-                    primitive_link(f, PrimitiveType::from(p), &*name, cache)?;
+                    primitive_link(f, PrimitiveType::from(p), &*name, cx)?;
                 } else {
                     write!(f, "{}", name)?;
                 }
@@ -1326,27 +1310,26 @@ impl clean::ImportSource {
 }
 
 impl clean::TypeBinding {
-    crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+    crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'b Cache,
-        tcx: TyCtxt<'tcx>,
-    ) -> impl fmt::Display + 'b + Captures<'tcx> {
+        cx: &'a Context<'tcx>,
+    ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| {
             f.write_str(&*self.name.as_str())?;
             match self.kind {
                 clean::TypeBindingKind::Equality { ref ty } => {
                     if f.alternate() {
-                        write!(f, " = {:#}", ty.print(cache, tcx))?;
+                        write!(f, " = {:#}", ty.print(cx))?;
                     } else {
-                        write!(f, " = {}", ty.print(cache, tcx))?;
+                        write!(f, " = {}", ty.print(cx))?;
                     }
                 }
                 clean::TypeBindingKind::Constraint { ref bounds } => {
                     if !bounds.is_empty() {
                         if f.alternate() {
-                            write!(f, ": {:#}", print_generic_bounds(bounds, cache, tcx))?;
+                            write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
                         } else {
-                            write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cache, tcx))?;
+                            write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
                         }
                     }
                 }
@@ -1371,15 +1354,14 @@ impl clean::TypeBinding {
 }
 
 impl clean::GenericArg {
-    crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+    crate fn print<'a, 'tcx: 'a>(
         &'a self,
-        cache: &'b Cache,
-        tcx: TyCtxt<'tcx>,
-    ) -> impl fmt::Display + 'b + Captures<'tcx> {
+        cx: &'a Context<'tcx>,
+    ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| match self {
             clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(&lt.print(), f),
-            clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cache, tcx), f),
-            clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(tcx), f),
+            clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
+            clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
         })
     }
 }
index 68d70f27c8c7888acb6360295c73cc375db10cfc..dc29add9333141ac7b1b7bac92b20a0f4f05dcfd 100644 (file)
@@ -68,7 +68,7 @@
     </style>\
 </head>\
 <body class=\"rustdoc {css_class}\">\
-    <!--[if lte IE 8]>\
+    <!--[if lte IE 11]>\
     <div class=\"warning\">\
         This old browser is unsupported and will most likely display funky \
         things.\
     </nav>\
     <section id=\"main\" class=\"content\">{content}</section>\
     <section id=\"search\" class=\"content hidden\"></section>\
-    <section class=\"footer\"></section>\
     {after_content}\
     <div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\" \
-       data-search-js=\"{root_path}search-index{suffix}.js\"></div>
+       data-search-index-js=\"{root_path}search-index{suffix}.js\" \
+       data-search-js=\"{static_root_path}search{suffix}.js\"></div>
     <script src=\"{static_root_path}main{suffix}.js\"></script>\
     {extra_scripts}\
 </body>\
index 4318be898ceb49c60ca0211e20811e080e411b9f..60ebdf5690d0d2aa9fffa476f38826a454767545 100644 (file)
@@ -8,3 +8,6 @@
 crate mod sources;
 crate mod static_files;
 crate mod toc;
+
+#[cfg(test)]
+mod tests;
index 2265905dcbaf41d5a0e7d212380af1948a8b2728..f4296a04e59216c8683579f66bb6b79881377c10 100644 (file)
@@ -1,15 +1,16 @@
 use std::collections::BTreeMap;
 use std::path::Path;
 
+use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::{sym, Symbol};
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 
+use crate::clean;
 use crate::clean::types::{
-    FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, TypeKind, WherePredicate,
+    AttributesExt, FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, WherePredicate,
 };
-use crate::clean::{self, AttributesExt};
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::markdown::short_markdown_summary;
 crate fn extern_location(
     e: &clean::ExternalCrate,
     extern_url: Option<&str>,
+    ast_attrs: &[ast::Attribute],
     dst: &Path,
+    tcx: TyCtxt<'_>,
 ) -> ExternalLocation {
     use ExternalLocation::*;
     // See if there's documentation generated into the local directory
-    let local_location = dst.join(&*e.name.as_str());
+    let local_location = dst.join(&*e.name(tcx).as_str());
     if local_location.is_dir() {
         return Local;
     }
@@ -49,7 +52,7 @@
 
     // Failing that, see if there's an attribute specifying where to find this
     // external crate
-    e.attrs
+    ast_attrs
         .lists(sym::doc)
         .filter(|a| a.has_name(sym::html_root_url))
         .filter_map(|a| a.value_str())
@@ -315,15 +318,15 @@ fn get_generics(clean_type: &clean::Type, cache: &Cache) -> Option<Vec<Generic>>
     arg: &Type,
     tcx: TyCtxt<'tcx>,
     recurse: i32,
-    res: &mut FxHashSet<(Type, TypeKind)>,
+    res: &mut FxHashSet<(Type, ItemType)>,
 ) -> usize {
-    fn insert(res: &mut FxHashSet<(Type, TypeKind)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
+    fn insert(res: &mut FxHashSet<(Type, ItemType)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
         if let Some(kind) = ty.def_id().map(|did| tcx.def_kind(did).into()) {
             res.insert((ty, kind));
             1
         } else if ty.is_primitive() {
             // This is a primitive, let's store it as such.
-            res.insert((ty, TypeKind::Primitive));
+            res.insert((ty, ItemType::Primitive));
             1
         } else {
             0
@@ -393,7 +396,7 @@ fn insert(res: &mut FxHashSet<(Type, TypeKind)>, tcx: TyCtxt<'_>, ty: Type) -> u
     generics: &Generics,
     decl: &FnDecl,
     tcx: TyCtxt<'tcx>,
-) -> (Vec<(Type, TypeKind)>, Vec<(Type, TypeKind)>) {
+) -> (Vec<(Type, ItemType)>, Vec<(Type, ItemType)>) {
     let mut all_types = FxHashSet::default();
     for arg in decl.inputs.values.iter() {
         if arg.type_.is_self_type() {
index df5ff6e106d7c966567d23a051dbe5394b693a59..4c8ba0e7b496e02fdd46d3ae6e9c5711150c343a 100644 (file)
 use rustc_session::Session;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::FileName;
-use rustc_span::{symbol::sym, Symbol};
+use rustc_span::symbol::sym;
 
 use super::cache::{build_index, ExternalLocation};
 use super::print_item::{full_path, item_path, print_item};
 use super::write_shared::write_shared;
-use super::{print_sidebar, settings, AllTypes, NameDoc, StylePath, BASIC_KEYWORDS, CURRENT_DEPTH};
+use super::{print_sidebar, settings, AllTypes, NameDoc, StylePath, BASIC_KEYWORDS};
 
-use crate::clean::{self, AttributesExt};
+use crate::clean;
 use crate::config::RenderOptions;
 use crate::docfs::{DocFS, PathError};
 use crate::error::Error;
@@ -40,7 +40,7 @@
 crate struct Context<'tcx> {
     /// Current hierarchy of components leading down to what's currently being
     /// rendered
-    pub(super) current: Vec<String>,
+    pub(crate) current: Vec<String>,
     /// The current destination folder of where HTML artifacts should be placed.
     /// This changes as the context descends into the module hierarchy.
     pub(super) dst: PathBuf,
     crate static_root_path: Option<String>,
     /// The fs handle we are working with.
     crate fs: DocFS,
-    /// The default edition used to parse doctests.
-    crate edition: Edition,
     pub(super) codes: ErrorCodes,
     pub(super) playground: Option<markdown::Playground>,
     all: RefCell<AllTypes>,
@@ -141,13 +139,21 @@ impl SharedContext<'_> {
     crate fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option<String> {
         if self.collapsed { item.collapsed_doc_value() } else { item.doc_value() }
     }
+
+    crate fn edition(&self) -> Edition {
+        self.tcx.sess.edition()
+    }
 }
 
 impl<'tcx> Context<'tcx> {
-    pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
+    pub(crate) fn tcx(&self) -> TyCtxt<'tcx> {
         self.shared.tcx
     }
 
+    pub(crate) fn cache(&self) -> &Cache {
+        &self.cache
+    }
+
     fn sess(&self) -> &'tcx Session {
         &self.shared.tcx.sess
     }
@@ -163,25 +169,18 @@ pub(super) fn root_path(&self) -> String {
         "../".repeat(self.current.len())
     }
 
-    fn render_item(&self, it: &clean::Item, pushname: bool) -> String {
-        // A little unfortunate that this is done like this, but it sure
-        // does make formatting *a lot* nicer.
-        CURRENT_DEPTH.with(|slot| {
-            slot.set(self.current.len());
-        });
-
-        let mut title = if it.is_primitive() || it.is_keyword() {
-            // No need to include the namespace for primitive types and keywords
-            String::new()
-        } else {
-            self.current.join("::")
-        };
-        if pushname {
-            if !title.is_empty() {
-                title.push_str("::");
-            }
+    fn render_item(&self, it: &clean::Item, is_module: bool) -> String {
+        let mut title = String::new();
+        if !is_module {
             title.push_str(&it.name.unwrap().as_str());
         }
+        if !it.is_primitive() && !it.is_keyword() {
+            if !is_module {
+                title.push_str(" in ");
+            }
+            // No need to include the namespace for primitive types and keywords
+            title.push_str(&self.current.join("::"));
+        };
         title.push_str(" - Rust");
         let tyname = it.type_();
         let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(&doc));
@@ -282,15 +281,15 @@ fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc
     /// may happen, for example, with externally inlined items where the source
     /// of their crate documentation isn't known.
     pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
-        if item.span.is_dummy() {
+        if item.span(self.tcx()).is_dummy() {
             return None;
         }
         let mut root = self.root_path();
         let mut path = String::new();
-        let cnum = item.span.cnum(self.sess());
+        let cnum = item.span(self.tcx()).cnum(self.sess());
 
         // We can safely ignore synthetic `SourceFile`s.
-        let file = match item.span.filename(self.sess()) {
+        let file = match item.span(self.tcx()).filename(self.sess()) {
             FileName::Real(ref path) => path.local_path().to_path_buf(),
             _ => return None,
         };
@@ -324,8 +323,8 @@ pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
             (&*symbol, &path)
         };
 
-        let loline = item.span.lo(self.sess()).line;
-        let hiline = item.span.hi(self.sess()).line;
+        let loline = item.span(self.tcx()).lo(self.sess()).line;
+        let hiline = item.span(self.tcx()).hi(self.sess()).line;
         let lines =
             if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) };
         Some(format!(
@@ -349,7 +348,6 @@ fn descr() -> &'static str {
     fn init(
         mut krate: clean::Crate,
         options: RenderOptions,
-        edition: Edition,
         mut cache: Cache,
         tcx: TyCtxt<'tcx>,
     ) -> Result<(Self, clean::Crate), Error> {
@@ -438,7 +436,6 @@ fn init(
             resource_suffix,
             static_root_path,
             fs: DocFS::new(sender),
-            edition,
             codes: ErrorCodes::from(unstable_features.is_nightly_build()),
             playground,
             all: RefCell::new(AllTypes::new()),
@@ -478,8 +475,6 @@ fn init(
             cache: Rc::new(cache),
         };
 
-        CURRENT_DEPTH.with(|s| s.set(0));
-
         // Write shared runs within a flock; disable thread dispatching of IO temporarily.
         Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
         write_shared(&cx, &krate, index, &md_opts)?;
@@ -499,11 +494,8 @@ fn make_child_renderer(&self) -> Self {
         }
     }
 
-    fn after_krate(
-        &mut self,
-        crate_name: Symbol,
-        diag: &rustc_errors::Handler,
-    ) -> Result<(), Error> {
+    fn after_krate(&mut self) -> Result<(), Error> {
+        let crate_name = self.tcx().crate_name(LOCAL_CRATE);
         let final_file = self.dst.join(&*crate_name.as_str()).join("all.html");
         let settings_file = self.dst.join("settings.html");
 
@@ -577,7 +569,8 @@ fn after_krate(
 
         // Flush pending errors.
         Rc::get_mut(&mut self.shared).unwrap().fs.close();
-        let nb_errors = self.shared.errors.iter().map(|err| diag.struct_err(&err).emit()).count();
+        let nb_errors =
+            self.shared.errors.iter().map(|err| self.tcx().sess.struct_err(&err).emit()).count();
         if nb_errors > 0 {
             Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
         } else {
@@ -585,7 +578,7 @@ fn after_krate(
         }
     }
 
-    fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error> {
+    fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error> {
         // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
         // if they contain impls for public types. These modules can also
         // contain items such as publicly re-exported structures.
@@ -597,12 +590,13 @@ fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Err
             self.render_redirect_pages = item.is_stripped();
         }
         let scx = &self.shared;
-        self.dst.push(item_name);
-        self.current.push(item_name.to_owned());
+        let item_name = item.name.as_ref().unwrap().to_string();
+        self.dst.push(&item_name);
+        self.current.push(item_name);
 
         info!("Recursing into {}", self.dst.display());
 
-        let buf = self.render_item(item, false);
+        let buf = self.render_item(item, true);
         // buf will be empty if the module is stripped and there is no redirect for it
         if !buf.is_empty() {
             self.shared.ensure_dir(&self.dst)?;
@@ -624,7 +618,7 @@ fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Err
         Ok(())
     }
 
-    fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> {
+    fn mod_item_out(&mut self) -> Result<(), Error> {
         info!("Recursed; leaving {}", self.dst.display());
 
         // Go back to where we were at
@@ -645,7 +639,7 @@ fn item(&mut self, item: clean::Item) -> Result<(), Error> {
             self.render_redirect_pages = item.is_stripped();
         }
 
-        let buf = self.render_item(&item, true);
+        let buf = self.render_item(&item, false);
         // buf will be empty if the item is stripped and there is no redirect for it
         if !buf.is_empty() {
             let name = item.name.as_ref().unwrap();
index efd453f96b8e7a6c20dec24119b0d0c13607be2f..518dbc6eeb3b9406e357cda5a2cf5dc90a1440c1 100644 (file)
@@ -35,7 +35,6 @@
 crate use context::*;
 crate use write_shared::FILES_UNVERSIONED;
 
-use std::cell::Cell;
 use std::collections::VecDeque;
 use std::default::Default;
 use std::fmt;
@@ -43,7 +42,6 @@
 use std::str;
 use std::string::ToString;
 
-use itertools::Itertools;
 use rustc_ast_pretty::pprust;
 use rustc_attr::{Deprecation, StabilityLevel};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::DefId;
 use rustc_hir::Mutability;
 use rustc_middle::middle::stability;
-use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::{kw, sym, Symbol};
 use serde::ser::SerializeSeq;
 use serde::{Serialize, Serializer};
 
-use crate::clean::{self, GetDefId, RenderedLink, SelfTy, TypeKind};
+use crate::clean::{self, GetDefId, RenderedLink, SelfTy};
 use crate::docfs::PathError;
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
-use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode};
+use crate::formats::{AssocItemRender, Impl, RenderMode};
 use crate::html::escape::Escape;
 use crate::html::format::{
     href, print_abi_with_space, print_default_space, print_generic_bounds, print_where_clause,
@@ -185,11 +182,11 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 #[derive(Debug)]
 crate struct TypeWithKind {
     ty: RenderType,
-    kind: TypeKind,
+    kind: ItemType,
 }
 
-impl From<(RenderType, TypeKind)> for TypeWithKind {
-    fn from(x: (RenderType, TypeKind)) -> TypeWithKind {
+impl From<(RenderType, ItemType)> for TypeWithKind {
+    fn from(x: (RenderType, ItemType)) -> TypeWithKind {
         TypeWithKind { ty: x.0, kind: x.1 }
     }
 }
@@ -199,7 +196,7 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
         S: Serializer,
     {
-        (&self.ty.name, ItemType::from(self.kind)).serialize(serializer)
+        (&self.ty.name, self.kind).serialize(serializer)
     }
 }
 
@@ -211,8 +208,6 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     crate disabled: bool,
 }
 
-thread_local!(crate static CURRENT_DEPTH: Cell<usize> = Cell::new(0));
-
 fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
     if let Some(l) = cx.src_href(item) {
         write!(buf, "<a class=\"srclink\" href=\"{}\" title=\"goto source code\">[src]</a>", l)
@@ -486,18 +481,7 @@ fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<Strin
             ],
         )
             .into(),
-        (
-            "Auto-hide item declarations",
-            vec![
-                ("auto-hide-struct", "Auto-hide structs declaration", true),
-                ("auto-hide-enum", "Auto-hide enums declaration", false),
-                ("auto-hide-union", "Auto-hide unions declaration", true),
-                ("auto-hide-trait", "Auto-hide traits declaration", true),
-                ("auto-hide-macro", "Auto-hide macros declaration", false),
-            ],
-        )
-            .into(),
-        ("auto-hide-attributes", "Auto-hide item attributes.", true).into(),
+        ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(),
         ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(),
         ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true)
             .into(),
@@ -525,7 +509,7 @@ fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option
         info!("Documenting {}", name);
     }
     document_item_info(w, cx, item, false, parent);
-    document_full(w, item, cx, "", false);
+    document_full(w, item, cx, false);
 }
 
 /// Render md_text as markdown.
@@ -534,21 +518,19 @@ fn render_markdown(
     cx: &Context<'_>,
     md_text: &str,
     links: Vec<RenderedLink>,
-    prefix: &str,
     is_hidden: bool,
 ) {
     let mut ids = cx.id_map.borrow_mut();
     write!(
         w,
-        "<div class=\"docblock{}\">{}{}</div>",
+        "<div class=\"docblock{}\">{}</div>",
         if is_hidden { " hidden" } else { "" },
-        prefix,
         Markdown(
             md_text,
             &links,
             &mut ids,
             cx.shared.codes,
-            cx.shared.edition,
+            cx.shared.edition(),
             &cx.shared.playground
         )
         .into_string()
@@ -562,21 +544,19 @@ fn document_short(
     item: &clean::Item,
     cx: &Context<'_>,
     link: AssocItemLink<'_>,
-    prefix: &str,
     is_hidden: bool,
-    parent: Option<&clean::Item>,
+    parent: &clean::Item,
     show_def_docs: bool,
 ) {
-    document_item_info(w, cx, item, is_hidden, parent);
+    document_item_info(w, cx, item, is_hidden, Some(parent));
     if !show_def_docs {
         return;
     }
     if let Some(s) = item.doc_value() {
-        let mut summary_html = MarkdownSummaryLine(&s, &item.links(&cx.cache)).into_string();
+        let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string();
 
         if s.contains('\n') {
-            let link =
-                format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link, cx.cache()));
+            let link = format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link, cx));
 
             if let Some(idx) = summary_html.rfind("</p>") {
                 summary_html.insert_str(idx, &link);
@@ -587,39 +567,17 @@ fn document_short(
 
         write!(
             w,
-            "<div class='docblock{}'>{}{}</div>",
+            "<div class='docblock{}'>{}</div>",
             if is_hidden { " hidden" } else { "" },
-            prefix,
             summary_html,
         );
-    } else if !prefix.is_empty() {
-        write!(
-            w,
-            "<div class=\"docblock{}\">{}</div>",
-            if is_hidden { " hidden" } else { "" },
-            prefix
-        );
     }
 }
 
-fn document_full(
-    w: &mut Buffer,
-    item: &clean::Item,
-    cx: &Context<'_>,
-    prefix: &str,
-    is_hidden: bool,
-) {
+fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>, is_hidden: bool) {
     if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) {
         debug!("Doc block: =====\n{}\n=====", s);
-        render_markdown(w, cx, &*s, item.links(&cx.cache), prefix, is_hidden);
-    } else if !prefix.is_empty() {
-        if is_hidden {
-            w.write_str("<div class=\"docblock hidden\">");
-        } else {
-            w.write_str("<div class=\"docblock\">");
-        }
-        w.write_str(prefix);
-        w.write_str("</div>");
+        render_markdown(w, cx, &s, item.links(cx), is_hidden);
     }
 }
 
@@ -650,17 +608,12 @@ fn document_item_info(
 }
 
 fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
-    let cfg = match (&item.attrs.cfg, parent.and_then(|p| p.attrs.cfg.as_ref())) {
+    let cfg = match (&item.cfg, parent.and_then(|p| p.cfg.as_ref())) {
         (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
         (cfg, _) => cfg.as_deref().cloned(),
     };
 
-    debug!(
-        "Portability {:?} - {:?} = {:?}",
-        item.attrs.cfg,
-        parent.and_then(|p| p.attrs.cfg.as_ref()),
-        cfg
-    );
+    debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg);
 
     Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html()))
 }
@@ -702,7 +655,7 @@ fn short_item_info(
                 &note,
                 &mut ids,
                 error_codes,
-                cx.shared.edition,
+                cx.shared.edition(),
                 &cx.shared.playground,
             );
             message.push_str(&format!(": {}", html.into_string()));
@@ -744,7 +697,7 @@ fn short_item_info(
                     &unstable_reason.as_str(),
                     &mut ids,
                     error_codes,
-                    cx.shared.edition,
+                    cx.shared.edition(),
                     &cx.shared.playground,
                 )
                 .into_string()
@@ -797,7 +750,7 @@ fn render_impls(
     w.write_str(&impls.join(""));
 }
 
-fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) -> String {
+fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
     use crate::formats::item_type::ItemType::*;
 
     let name = it.name.as_ref().unwrap();
@@ -811,7 +764,7 @@ fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) ->
         AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
         AssocItemLink::Anchor(None) => anchor,
         AssocItemLink::GotoSource(did, _) => {
-            href(did, cache).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
+            href(did, cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
         }
     }
 }
@@ -825,16 +778,14 @@ fn assoc_const(
     extra: &str,
     cx: &Context<'_>,
 ) {
-    let cache = cx.cache();
-    let tcx = cx.tcx();
     write!(
         w,
         "{}{}const <a href=\"{}\" class=\"constant\"><b>{}</b></a>: {}",
         extra,
-        it.visibility.print_with_space(tcx, it.def_id, cache),
-        naive_assoc_href(it, link, cache),
+        it.visibility.print_with_space(it.def_id, cx),
+        naive_assoc_href(it, link, cx),
         it.name.as_ref().unwrap(),
-        ty.print(cache, tcx)
+        ty.print(cx)
     );
 }
 
@@ -845,21 +796,20 @@ fn assoc_type(
     default: Option<&clean::Type>,
     link: AssocItemLink<'_>,
     extra: &str,
-    cache: &Cache,
-    tcx: TyCtxt<'_>,
+    cx: &Context<'_>,
 ) {
     write!(
         w,
         "{}type <a href=\"{}\" class=\"type\">{}</a>",
         extra,
-        naive_assoc_href(it, link, cache),
+        naive_assoc_href(it, link, cx),
         it.name.as_ref().unwrap()
     );
     if !bounds.is_empty() {
-        write!(w, ": {}", print_generic_bounds(bounds, cache, tcx))
+        write!(w, ": {}", print_generic_bounds(bounds, cx))
     }
     if let Some(default) = default {
-        write!(w, " = {}", default.print(cache, tcx))
+        write!(w, " = {}", default.print(cx))
     }
 }
 
@@ -909,8 +859,6 @@ fn method(
         parent: ItemType,
         cx: &Context<'_>,
     ) {
-        let cache = cx.cache();
-        let tcx = cx.tcx();
         let name = meth.name.as_ref().unwrap();
         let href = match link {
             AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
@@ -924,19 +872,19 @@ fn method(
                     ItemType::TyMethod
                 };
 
-                href(did, cache)
+                href(did, cx)
                     .map(|p| format!("{}#{}.{}", p.0, ty, name))
                     .unwrap_or_else(|| format!("#{}.{}", ty, name))
             }
         };
-        let vis = meth.visibility.print_with_space(tcx, meth.def_id, cache).to_string();
+        let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string();
         let constness = header.constness.print_with_space();
         let asyncness = header.asyncness.print_with_space();
         let unsafety = header.unsafety.print_with_space();
         let defaultness = print_default_space(meth.is_default());
         let abi = print_abi_with_space(header.abi).to_string();
         // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
-        let generics_len = format!("{:#}", g.print(cache, tcx)).len();
+        let generics_len = format!("{:#}", g.print(cx)).len();
         let mut header_len = "fn ".len()
             + vis.len()
             + constness.len()
@@ -947,19 +895,21 @@ fn method(
             + name.as_str().len()
             + generics_len;
 
-        let (indent, end_newline) = if parent == ItemType::Trait {
+        let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
             header_len += 4;
-            (4, false)
+            let indent_str = "    ";
+            render_attributes_in_pre(w, meth, indent_str);
+            (4, indent_str, false)
         } else {
-            (0, true)
+            render_attributes_in_code(w, meth);
+            (0, "", true)
         };
-        render_attributes(w, meth, false);
         w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
         write!(
             w,
             "{}{}{}{}{}{}{}fn <a href=\"{href}\" class=\"fnname\">{name}</a>\
              {generics}{decl}{notable_traits}{where_clause}",
-            if parent == ItemType::Trait { "    " } else { "" },
+            indent_str,
             vis,
             constness,
             asyncness,
@@ -968,10 +918,10 @@ fn method(
             abi,
             href = href,
             name = name,
-            generics = g.print(cache, tcx),
-            decl = d.full_print(cache, tcx, header_len, indent, header.asyncness),
-            notable_traits = notable_traits_decl(&d, cache, tcx),
-            where_clause = print_where_clause(g, cache, tcx, indent, end_newline),
+            generics = g.print(cx),
+            decl = d.full_print(header_len, indent, header.asyncness, cx),
+            notable_traits = notable_traits_decl(&d, cx),
+            where_clause = print_where_clause(g, cx, indent, end_newline),
         )
     }
     match *item.kind {
@@ -998,8 +948,7 @@ fn method(
             default.as_ref(),
             link,
             if parent == ItemType::Trait { "    " } else { "" },
-            cx.cache(),
-            cx.tcx(),
+            cx,
         ),
         _ => panic!("render_assoc_item called on non-associated-item"),
     }
@@ -1007,7 +956,6 @@ fn method(
 
 const ALLOWED_ATTRIBUTES: &[Symbol] = &[
     sym::export_name,
-    sym::lang,
     sym::link_section,
     sym::must_use,
     sym::no_mangle,
@@ -1015,35 +963,33 @@ fn method(
     sym::non_exhaustive,
 ];
 
-// The `top` parameter is used when generating the item declaration to ensure it doesn't have a
-// left padding. For example:
-//
-// #[foo] <----- "top" attribute
-// struct Foo {
-//     #[bar] <---- not "top" attribute
-//     bar: usize,
-// }
-fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) {
-    let attrs = it
-        .attrs
+fn attributes(it: &clean::Item) -> Vec<String> {
+    it.attrs
         .other_attrs
         .iter()
         .filter_map(|attr| {
             if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) {
-                Some(pprust::attribute_to_string(&attr))
+                Some(pprust::attribute_to_string(&attr).replace("\n", "").replace("  ", " "))
             } else {
                 None
             }
         })
-        .join("\n");
+        .collect()
+}
 
-    if !attrs.is_empty() {
-        write!(
-            w,
-            "<span class=\"docblock attributes{}\">{}</span>",
-            if top { " top-attr" } else { "" },
-            &attrs
-        );
+// When an attribute is rendered inside a `<pre>` tag, it is formatted using
+// a whitespace prefix and newline.
+fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) {
+    for a in attributes(it) {
+        write!(w, "{}{}\n", prefix, a);
+    }
+}
+
+// When an attribute is rendered inside a <code> tag, it is formatted using
+// a div to produce a newline after it.
+fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item) {
+    for a in attributes(it) {
+        write!(w, "<div class=\"code-attribute\">{}</div>", a);
     }
 }
 
@@ -1088,11 +1034,9 @@ fn render_assoc_items(
                 RenderMode::Normal
             }
             AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
-                let id = cx.derive_id(small_url_encode(format!(
-                    "deref-methods-{:#}",
-                    type_.print(cache, tcx)
-                )));
-                debug!("Adding {} to deref id map", type_.print(cache, tcx));
+                let id =
+                    cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
+                debug!("Adding {} to deref id map", type_.print(cx));
                 cx.deref_id_map.borrow_mut().insert(type_.def_id_full(cache).unwrap(), id.clone());
                 write!(
                     w,
@@ -1101,8 +1045,8 @@ fn render_assoc_items(
                          <a href=\"#{id}\" class=\"anchor\"></a>\
                      </h2>",
                     id = id,
-                    trait_ = trait_.print(cache, tcx),
-                    type_ = type_.print(cache, tcx),
+                    trait_ = trait_.print(cx),
+                    type_ = type_.print(cx),
                 );
                 RenderMode::ForDeref { mut_: deref_mut_ }
             }
@@ -1254,36 +1198,34 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, cache: &Cache) -> bo
     }
 }
 
-fn notable_traits_decl(decl: &clean::FnDecl, cache: &Cache, tcx: TyCtxt<'_>) -> String {
+fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
     let mut out = Buffer::html();
     let mut trait_ = String::new();
 
-    if let Some(did) = decl.output.def_id_full(cache) {
-        if let Some(impls) = cache.impls.get(&did) {
+    if let Some(did) = decl.output.def_id_full(cx.cache()) {
+        if let Some(impls) = cx.cache().impls.get(&did) {
             for i in impls {
                 let impl_ = i.inner_impl();
-                if impl_
-                    .trait_
-                    .def_id()
-                    .map_or(false, |d| cache.traits.get(&d).map(|t| t.is_notable).unwrap_or(false))
-                {
+                if impl_.trait_.def_id().map_or(false, |d| {
+                    cx.cache().traits.get(&d).map(|t| t.is_notable).unwrap_or(false)
+                }) {
                     if out.is_empty() {
                         write!(
                             &mut out,
                             "<h3 class=\"notable\">Notable traits for {}</h3>\
                              <code class=\"content\">",
-                            impl_.for_.print(cache, tcx)
+                            impl_.for_.print(cx)
                         );
-                        trait_.push_str(&impl_.for_.print(cache, tcx).to_string());
+                        trait_.push_str(&impl_.for_.print(cx).to_string());
                     }
 
                     //use the "where" class here to make it small
                     write!(
                         &mut out,
                         "<span class=\"where fmt-newline\">{}</span>",
-                        impl_.print(cache, false, tcx)
+                        impl_.print(false, cx)
                     );
-                    let t_did = impl_.trait_.def_id_full(cache).unwrap();
+                    let t_did = impl_.trait_.def_id_full(cx.cache()).unwrap();
                     for it in &impl_.items {
                         if let clean::TypedefItem(ref tydef, _) = *it.kind {
                             out.push_str("<span class=\"where fmt-newline\">    ");
@@ -1294,8 +1236,7 @@ fn notable_traits_decl(decl: &clean::FnDecl, cache: &Cache, tcx: TyCtxt<'_>) ->
                                 Some(&tydef.type_),
                                 AssocItemLink::GotoSource(t_did, &FxHashSet::default()),
                                 "",
-                                cache,
-                                tcx,
+                                cx,
                             );
                             out.push_str(";</span>");
                         }
@@ -1334,92 +1275,11 @@ fn render_impl(
     // in documentation pages for trait with automatic implementations like "Send" and "Sync".
     aliases: &[String],
 ) {
-    let traits = &cx.cache.traits;
     let tcx = cx.tcx();
     let cache = cx.cache();
+    let traits = &cache.traits;
     let trait_ = i.trait_did_full(cache).map(|did| &traits[&did]);
-
-    if render_mode == RenderMode::Normal {
-        let id = cx.derive_id(match i.inner_impl().trait_ {
-            Some(ref t) => {
-                if is_on_foreign_type {
-                    get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cache, tcx)
-                } else {
-                    format!("impl-{}", small_url_encode(format!("{:#}", t.print(cache, tcx))))
-                }
-            }
-            None => "impl".to_string(),
-        });
-        let aliases = if aliases.is_empty() {
-            String::new()
-        } else {
-            format!(" aliases=\"{}\"", aliases.join(","))
-        };
-        if let Some(use_absolute) = use_absolute {
-            write!(w, "<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">", id, aliases);
-            write!(w, "{}", i.inner_impl().print(cache, use_absolute, tcx));
-            if show_def_docs {
-                for it in &i.inner_impl().items {
-                    if let clean::TypedefItem(ref tydef, _) = *it.kind {
-                        w.write_str("<span class=\"where fmt-newline\">  ");
-                        assoc_type(
-                            w,
-                            it,
-                            &[],
-                            Some(&tydef.type_),
-                            AssocItemLink::Anchor(None),
-                            "",
-                            cache,
-                            tcx,
-                        );
-                        w.write_str(";</span>");
-                    }
-                }
-            }
-            w.write_str("</code>");
-        } else {
-            write!(
-                w,
-                "<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>",
-                id,
-                aliases,
-                i.inner_impl().print(cache, false, tcx)
-            );
-        }
-        write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
-        render_stability_since_raw(
-            w,
-            i.impl_item.stable_since(tcx).as_deref(),
-            i.impl_item.const_stable_since(tcx).as_deref(),
-            outer_version,
-            outer_const_version,
-        );
-        write_srclink(cx, &i.impl_item, w);
-        w.write_str("</h3>");
-
-        if trait_.is_some() {
-            if let Some(portability) = portability(&i.impl_item, Some(parent)) {
-                write!(w, "<div class=\"item-info\">{}</div>", portability);
-            }
-        }
-
-        if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
-            let mut ids = cx.id_map.borrow_mut();
-            write!(
-                w,
-                "<div class=\"docblock\">{}</div>",
-                Markdown(
-                    &*dox,
-                    &i.impl_item.links(&cx.cache),
-                    &mut ids,
-                    cx.shared.codes,
-                    cx.shared.edition,
-                    &cx.shared.playground
-                )
-                .into_string()
-            );
-        }
-    }
+    let mut close_tags = String::new();
 
     fn doc_impl_item(
         w: &mut Buffer,
@@ -1507,8 +1367,7 @@ fn doc_impl_item(
                     Some(&tydef.type_),
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
                     "",
-                    cx.cache(),
-                    tcx,
+                    cx,
                 );
                 w.write_str("</code>");
                 write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
@@ -1558,8 +1417,7 @@ fn doc_impl_item(
                     default.as_ref(),
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
                     "",
-                    cx.cache(),
-                    tcx,
+                    cx,
                 );
                 w.write_str("</code>");
                 write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
@@ -1579,38 +1437,29 @@ fn doc_impl_item(
                         // because impls can't have a stability.
                         if item.doc_value().is_some() {
                             document_item_info(w, cx, it, is_hidden, Some(parent));
-                            document_full(w, item, cx, "", is_hidden);
+                            document_full(w, item, cx, is_hidden);
                         } else {
                             // In case the item isn't documented,
                             // provide short documentation from the trait.
-                            document_short(
-                                w,
-                                it,
-                                cx,
-                                link,
-                                "",
-                                is_hidden,
-                                Some(parent),
-                                show_def_docs,
-                            );
+                            document_short(w, it, cx, link, is_hidden, parent, show_def_docs);
                         }
                     }
                 } else {
                     document_item_info(w, cx, item, is_hidden, Some(parent));
                     if show_def_docs {
-                        document_full(w, item, cx, "", is_hidden);
+                        document_full(w, item, cx, is_hidden);
                     }
                 }
             } else {
-                document_short(w, item, cx, link, "", is_hidden, Some(parent), show_def_docs);
+                document_short(w, item, cx, link, is_hidden, parent, show_def_docs);
             }
         }
     }
 
-    w.write_str("<div class=\"impl-items\">");
+    let mut impl_items = Buffer::empty_from(w);
     for trait_item in &i.inner_impl().items {
         doc_impl_item(
-            w,
+            &mut impl_items,
             cx,
             trait_item,
             if trait_.is_some() { &i.impl_item } else { parent },
@@ -1666,7 +1515,7 @@ fn render_default_items(
     if show_default_items {
         if let Some(t) = trait_ {
             render_default_items(
-                w,
+                &mut impl_items,
                 cx,
                 &t.trait_,
                 &i.inner_impl(),
@@ -1678,7 +1527,112 @@ fn render_default_items(
             );
         }
     }
-    w.write_str("</div>");
+    let details_str = if impl_items.is_empty() {
+        ""
+    } else {
+        "<details class=\"rustdoc-toggle implementors-toggle\" open><summary>"
+    };
+    if render_mode == RenderMode::Normal {
+        let id = cx.derive_id(match i.inner_impl().trait_ {
+            Some(ref t) => {
+                if is_on_foreign_type {
+                    get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx)
+                } else {
+                    format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx))))
+                }
+            }
+            None => "impl".to_string(),
+        });
+        let aliases = if aliases.is_empty() {
+            String::new()
+        } else {
+            format!(" aliases=\"{}\"", aliases.join(","))
+        };
+        if let Some(use_absolute) = use_absolute {
+            write!(
+                w,
+                "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">",
+                details_str, id, aliases
+            );
+            if !impl_items.is_empty() {
+                close_tags.insert_str(0, "</details>");
+            }
+            write!(w, "{}", i.inner_impl().print(use_absolute, cx));
+            if show_def_docs {
+                for it in &i.inner_impl().items {
+                    if let clean::TypedefItem(ref tydef, _) = *it.kind {
+                        w.write_str("<span class=\"where fmt-newline\">  ");
+                        assoc_type(
+                            w,
+                            it,
+                            &[],
+                            Some(&tydef.type_),
+                            AssocItemLink::Anchor(None),
+                            "",
+                            cx,
+                        );
+                        w.write_str(";</span>");
+                    }
+                }
+            }
+            w.write_str("</code>");
+        } else {
+            write!(
+                w,
+                "{}<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>",
+                details_str,
+                id,
+                aliases,
+                i.inner_impl().print(false, cx)
+            );
+            if !impl_items.is_empty() {
+                close_tags.insert_str(0, "</details>");
+            }
+        }
+        write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+        render_stability_since_raw(
+            w,
+            i.impl_item.stable_since(tcx).as_deref(),
+            i.impl_item.const_stable_since(tcx).as_deref(),
+            outer_version,
+            outer_const_version,
+        );
+        write_srclink(cx, &i.impl_item, w);
+        if impl_items.is_empty() {
+            w.write_str("</h3>");
+        } else {
+            w.write_str("</h3></summary>");
+        }
+
+        if trait_.is_some() {
+            if let Some(portability) = portability(&i.impl_item, Some(parent)) {
+                write!(w, "<div class=\"item-info\">{}</div>", portability);
+            }
+        }
+
+        if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
+            let mut ids = cx.id_map.borrow_mut();
+            write!(
+                w,
+                "<div class=\"docblock\">{}</div>",
+                Markdown(
+                    &*dox,
+                    &i.impl_item.links(cx),
+                    &mut ids,
+                    cx.shared.codes,
+                    cx.shared.edition(),
+                    &cx.shared.playground
+                )
+                .into_string()
+            );
+        }
+    }
+    if !impl_items.is_empty() {
+        w.write_str("<div class=\"impl-items\">");
+        w.push_buffer(impl_items);
+        close_tags.insert_str(0, "</div>");
+    }
+    w.write_str(&close_tags);
 }
 
 fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
@@ -1865,7 +1819,6 @@ fn small_url_encode(s: String) -> String {
 fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
     if let Some(v) = cx.cache.impls.get(&it.def_id) {
         let mut used_links = FxHashSet::default();
-        let tcx = cx.tcx();
         let cache = cx.cache();
 
         {
@@ -1900,9 +1853,9 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
                     .iter()
                     .filter_map(|it| {
                         if let Some(ref i) = it.inner_impl().trait_ {
-                            let i_display = format!("{:#}", i.print(cache, tcx));
+                            let i_display = format!("{:#}", i.print(cx));
                             let out = Escape(&i_display);
-                            let encoded = small_url_encode(format!("{:#}", i.print(cache, tcx)));
+                            let encoded = small_url_encode(format!("{:#}", i.print(cx)));
                             let generated = format!(
                                 "<a href=\"#impl-{}\">{}{}</a>",
                                 encoded,
@@ -1974,7 +1927,6 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
 
 fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &Vec<Impl>) {
     let c = cx.cache();
-    let tcx = cx.tcx();
 
     debug!("found Deref: {:?}", impl_);
     if let Some((target, real_target)) =
@@ -2023,11 +1975,8 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &V
                     out,
                     "<a class=\"sidebar-title\" href=\"#{}\">Methods from {}&lt;Target={}&gt;</a>",
                     id,
-                    Escape(&format!(
-                        "{:#}",
-                        impl_.inner_impl().trait_.as_ref().unwrap().print(c, tcx)
-                    )),
-                    Escape(&format!("{:#}", real_target.print(c, tcx))),
+                    Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
+                    Escape(&format!("{:#}", real_target.print(cx))),
                 );
                 // We want links' order to be reproducible so we don't use unstable sort.
                 ret.sort();
@@ -2083,27 +2032,20 @@ fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clea
 fn get_id_for_impl_on_foreign_type(
     for_: &clean::Type,
     trait_: &clean::Type,
-    cache: &Cache,
-    tcx: TyCtxt<'_>,
+    cx: &Context<'_>,
 ) -> String {
-    small_url_encode(format!(
-        "impl-{:#}-for-{:#}",
-        trait_.print(cache, tcx),
-        for_.print(cache, tcx)
-    ))
+    small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cx), for_.print(cx),))
 }
 
-fn extract_for_impl_name(
-    item: &clean::Item,
-    cache: &Cache,
-    tcx: TyCtxt<'_>,
-) -> Option<(String, String)> {
+fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
     match *item.kind {
         clean::ItemKind::ImplItem(ref i) => {
             if let Some(ref trait_) = i.trait_ {
+                // Alternative format produces no URLs,
+                // so this parameter does nothing.
                 Some((
-                    format!("{:#}", i.for_.print(cache, tcx)),
-                    get_id_for_impl_on_foreign_type(&i.for_, trait_, cache, tcx),
+                    format!("{:#}", i.for_.print(cx)),
+                    get_id_for_impl_on_foreign_type(&i.for_, trait_, cx),
                 ))
             } else {
                 None
@@ -2184,7 +2126,6 @@ fn print_sidebar_section(
 
     if let Some(implementors) = cx.cache.implementors.get(&it.def_id) {
         let cache = cx.cache();
-        let tcx = cx.tcx();
         let mut res = implementors
             .iter()
             .filter(|i| {
@@ -2193,7 +2134,7 @@ fn print_sidebar_section(
                     .def_id_full(cache)
                     .map_or(false, |d| !cx.cache.paths.contains_key(&d))
             })
-            .filter_map(|i| extract_for_impl_name(&i.impl_item, cache, tcx))
+            .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
             .collect::<Vec<_>>();
 
         if !res.is_empty() {
index 0cdfe435b9c9346379c5d2a6da808c1266c080af..1bb1db00e8825d4a3d83d2757e3841a2df38601e 100644 (file)
@@ -1,3 +1,5 @@
+use clean::AttributesExt;
+
 use std::cmp::Ordering;
 
 use rustc_data_structures::fx::FxHashMap;
 
 use super::{
     collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_strs, notable_traits_decl,
-    render_assoc_item, render_assoc_items, render_attributes, render_impl,
-    render_stability_since_raw, write_srclink, AssocItemLink, Context,
+    render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
+    render_impl, render_stability_since_raw, write_srclink, AssocItemLink, Context,
 };
 use crate::clean::{self, GetDefId};
-use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
-use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode};
+use crate::formats::{AssocItemRender, Impl, RenderMode};
 use crate::html::escape::Escape;
 use crate::html::format::{print_abi_with_space, print_where_clause, Buffer, PrintWithSpace};
 use crate::html::highlight;
@@ -28,8 +29,8 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer)
     // Write the breadcrumb trail header for the top
     buf.write_str("<h1 class=\"fqn\"><span class=\"in-band\">");
     let name = match *item.kind {
-        clean::ModuleItem(ref m) => {
-            if m.is_crate {
+        clean::ModuleItem(_) => {
+            if item.is_crate() {
                 "Crate "
             } else {
                 "Module "
@@ -131,6 +132,26 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer)
     }
 }
 
+/// For large structs, enums, unions, etc, determine whether to hide their fields
+fn should_hide_fields(n_fields: usize) -> bool {
+    n_fields > 12
+}
+
+fn toggle_open(w: &mut Buffer, text: &str) {
+    write!(
+        w,
+        "<details class=\"rustdoc-toggle type-contents-toggle\">\
+            <summary class=\"hideme\">\
+                <span>Show {}</span>\
+            </summary>",
+        text
+    );
+}
+
+fn toggle_close(w: &mut Buffer) {
+    w.write_str("</details>");
+}
+
 fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) {
     document(w, cx, item, None);
 
@@ -248,26 +269,53 @@ fn cmp(
                     Some(ref src) => write!(
                         w,
                         "<tr><td><code>{}extern crate {} as {};",
-                        myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
-                        anchor(myitem.def_id, &*src.as_str(), cx.cache()),
+                        myitem.visibility.print_with_space(myitem.def_id, cx),
+                        anchor(myitem.def_id, &*src.as_str(), cx),
                         myitem.name.as_ref().unwrap(),
                     ),
                     None => write!(
                         w,
                         "<tr><td><code>{}extern crate {};",
-                        myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
-                        anchor(myitem.def_id, &*myitem.name.as_ref().unwrap().as_str(), cx.cache()),
+                        myitem.visibility.print_with_space(myitem.def_id, cx),
+                        anchor(myitem.def_id, &*myitem.name.as_ref().unwrap().as_str(), cx),
                     ),
                 }
                 w.write_str("</code></td></tr>");
             }
 
             clean::ImportItem(ref import) => {
+                let (stab, stab_tags) = if let Some(import_def_id) = import.source.did {
+                    let ast_attrs = cx.tcx().get_attrs(import_def_id);
+                    let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs, None));
+
+                    // Just need an item with the correct def_id and attrs
+                    let import_item = clean::Item {
+                        def_id: import_def_id,
+                        attrs: import_attrs,
+                        cfg: ast_attrs.cfg(cx.tcx().sess.diagnostic()),
+                        ..myitem.clone()
+                    };
+
+                    let stab = import_item.stability_class(cx.tcx());
+                    let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()));
+                    (stab, stab_tags)
+                } else {
+                    (None, None)
+                };
+
+                let add = if stab.is_some() { " " } else { "" };
+
                 write!(
                     w,
-                    "<tr><td><code>{}{}</code></td></tr>",
-                    myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
-                    import.print(cx.cache(), cx.tcx()),
+                    "<tr class=\"{stab}{add}import-item\">\
+                         <td><code>{vis}{imp}</code></td>\
+                         <td class=\"docblock-short\">{stab_tags}</td>\
+                     </tr>",
+                    stab = stab.unwrap_or_default(),
+                    add = add,
+                    vis = myitem.visibility.print_with_space(myitem.def_id, cx),
+                    imp = import.print(cx),
+                    stab_tags = stab_tags.unwrap_or_default(),
                 );
             }
 
@@ -298,10 +346,10 @@ fn cmp(
                      </tr>",
                     name = *myitem.name.as_ref().unwrap(),
                     stab_tags = extra_info_tags(myitem, item, cx.tcx()),
-                    docs = MarkdownSummaryLine(&doc_value, &myitem.links(&cx.cache)).into_string(),
+                    docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(),
                     class = myitem.type_(),
                     add = add,
-                    stab = stab.unwrap_or_else(String::new),
+                    stab = stab.unwrap_or_default(),
                     unsafety_flag = unsafety_flag,
                     href = item_path(myitem.type_(), &myitem.name.unwrap().as_str()),
                     title = [full_path(cx, myitem), myitem.type_().to_string()]
@@ -351,12 +399,12 @@ fn tag_html(class: &str, title: &str, contents: &str) -> String {
         tags += &tag_html("unstable", "", "Experimental");
     }
 
-    let cfg = match (&item.attrs.cfg, parent.attrs.cfg.as_ref()) {
+    let cfg = match (&item.cfg, parent.cfg.as_ref()) {
         (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
         (cfg, _) => cfg.as_deref().cloned(),
     };
 
-    debug!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg);
+    debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.cfg, cfg);
     if let Some(ref cfg) = cfg {
         tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
     }
@@ -367,37 +415,37 @@ fn tag_html(class: &str, title: &str, contents: &str) -> String {
 fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) {
     let header_len = format!(
         "{}{}{}{}{:#}fn {}{:#}",
-        it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+        it.visibility.print_with_space(it.def_id, cx),
         f.header.constness.print_with_space(),
         f.header.asyncness.print_with_space(),
         f.header.unsafety.print_with_space(),
         print_abi_with_space(f.header.abi),
         it.name.as_ref().unwrap(),
-        f.generics.print(cx.cache(), cx.tcx())
+        f.generics.print(cx),
     )
     .len();
     w.write_str("<pre class=\"rust fn\">");
-    render_attributes(w, it, false);
+    render_attributes_in_pre(w, it, "");
     write!(
         w,
         "{vis}{constness}{asyncness}{unsafety}{abi}fn \
          {name}{generics}{decl}{notable_traits}{where_clause}</pre>",
-        vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+        vis = it.visibility.print_with_space(it.def_id, cx),
         constness = f.header.constness.print_with_space(),
         asyncness = f.header.asyncness.print_with_space(),
         unsafety = f.header.unsafety.print_with_space(),
         abi = print_abi_with_space(f.header.abi),
         name = it.name.as_ref().unwrap(),
-        generics = f.generics.print(cx.cache(), cx.tcx()),
-        where_clause = print_where_clause(&f.generics, cx.cache(), cx.tcx(), 0, true),
-        decl = f.decl.full_print(cx.cache(), cx.tcx(), header_len, 0, f.header.asyncness),
-        notable_traits = notable_traits_decl(&f.decl, cx.cache(), cx.tcx()),
+        generics = f.generics.print(cx),
+        where_clause = print_where_clause(&f.generics, cx, 0, true),
+        decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx),
+        notable_traits = notable_traits_decl(&f.decl, cx),
     );
     document(w, cx, it, None)
 }
 
 fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
-    let bounds = bounds(&t.bounds, false, cx.cache(), cx.tcx());
+    let bounds = bounds(&t.bounds, false, cx);
     let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
     let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
     let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
@@ -406,20 +454,20 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
     // Output the trait definition
     wrap_into_docblock(w, |w| {
         w.write_str("<pre class=\"rust trait\">");
-        render_attributes(w, it, true);
+        render_attributes_in_pre(w, it, "");
         write!(
             w,
             "{}{}{}trait {}{}{}",
-            it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+            it.visibility.print_with_space(it.def_id, cx),
             t.unsafety.print_with_space(),
             if t.is_auto { "auto " } else { "" },
             it.name.as_ref().unwrap(),
-            t.generics.print(cx.cache(), cx.tcx()),
+            t.generics.print(cx),
             bounds
         );
 
         if !t.generics.where_predicates.is_empty() {
-            write!(w, "{}", print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true));
+            write!(w, "{}", print_where_clause(&t.generics, cx, 0, true));
         } else {
             w.write_str(" ");
         }
@@ -429,10 +477,25 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         } else {
             // FIXME: we should be using a derived_id for the Anchors here
             w.write_str("{\n");
+            let mut toggle = false;
+
+            // If there are too many associated types, hide _everything_
+            if should_hide_fields(types.len()) {
+                toggle = true;
+                toggle_open(w, "associated items");
+            }
             for t in &types {
                 render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
                 w.write_str(";\n");
             }
+            // If there are too many associated constants, hide everything after them
+            // We also do this if the types + consts is large because otherwise we could
+            // render a bunch of types and _then_ a bunch of consts just because both were
+            // _just_ under the limit
+            if !toggle && should_hide_fields(types.len() + consts.len()) {
+                toggle = true;
+                toggle_open(w, "associated constants and methods");
+            }
             if !types.is_empty() && !consts.is_empty() {
                 w.write_str("\n");
             }
@@ -440,6 +503,10 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                 render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
                 w.write_str(";\n");
             }
+            if !toggle && should_hide_fields(required.len() + provided.len()) {
+                toggle = true;
+                toggle_open(w, "methods");
+            }
             if !consts.is_empty() && !required.is_empty() {
                 w.write_str("\n");
             }
@@ -470,6 +537,9 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                     w.write_str("<div class=\"item-spacer\"></div>");
                 }
             }
+            if toggle {
+                toggle_close(w);
+            }
             w.write_str("}");
         }
         w.write_str("</pre>")
@@ -592,8 +662,8 @@ fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item
         let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
             local.iter().partition(|i| i.inner_impl().synthetic);
 
-        synthetic.sort_by(|a, b| compare_impl(a, b, cx.cache(), cx.tcx()));
-        concrete.sort_by(|a, b| compare_impl(a, b, cx.cache(), cx.tcx()));
+        synthetic.sort_by(|a, b| compare_impl(a, b, cx));
+        concrete.sort_by(|a, b| compare_impl(a, b, cx));
 
         if !foreign.is_empty() {
             write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
@@ -693,14 +763,14 @@ fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item
 
 fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
     w.write_str("<pre class=\"rust trait-alias\">");
-    render_attributes(w, it, false);
+    render_attributes_in_pre(w, it, "");
     write!(
         w,
         "trait {}{}{} = {};</pre>",
         it.name.as_ref().unwrap(),
-        t.generics.print(cx.cache(), cx.tcx()),
-        print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true),
-        bounds(&t.bounds, true, cx.cache(), cx.tcx())
+        t.generics.print(cx),
+        print_where_clause(&t.generics, cx, 0, true),
+        bounds(&t.bounds, true, cx)
     );
 
     document(w, cx, it, None);
@@ -714,14 +784,14 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clea
 
 fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
     w.write_str("<pre class=\"rust opaque\">");
-    render_attributes(w, it, false);
+    render_attributes_in_pre(w, it, "");
     write!(
         w,
         "type {}{}{where_clause} = impl {bounds};</pre>",
         it.name.as_ref().unwrap(),
-        t.generics.print(cx.cache(), cx.tcx()),
-        where_clause = print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true),
-        bounds = bounds(&t.bounds, false, cx.cache(), cx.tcx()),
+        t.generics.print(cx),
+        where_clause = print_where_clause(&t.generics, cx, 0, true),
+        bounds = bounds(&t.bounds, false, cx),
     );
 
     document(w, cx, it, None);
@@ -735,14 +805,14 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean:
 
 fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
     w.write_str("<pre class=\"rust typedef\">");
-    render_attributes(w, it, false);
+    render_attributes_in_pre(w, it, "");
     write!(
         w,
         "type {}{}{where_clause} = {type_};</pre>",
         it.name.as_ref().unwrap(),
-        t.generics.print(cx.cache(), cx.tcx()),
-        where_clause = print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true),
-        type_ = t.type_.print(cx.cache(), cx.tcx()),
+        t.generics.print(cx),
+        where_clause = print_where_clause(&t.generics, cx, 0, true),
+        type_ = t.type_.print(cx),
     );
 
     document(w, cx, it, None);
@@ -757,7 +827,7 @@ fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::T
 fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
     wrap_into_docblock(w, |w| {
         w.write_str("<pre class=\"rust union\">");
-        render_attributes(w, it, true);
+        render_attributes_in_pre(w, it, "");
         render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
         w.write_str("</pre>")
     });
@@ -789,7 +859,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
                 id = id,
                 name = name,
                 shortty = ItemType::StructField,
-                ty = ty.print(cx.cache(), cx.tcx()),
+                ty = ty.print(cx),
             );
             if let Some(stability_class) = field.stability_class(cx.tcx()) {
                 write!(w, "<span class=\"stab {stab}\"></span>", stab = stability_class);
@@ -803,19 +873,23 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
 fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
     wrap_into_docblock(w, |w| {
         w.write_str("<pre class=\"rust enum\">");
-        render_attributes(w, it, true);
+        render_attributes_in_pre(w, it, "");
         write!(
             w,
             "{}enum {}{}{}",
-            it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+            it.visibility.print_with_space(it.def_id, cx),
             it.name.as_ref().unwrap(),
-            e.generics.print(cx.cache(), cx.tcx()),
-            print_where_clause(&e.generics, cx.cache(), cx.tcx(), 0, true),
+            e.generics.print(cx),
+            print_where_clause(&e.generics, cx, 0, true),
         );
         if e.variants.is_empty() && !e.variants_stripped {
             w.write_str(" {}");
         } else {
             w.write_str(" {\n");
+            let toggle = should_hide_fields(e.variants.len());
+            if toggle {
+                toggle_open(w, "variants");
+            }
             for v in &e.variants {
                 w.write_str("    ");
                 let name = v.name.as_ref().unwrap();
@@ -828,7 +902,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                                 if i > 0 {
                                     w.write_str(",&nbsp;")
                                 }
-                                write!(w, "{}", ty.print(cx.cache(), cx.tcx()));
+                                write!(w, "{}", ty.print(cx));
                             }
                             w.write_str(")");
                         }
@@ -844,6 +918,9 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
             if e.variants_stripped {
                 w.write_str("    // some variants omitted\n");
             }
+            if toggle {
+                toggle_close(w);
+            }
             w.write_str("}");
         }
         w.write_str("</pre>")
@@ -875,7 +952,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                     if i > 0 {
                         w.write_str(",&nbsp;");
                     }
-                    write!(w, "{}", ty.print(cx.cache(), cx.tcx()));
+                    write!(w, "{}", ty.print(cx));
                 }
                 w.write_str(")");
             }
@@ -885,6 +962,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
 
             use crate::clean::Variant;
             if let clean::VariantItem(Variant::Struct(ref s)) = *variant.kind {
+                toggle_open(w, "fields");
                 let variant_id = cx.derive_id(format!(
                     "{}.{}.fields",
                     ItemType::Variant,
@@ -912,12 +990,13 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                              </span>",
                             id = id,
                             f = field.name.as_ref().unwrap(),
-                            t = ty.print(cx.cache(), cx.tcx())
+                            t = ty.print(cx)
                         );
                         document(w, cx, field, Some(variant));
                     }
                 }
                 w.write_str("</div></div>");
+                toggle_close(w);
             }
             render_stability_since(w, variant, it, cx.tcx());
         }
@@ -933,7 +1012,7 @@ fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Mac
             Some("macro"),
             None,
             None,
-            it.span.inner().edition(),
+            it.span(cx.tcx()).inner().edition(),
         );
     });
     document(w, cx, it, None)
@@ -976,14 +1055,14 @@ fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
 
 fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) {
     w.write_str("<pre class=\"rust const\">");
-    render_attributes(w, it, false);
+    render_attributes_in_code(w, it);
 
     write!(
         w,
         "{vis}const {name}: {typ}",
-        vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+        vis = it.visibility.print_with_space(it.def_id, cx),
         name = it.name.as_ref().unwrap(),
-        typ = c.type_.print(cx.cache(), cx.tcx()),
+        typ = c.type_.print(cx),
     );
 
     let value = c.value(cx.tcx());
@@ -1015,7 +1094,7 @@ fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::
 fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
     wrap_into_docblock(w, |w| {
         w.write_str("<pre class=\"rust struct\">");
-        render_attributes(w, it, true);
+        render_attributes_in_code(w, it);
         render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
         w.write_str("</pre>")
     });
@@ -1053,7 +1132,7 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St
                     item_type = ItemType::StructField,
                     id = id,
                     name = field.name.as_ref().unwrap(),
-                    ty = ty.print(cx.cache(), cx.tcx())
+                    ty = ty.print(cx)
                 );
                 document(w, cx, field, Some(it));
             }
@@ -1064,25 +1143,25 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St
 
 fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
     w.write_str("<pre class=\"rust static\">");
-    render_attributes(w, it, false);
+    render_attributes_in_code(w, it);
     write!(
         w,
         "{vis}static {mutability}{name}: {typ}</pre>",
-        vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+        vis = it.visibility.print_with_space(it.def_id, cx),
         mutability = s.mutability.print_with_space(),
         name = it.name.as_ref().unwrap(),
-        typ = s.type_.print(cx.cache(), cx.tcx())
+        typ = s.type_.print(cx)
     );
     document(w, cx, it, None)
 }
 
 fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
     w.write_str("<pre class=\"rust foreigntype\">extern {\n");
-    render_attributes(w, it, false);
+    render_attributes_in_code(w, it);
     write!(
         w,
         "    {}type {};\n}}</pre>",
-        it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+        it.visibility.print_with_space(it.def_id, cx),
         it.name.as_ref().unwrap(),
     );
 
@@ -1146,12 +1225,7 @@ pub(super) fn item_path(ty: ItemType, name: &str) -> String {
     }
 }
 
-fn bounds(
-    t_bounds: &[clean::GenericBound],
-    trait_alias: bool,
-    cache: &Cache,
-    tcx: TyCtxt<'_>,
-) -> String {
+fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String {
     let mut bounds = String::new();
     if !t_bounds.is_empty() {
         if !trait_alias {
@@ -1161,7 +1235,7 @@ fn bounds(
             if i > 0 {
                 bounds.push_str(" + ");
             }
-            bounds.push_str(&p.print(cache, tcx).to_string());
+            bounds.push_str(&p.print(cx).to_string());
         }
     }
     bounds
@@ -1171,7 +1245,7 @@ fn wrap_into_docblock<F>(w: &mut Buffer, f: F)
 where
     F: FnOnce(&mut Buffer),
 {
-    w.write_str("<div class=\"docblock type-decl hidden-by-usual-hider\">");
+    w.write_str("<div class=\"docblock type-decl\">");
     f(w);
     w.write_str("</div>")
 }
@@ -1191,17 +1265,12 @@ fn render_stability_since(
     )
 }
 
-fn compare_impl<'a, 'b>(
-    lhs: &'a &&Impl,
-    rhs: &'b &&Impl,
-    cache: &Cache,
-    tcx: TyCtxt<'_>,
-) -> Ordering {
-    let lhs = format!("{}", lhs.inner_impl().print(cache, false, tcx));
-    let rhs = format!("{}", rhs.inner_impl().print(cache, false, tcx));
+fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering {
+    let lhss = format!("{}", lhs.inner_impl().print(false, cx));
+    let rhss = format!("{}", rhs.inner_impl().print(false, cx));
 
     // lhs and rhs are formatted as HTML, which may be unnecessary
-    compare_names(&lhs, &rhs)
+    compare_names(&lhss, &rhss)
 }
 
 fn render_implementor(
@@ -1251,24 +1320,31 @@ fn render_union(
     write!(
         w,
         "{}{}{}",
-        it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+        it.visibility.print_with_space(it.def_id, cx),
         if structhead { "union " } else { "" },
         it.name.as_ref().unwrap()
     );
     if let Some(g) = g {
-        write!(w, "{}", g.print(cx.cache(), cx.tcx()));
-        write!(w, "{}", print_where_clause(&g, cx.cache(), cx.tcx(), 0, true));
+        write!(w, "{}", g.print(cx));
+        write!(w, "{}", print_where_clause(&g, cx, 0, true));
     }
 
     write!(w, " {{\n{}", tab);
+    let count_fields =
+        fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
+    let toggle = should_hide_fields(count_fields);
+    if toggle {
+        toggle_open(w, "fields");
+    }
+
     for field in fields {
         if let clean::StructFieldItem(ref ty) = *field.kind {
             write!(
                 w,
                 "    {}{}: {},\n{}",
-                field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
+                field.visibility.print_with_space(field.def_id, cx),
                 field.name.as_ref().unwrap(),
-                ty.print(cx.cache(), cx.tcx()),
+                ty.print(cx),
                 tab
             );
         }
@@ -1277,6 +1353,9 @@ fn render_union(
     if it.has_stripped_fields().unwrap() {
         write!(w, "    // some fields omitted\n{}", tab);
     }
+    if toggle {
+        toggle_close(w);
+    }
     w.write_str("}");
 }
 
@@ -1293,31 +1372,36 @@ fn render_struct(
     write!(
         w,
         "{}{}{}",
-        it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+        it.visibility.print_with_space(it.def_id, cx),
         if structhead { "struct " } else { "" },
         it.name.as_ref().unwrap()
     );
     if let Some(g) = g {
-        write!(w, "{}", g.print(cx.cache(), cx.tcx()))
+        write!(w, "{}", g.print(cx))
     }
     match ty {
         CtorKind::Fictive => {
             if let Some(g) = g {
-                write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, true),)
+                write!(w, "{}", print_where_clause(g, cx, 0, true),)
             }
-            let mut has_visible_fields = false;
             w.write_str(" {");
+            let count_fields =
+                fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
+            let has_visible_fields = count_fields > 0;
+            let toggle = should_hide_fields(count_fields);
+            if toggle {
+                toggle_open(w, "fields");
+            }
             for field in fields {
                 if let clean::StructFieldItem(ref ty) = *field.kind {
                     write!(
                         w,
                         "\n{}    {}{}: {},",
                         tab,
-                        field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
+                        field.visibility.print_with_space(field.def_id, cx),
                         field.name.as_ref().unwrap(),
-                        ty.print(cx.cache(), cx.tcx()),
+                        ty.print(cx),
                     );
-                    has_visible_fields = true;
                 }
             }
 
@@ -1331,6 +1415,9 @@ fn render_struct(
                 // `{ /* fields omitted */ }` to save space.
                 write!(w, " /* fields omitted */ ");
             }
+            if toggle {
+                toggle_close(w);
+            }
             w.write_str("}");
         }
         CtorKind::Fn => {
@@ -1345,8 +1432,8 @@ fn render_struct(
                         write!(
                             w,
                             "{}{}",
-                            field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
-                            ty.print(cx.cache(), cx.tcx()),
+                            field.visibility.print_with_space(field.def_id, cx),
+                            ty.print(cx),
                         )
                     }
                     _ => unreachable!(),
@@ -1354,14 +1441,14 @@ fn render_struct(
             }
             w.write_str(")");
             if let Some(g) = g {
-                write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),)
+                write!(w, "{}", print_where_clause(g, cx, 0, false),)
             }
             w.write_str(";");
         }
         CtorKind::Const => {
             // Needed for PhantomData.
             if let Some(g) = g {
-                write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),)
+                write!(w, "{}", print_where_clause(g, cx, 0, false),)
             }
             w.write_str(";");
         }
index 8fb6d68f3c6bc61df4b50798b1e14dcc39684453..8e10c696df05d3f9fa452f446ee7299d1a0c5805 100644 (file)
@@ -16,7 +16,6 @@
 use crate::config::{EmitType, RenderOptions};
 use crate::docfs::PathError;
 use crate::error::Error;
-use crate::formats::FormatRenderer;
 use crate::html::{layout, static_files};
 
 crate static FILES_UNVERSIONED: Lazy<FxHashMap<&str, &[u8]>> = Lazy::new(|| {
@@ -223,6 +222,7 @@ pub(super) fn write_shared(
             &format!(" = {}", serde_json::to_string(&themes).unwrap()),
         ),
     )?;
+    write_minify("search.js", static_files::SEARCH_JS)?;
     write_minify("settings.js", static_files::SETTINGS_JS)?;
     if cx.shared.include_sources {
         write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?;
@@ -410,7 +410,7 @@ fn to_json_string(&self) -> String {
     write_crate("search-index.js", &|| {
         let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
         v.push_str(&all_indexes.join(",\\\n"));
-        v.push_str("\\\n}');\ninitSearch(searchIndex);");
+        v.push_str("\\\n}');\nif (window.initSearch) {window.initSearch(searchIndex)};");
         Ok(v.into_bytes())
     })?;
 
@@ -425,7 +425,7 @@ fn to_json_string(&self) -> String {
             md_opts.output = cx.dst.clone();
             md_opts.external_html = (*cx.shared).layout.external_html.clone();
 
-            crate::markdown::render(&index_page, md_opts, cx.shared.edition)
+            crate::markdown::render(&index_page, md_opts, cx.shared.edition())
                 .map_err(|e| Error::new(e, &index_page))?;
         } else {
             let dst = cx.dst.join("index.html");
@@ -500,7 +500,7 @@ struct Implementor {
                     None
                 } else {
                     Some(Implementor {
-                        text: imp.inner_impl().print(cx.cache(), false, cx.tcx()).to_string(),
+                        text: imp.inner_impl().print(false, cx).to_string(),
                         synthetic: imp.inner_impl().synthetic,
                         types: collect_paths_for_type(imp.inner_impl().for_.clone(), cx.cache()),
                     })
index 001c8b090448bed2bb20ce825f4295a5a5c9155c..3d4d8df0a71989165dcc65095b3fdeca34021989 100644 (file)
@@ -41,11 +41,11 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
         // then we need to render it out to the filesystem.
         if self.scx.include_sources
             // skip all synthetic "files"
-            && item.span.filename(self.sess()).is_real()
+            && item.span(self.scx.tcx).filename(self.sess()).is_real()
             // skip non-local files
-            && item.span.cnum(self.sess()) == LOCAL_CRATE
+            && item.span(self.scx.tcx).cnum(self.sess()) == LOCAL_CRATE
         {
-            let filename = item.span.filename(self.sess());
+            let filename = item.span(self.scx.tcx).filename(self.sess());
             // If it turns out that we couldn't read this file, then we probably
             // can't read any of the files (generating html output from json or
             // something like that), so just don't include sources for the
@@ -55,7 +55,7 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
                 Ok(()) => true,
                 Err(e) => {
                     self.scx.tcx.sess.span_err(
-                        item.span.inner(),
+                        item.span(self.scx.tcx).inner(),
                         &format!("failed to render source code for `{}`: {}", filename, e),
                     );
                     false
@@ -129,7 +129,7 @@ fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
             &self.scx.layout,
             &page,
             "",
-            |buf: &mut _| print_src(buf, contents, self.scx.edition),
+            |buf: &mut _| print_src(buf, contents, self.scx.edition()),
             &self.scx.style_files,
         );
         self.scx.fs.write(&cur, v.as_bytes())?;
index 80dc6b923f68d8ee0df5d32e99b4dc64ce21e386..7fbb97beae7e9cd87328005c5d96098473e27a4b 100644 (file)
@@ -1,4 +1,3 @@
-// ignore-tidy-filelength
 // Local js definitions:
 /* global addClass, getSettingValue, hasClass */
 /* global onEach, onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */
@@ -44,6 +43,7 @@ if (!DOMTokenList.prototype.remove) {
         window.rootPath = rustdocVars.attributes["data-root-path"].value;
         window.currentCrate = rustdocVars.attributes["data-current-crate"].value;
         window.searchJS = rustdocVars.attributes["data-search-js"].value;
+        window.searchIndexJS = rustdocVars.attributes["data-search-index-js"].value;
     }
     var sidebarVars = document.getElementById("sidebar-vars");
     if (sidebarVars) {
@@ -77,14 +77,6 @@ function getVirtualKey(ev) {
     return String.fromCharCode(c);
 }
 
-function getSearchInput() {
-    return document.getElementsByClassName("search-input")[0];
-}
-
-function getSearchElement() {
-    return document.getElementById("search");
-}
-
 var THEME_PICKER_ELEMENT_ID = "theme-picker";
 var THEMES_ELEMENT_ID = "theme-choices";
 
@@ -101,16 +93,6 @@ function getNakedUrl() {
     return window.location.href.split("?")[0].split("#")[0];
 }
 
-// Sets the focus on the search bar at the top of the page
-function focusSearchBar() {
-    getSearchInput().focus();
-}
-
-// Removes the focus from the search bar.
-function defocusSearchBar() {
-    getSearchInput().blur();
-}
-
 function showThemeButtonState() {
     var themePicker = getThemePickerElement();
     var themeChoices = getThemesElement();
@@ -173,67 +155,154 @@ function hideThemeButtonState() {
 (function() {
     "use strict";
 
-    // This mapping table should match the discriminants of
-    // `rustdoc::html::item_type::ItemType` type in Rust.
-    var itemTypes = ["mod",
-                     "externcrate",
-                     "import",
-                     "struct",
-                     "enum",
-                     "fn",
-                     "type",
-                     "static",
-                     "trait",
-                     "impl",
-                     "tymethod",
-                     "method",
-                     "structfield",
-                     "variant",
-                     "macro",
-                     "primitive",
-                     "associatedtype",
-                     "constant",
-                     "associatedconstant",
-                     "union",
-                     "foreigntype",
-                     "keyword",
-                     "existential",
-                     "attr",
-                     "derive",
-                     "traitalias"];
+    window.searchState = {
+      loadingText: "Loading search results...",
+      input: document.getElementsByClassName("search-input")[0],
+      outputElement: function() {
+        return document.getElementById("search");
+      },
+      title: null,
+      titleBeforeSearch: document.title,
+      timeout: null,
+      // On the search screen, so you remain on the last tab you opened.
+      //
+      // 0 for "In Names"
+      // 1 for "In Parameters"
+      // 2 for "In Return Types"
+      currentTab: 0,
+      mouseMovedAfterSearch: true,
+      clearInputTimeout: function() {
+        if (searchState.timeout !== null) {
+            clearTimeout(searchState.timeout);
+            searchState.timeout = null;
+        }
+      },
+      // Sets the focus on the search bar at the top of the page
+      focus: function() {
+          searchState.input.focus();
+      },
+      // Removes the focus from the search bar.
+      defocus: function() {
+          searchState.input.blur();
+      },
+      showResults: function(search) {
+        if (search === null || typeof search === 'undefined') {
+            search = searchState.outputElement();
+        }
+        addClass(main, "hidden");
+        removeClass(search, "hidden");
+        searchState.mouseMovedAfterSearch = false;
+        document.title = searchState.title;
+      },
+      hideResults: function(search) {
+        if (search === null || typeof search === 'undefined') {
+            search = searchState.outputElement();
+        }
+        addClass(search, "hidden");
+        removeClass(main, "hidden");
+        document.title = searchState.titleBeforeSearch;
+        // We also remove the query parameter from the URL.
+        if (searchState.browserSupportsHistoryApi()) {
+            history.replaceState("", window.currentCrate + " - Rust",
+                getNakedUrl() + window.location.hash);
+        }
+      },
+      getQueryStringParams: function() {
+        var params = {};
+        window.location.search.substring(1).split("&").
+            map(function(s) {
+                var pair = s.split("=");
+                params[decodeURIComponent(pair[0])] =
+                    typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
+            });
+        return params;
+      },
+      putBackSearch: function(search_input) {
+        var search = searchState.outputElement();
+        if (search_input.value !== "" && hasClass(search, "hidden")) {
+            searchState.showResults(search);
+            if (searchState.browserSupportsHistoryApi()) {
+                var extra = "?search=" + encodeURIComponent(search_input.value);
+                history.replaceState(search_input.value, "",
+                    getNakedUrl() + extra + window.location.hash);
+            }
+            document.title = searchState.title;
+        }
+      },
+      browserSupportsHistoryApi: function() {
+          return window.history && typeof window.history.pushState === "function";
+      },
+      setup: function() {
+        var search_input = searchState.input;
+        if (!searchState.input) {
+            return;
+        }
+        function loadScript(url) {
+            var script = document.createElement('script');
+            script.src = url;
+            document.head.append(script);
+        }
 
-    var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
-    var search_input = getSearchInput();
-    var searchTimeout = null;
-    var toggleAllDocsId = "toggle-all-docs";
+        var searchLoaded = false;
+        function loadSearch() {
+            if (!searchLoaded) {
+                searchLoaded = true;
+                loadScript(window.searchJS);
+                loadScript(window.searchIndexJS);
+            }
+        }
 
-    // On the search screen, so you remain on the last tab you opened.
-    //
-    // 0 for "In Names"
-    // 1 for "In Parameters"
-    // 2 for "In Return Types"
-    var currentTab = 0;
+        search_input.addEventListener("focus", function() {
+            searchState.putBackSearch(this);
+            search_input.origPlaceholder = searchState.input.placeholder;
+            search_input.placeholder = "Type your search here.";
+            loadSearch();
+        });
+        search_input.addEventListener("blur", function() {
+            search_input.placeholder = searchState.input.origPlaceholder;
+        });
 
-    var mouseMovedAfterSearch = true;
+        document.addEventListener("mousemove", function() {
+          searchState.mouseMovedAfterSearch = true;
+        });
 
-    var titleBeforeSearch = document.title;
-    var searchTitle = null;
+        search_input.removeAttribute('disabled');
 
-    function removeEmptyStringsFromArray(x) {
-        for (var i = 0, len = x.length; i < len; ++i) {
-            if (x[i] === "") {
-                x.splice(i, 1);
-                i -= 1;
-            }
+        // `crates{version}.js` should always be loaded before this script, so we can use it safely.
+        searchState.addCrateDropdown(window.ALL_CRATES);
+        var params = searchState.getQueryStringParams();
+        if (params.search !== undefined) {
+            var search = searchState.outputElement();
+            search.innerHTML = "<h3 style=\"text-align: center;\">" +
+               searchState.loadingText + "</h3>";
+            searchState.showResults(search);
+            loadSearch();
         }
-    }
+      },
+      addCrateDropdown: function(crates) {
+        var elem = document.getElementById("crate-search");
 
-    function clearInputTimeout() {
-        if (searchTimeout !== null) {
-            clearTimeout(searchTimeout);
-            searchTimeout = null;
+        if (!elem) {
+            return;
         }
-    }
+        var savedCrate = getSettingValue("saved-filter-crate");
+        for (var i = 0, len = crates.length; i < len; ++i) {
+            var option = document.createElement("option");
+            option.value = crates[i];
+            option.innerText = crates[i];
+            elem.appendChild(option);
+            // Set the crate filter from saved storage, if the current page has the saved crate
+            // filter.
+            //
+            // If not, ignore the crate filter -- we want to support filtering for crates on sites
+            // like doc.rust-lang.org where the crates may differ from page to page while on the
+            // same domain.
+            if (crates[i] === savedCrate) {
+                elem.value = savedCrate;
+            }
+        }
+      },
+    };
 
     function getPageId() {
         if (window.location.hash) {
@@ -276,65 +345,23 @@ function hideThemeButtonState() {
         document.getElementsByTagName("body")[0].style.marginTop = "";
     }
 
-    function showSearchResults(search) {
-        if (search === null || typeof search === 'undefined') {
-            search = getSearchElement();
-        }
-        addClass(main, "hidden");
-        removeClass(search, "hidden");
-        mouseMovedAfterSearch = false;
-        document.title = searchTitle;
-    }
-
-    function hideSearchResults(search) {
-        if (search === null || typeof search === 'undefined') {
-            search = getSearchElement();
-        }
-        addClass(search, "hidden");
-        removeClass(main, "hidden");
-        document.title = titleBeforeSearch;
-        // We also remove the query parameter from the URL.
-        if (browserSupportsHistoryApi()) {
-            history.replaceState("", window.currentCrate + " - Rust",
-                getNakedUrl() + window.location.hash);
-        }
-    }
-
-    // used for special search precedence
-    var TY_PRIMITIVE = itemTypes.indexOf("primitive");
-    var TY_KEYWORD = itemTypes.indexOf("keyword");
-
-    function getQueryStringParams() {
-        var params = {};
-        window.location.search.substring(1).split("&").
-            map(function(s) {
-                var pair = s.split("=");
-                params[decodeURIComponent(pair[0])] =
-                    typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
-            });
-        return params;
-    }
-
-    function browserSupportsHistoryApi() {
-        return window.history && typeof window.history.pushState === "function";
-    }
-
     function isHidden(elem) {
         return elem.offsetHeight === 0;
     }
 
+    var toggleAllDocsId = "toggle-all-docs";
     var main = document.getElementById("main");
     var savedHash = "";
 
     function handleHashes(ev) {
         var elem;
-        var search = getSearchElement();
+        var search = searchState.outputElement();
         if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
             // This block occurs when clicking on an element in the navbar while
             // in a search.
-            hideSearchResults(search);
+            searchState.hideResults(search);
             var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
-            if (browserSupportsHistoryApi()) {
+            if (searchState.browserSupportsHistoryApi()) {
                 // `window.location.search`` contains all the query parameters, not just `search`.
                 history.replaceState(hash, "",
                     getNakedUrl() + window.location.search + "#" + hash);
@@ -428,6 +455,15 @@ function hideThemeButtonState() {
         handleHashes(ev);
     }
 
+    function openParentDetails(elem) {
+        while (elem) {
+            if (elem.tagName === "DETAILS") {
+                elem.open = true;
+            }
+            elem = elem.parentNode;
+        }
+    }
+
     function expandSection(id) {
         var elem = document.getElementById(id);
         if (elem && isHidden(elem)) {
@@ -442,6 +478,10 @@ function hideThemeButtonState() {
                     // The element is not visible, we need to make it appear!
                     collapseDocs(collapses[0], "show");
                 }
+                // Open all ancestor <details> to make this element visible.
+                openParentDetails(h3.parentNode);
+            } else {
+                openParentDetails(elem.parentNode);
             }
         }
     }
@@ -475,18 +515,19 @@ function hideThemeButtonState() {
 
     function handleEscape(ev) {
         var help = getHelpElement(false);
-        var search = getSearchElement();
+        var search = searchState.outputElement();
         if (hasClass(help, "hidden") === false) {
             displayHelp(false, ev, help);
         } else if (hasClass(search, "hidden") === false) {
-            clearInputTimeout();
+            searchState.clearInputTimeout();
             ev.preventDefault();
-            hideSearchResults(search);
+            searchState.hideResults(search);
         }
-        defocusSearchBar();
+        searchState.defocus();
         hideThemeButtonState();
     }
 
+    var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
     function handleShortcut(ev) {
         // Don't interfere with browser shortcuts
         if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts === true) {
@@ -509,7 +550,7 @@ function hideThemeButtonState() {
             case "S":
                 displayHelp(false, ev);
                 ev.preventDefault();
-                focusSearchBar();
+                searchState.focus();
                 break;
 
             case "+":
@@ -581,1548 +622,108 @@ function hideThemeButtonState() {
         // The escape key is handled in handleEscape, not here,
         // so that pressing escape will close the menu even if it isn't focused
         }
-    }
-
-    function findParentElement(elem, tagName) {
-        do {
-            if (elem && elem.tagName === tagName) {
-                return elem;
-            }
-            elem = elem.parentNode;
-        } while (elem);
-        return null;
-    }
-
-    document.addEventListener("keypress", handleShortcut);
-    document.addEventListener("keydown", handleShortcut);
-
-    document.addEventListener("mousemove", function() { mouseMovedAfterSearch = true; });
-
-    var handleSourceHighlight = (function() {
-        var prev_line_id = 0;
-
-        var set_fragment = function(name) {
-            var x = window.scrollX,
-                y = window.scrollY;
-            if (browserSupportsHistoryApi()) {
-                history.replaceState(null, null, "#" + name);
-                highlightSourceLines();
-            } else {
-                location.replace("#" + name);
-            }
-            // Prevent jumps when selecting one or many lines
-            window.scrollTo(x, y);
-        };
-
-        return function(ev) {
-            var cur_line_id = parseInt(ev.target.id, 10);
-            ev.preventDefault();
-
-            if (ev.shiftKey && prev_line_id) {
-                // Swap selection if needed
-                if (prev_line_id > cur_line_id) {
-                    var tmp = prev_line_id;
-                    prev_line_id = cur_line_id;
-                    cur_line_id = tmp;
-                }
-
-                set_fragment(prev_line_id + "-" + cur_line_id);
-            } else {
-                prev_line_id = cur_line_id;
-
-                set_fragment(cur_line_id);
-            }
-        };
-    }());
-
-    document.addEventListener("click", function(ev) {
-        var helpElem = getHelpElement(false);
-        if (hasClass(ev.target, "help-button")) {
-            displayHelp(true, ev);
-        } else if (hasClass(ev.target, "collapse-toggle")) {
-            collapseDocs(ev.target, "toggle");
-        } else if (hasClass(ev.target.parentNode, "collapse-toggle")) {
-            collapseDocs(ev.target.parentNode, "toggle");
-        } else if (ev.target.tagName === "SPAN" && hasClass(ev.target.parentNode, "line-numbers")) {
-            handleSourceHighlight(ev);
-        } else if (helpElem && hasClass(helpElem, "hidden") === false) {
-            var is_inside_help_popup = ev.target !== helpElem && helpElem.contains(ev.target);
-            if (is_inside_help_popup === false) {
-                addClass(helpElem, "hidden");
-                removeClass(document.body, "blur");
-            }
-        } else {
-            // Making a collapsed element visible on onhashchange seems
-            // too late
-            var a = findParentElement(ev.target, "A");
-            if (a && a.hash) {
-                expandSection(a.hash.replace(/^#/, ""));
-            }
-        }
-    });
-
-    (function() {
-        var x = document.getElementsByClassName("version-selector");
-        if (x.length > 0) {
-            x[0].onchange = function() {
-                var i, match,
-                    url = document.location.href,
-                    stripped = "",
-                    len = window.rootPath.match(/\.\.\//g).length + 1;
-
-                for (i = 0; i < len; ++i) {
-                    match = url.match(/\/[^\/]*$/);
-                    if (i < len - 1) {
-                        stripped = match[0] + stripped;
-                    }
-                    url = url.substring(0, url.length - match[0].length);
-                }
-
-                var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
-                url += "/" + selectedVersion + stripped;
-
-                document.location.href = url;
-            };
-        }
-    }());
-
-    /**
-     * A function to compute the Levenshtein distance between two strings
-     * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
-     * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
-     * This code is an unmodified version of the code written by Marco de Wit
-     * and was found at http://stackoverflow.com/a/18514751/745719
-     */
-    var levenshtein_row2 = [];
-    function levenshtein(s1, s2) {
-        if (s1 === s2) {
-            return 0;
-        }
-        var s1_len = s1.length, s2_len = s2.length;
-        if (s1_len && s2_len) {
-            var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
-            while (i1 < s1_len) {
-                row[i1] = ++i1;
-            }
-            while (i2 < s2_len) {
-                c2 = s2.charCodeAt(i2);
-                a = i2;
-                ++i2;
-                b = i2;
-                for (i1 = 0; i1 < s1_len; ++i1) {
-                    c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
-                    a = row[i1];
-                    b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
-                    row[i1] = b;
-                }
-            }
-            return b;
-        }
-        return s1_len + s2_len;
-    }
-
-    window.initSearch = function(rawSearchIndex) {
-        var MAX_LEV_DISTANCE = 3;
-        var MAX_RESULTS = 200;
-        var GENERICS_DATA = 1;
-        var NAME = 0;
-        var INPUTS_DATA = 0;
-        var OUTPUT_DATA = 1;
-        var NO_TYPE_FILTER = -1;
-        var currentResults, index, searchIndex;
-        var ALIASES = {};
-        var params = getQueryStringParams();
-
-        // Populate search bar with query string search term when provided,
-        // but only if the input bar is empty. This avoid the obnoxious issue
-        // where you start trying to do a search, and the index loads, and
-        // suddenly your search is gone!
-        if (search_input.value === "") {
-            search_input.value = params.search || "";
-        }
-
-        /**
-         * Executes the query and builds an index of results
-         * @param  {[Object]} query      [The user query]
-         * @param  {[type]} searchWords  [The list of search words to query
-         *                                against]
-         * @param  {[type]} filterCrates [Crate to search in if defined]
-         * @return {[type]}              [A search index of results]
-         */
-        function execQuery(query, searchWords, filterCrates) {
-            function itemTypeFromName(typename) {
-                for (var i = 0, len = itemTypes.length; i < len; ++i) {
-                    if (itemTypes[i] === typename) {
-                        return i;
-                    }
-                }
-                return NO_TYPE_FILTER;
-            }
-
-            var valLower = query.query.toLowerCase(),
-                val = valLower,
-                typeFilter = itemTypeFromName(query.type),
-                results = {}, results_in_args = {}, results_returned = {},
-                split = valLower.split("::");
-
-            removeEmptyStringsFromArray(split);
-
-            function transformResults(results, isType) {
-                var out = [];
-                for (var i = 0, len = results.length; i < len; ++i) {
-                    if (results[i].id > -1) {
-                        var obj = searchIndex[results[i].id];
-                        obj.lev = results[i].lev;
-                        if (isType !== true || obj.type) {
-                            var res = buildHrefAndPath(obj);
-                            obj.displayPath = pathSplitter(res[0]);
-                            obj.fullPath = obj.displayPath + obj.name;
-                            // To be sure than it some items aren't considered as duplicate.
-                            obj.fullPath += "|" + obj.ty;
-                            obj.href = res[1];
-                            out.push(obj);
-                            if (out.length >= MAX_RESULTS) {
-                                break;
-                            }
-                        }
-                    }
-                }
-                return out;
-            }
-
-            function sortResults(results, isType) {
-                var ar = [];
-                for (var entry in results) {
-                    if (hasOwnProperty(results, entry)) {
-                        ar.push(results[entry]);
-                    }
-                }
-                results = ar;
-                var i, len, result;
-                for (i = 0, len = results.length; i < len; ++i) {
-                    result = results[i];
-                    result.word = searchWords[result.id];
-                    result.item = searchIndex[result.id] || {};
-                }
-                // if there are no results then return to default and fail
-                if (results.length === 0) {
-                    return [];
-                }
-
-                results.sort(function(aaa, bbb) {
-                    var a, b;
-
-                    // sort by exact match with regard to the last word (mismatch goes later)
-                    a = (aaa.word !== val);
-                    b = (bbb.word !== val);
-                    if (a !== b) { return a - b; }
-
-                    // Sort by non levenshtein results and then levenshtein results by the distance
-                    // (less changes required to match means higher rankings)
-                    a = (aaa.lev);
-                    b = (bbb.lev);
-                    if (a !== b) { return a - b; }
-
-                    // sort by crate (non-current crate goes later)
-                    a = (aaa.item.crate !== window.currentCrate);
-                    b = (bbb.item.crate !== window.currentCrate);
-                    if (a !== b) { return a - b; }
-
-                    // sort by item name length (longer goes later)
-                    a = aaa.word.length;
-                    b = bbb.word.length;
-                    if (a !== b) { return a - b; }
-
-                    // sort by item name (lexicographically larger goes later)
-                    a = aaa.word;
-                    b = bbb.word;
-                    if (a !== b) { return (a > b ? +1 : -1); }
-
-                    // sort by index of keyword in item name (no literal occurrence goes later)
-                    a = (aaa.index < 0);
-                    b = (bbb.index < 0);
-                    if (a !== b) { return a - b; }
-                    // (later literal occurrence, if any, goes later)
-                    a = aaa.index;
-                    b = bbb.index;
-                    if (a !== b) { return a - b; }
-
-                    // special precedence for primitive and keyword pages
-                    if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
-                        (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
-                        return -1;
-                    }
-                    if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
-                        (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
-                        return 1;
-                    }
-
-                    // sort by description (no description goes later)
-                    a = (aaa.item.desc === "");
-                    b = (bbb.item.desc === "");
-                    if (a !== b) { return a - b; }
-
-                    // sort by type (later occurrence in `itemTypes` goes later)
-                    a = aaa.item.ty;
-                    b = bbb.item.ty;
-                    if (a !== b) { return a - b; }
-
-                    // sort by path (lexicographically larger goes later)
-                    a = aaa.item.path;
-                    b = bbb.item.path;
-                    if (a !== b) { return (a > b ? +1 : -1); }
-
-                    // que sera, sera
-                    return 0;
-                });
-
-                for (i = 0, len = results.length; i < len; ++i) {
-                    var result = results[i];
-
-                    // this validation does not make sense when searching by types
-                    if (result.dontValidate) {
-                        continue;
-                    }
-                    var name = result.item.name.toLowerCase(),
-                        path = result.item.path.toLowerCase(),
-                        parent = result.item.parent;
-
-                    if (isType !== true &&
-                        validateResult(name, path, split, parent) === false)
-                    {
-                        result.id = -1;
-                    }
-                }
-                return transformResults(results);
-            }
-
-            function extractGenerics(val) {
-                val = val.toLowerCase();
-                if (val.indexOf("<") !== -1) {
-                    var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
-                    return {
-                        name: val.substring(0, val.indexOf("<")),
-                        generics: values.split(/\s*,\s*/),
-                    };
-                }
-                return {
-                    name: val,
-                    generics: [],
-                };
-            }
-
-            function getObjectNameFromId(id) {
-                if (typeof id === "number") {
-                    return searchIndex[id].name;
-                }
-                return id;
-            }
-
-            function checkGenerics(obj, val) {
-                // The names match, but we need to be sure that all generics kinda
-                // match as well.
-                var tmp_lev, elem_name;
-                if (val.generics.length > 0) {
-                    if (obj.length > GENERICS_DATA &&
-                          obj[GENERICS_DATA].length >= val.generics.length) {
-                        var elems = Object.create(null);
-                        var elength = object[GENERICS_DATA].length;
-                        for (var x = 0; x < elength; ++x) {
-                            elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
-                        }
-                        var total = 0;
-                        var done = 0;
-                        // We need to find the type that matches the most to remove it in order
-                        // to move forward.
-                        var vlength = val.generics.length;
-                        for (x = 0; x < vlength; ++x) {
-                            var lev = MAX_LEV_DISTANCE + 1;
-                            var firstGeneric = getObjectNameFromId(val.generics[x]);
-                            var match = null;
-                            if (elems[firstGeneric]) {
-                                match = firstGeneric;
-                                lev = 0;
-                            } else {
-                                for (elem_name in elems) {
-                                    tmp_lev = levenshtein(elem_name, firstGeneric);
-                                    if (tmp_lev < lev) {
-                                        lev = tmp_lev;
-                                        match = elem_name;
-                                    }
-                                }
-                            }
-                            if (match !== null) {
-                                elems[match] -= 1;
-                                if (elems[match] == 0) {
-                                    delete elems[match];
-                                }
-                                total += lev;
-                                done += 1;
-                            } else {
-                                return MAX_LEV_DISTANCE + 1;
-                            }
-                        }
-                        return Math.ceil(total / done);
-                    }
-                }
-                return MAX_LEV_DISTANCE + 1;
-            }
-
-            // Check for type name and type generics (if any).
-            function checkType(obj, val, literalSearch) {
-                var lev_distance = MAX_LEV_DISTANCE + 1;
-                var len, x, firstGeneric;
-                if (obj[NAME] === val.name) {
-                    if (literalSearch === true) {
-                        if (val.generics && val.generics.length !== 0) {
-                            if (obj.length > GENERICS_DATA &&
-                                  obj[GENERICS_DATA].length >= val.generics.length) {
-                                var elems = Object.create(null);
-                                len = obj[GENERICS_DATA].length;
-                                for (x = 0; x < len; ++x) {
-                                    elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
-                                }
-
-                                var allFound = true;
-                                len = val.generics.length;
-                                for (x = 0; x < len; ++x) {
-                                    firstGeneric = getObjectNameFromId(val.generics[x]);
-                                    if (elems[firstGeneric]) {
-                                        elems[firstGeneric] -= 1;
-                                    } else {
-                                        allFound = false;
-                                        break;
-                                    }
-                                }
-                                if (allFound === true) {
-                                    return true;
-                                }
-                            } else {
-                                return false;
-                            }
-                        }
-                        return true;
-                    }
-                    // If the type has generics but don't match, then it won't return at this point.
-                    // Otherwise, `checkGenerics` will return 0 and it'll return.
-                    if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
-                        var tmp_lev = checkGenerics(obj, val);
-                        if (tmp_lev <= MAX_LEV_DISTANCE) {
-                            return tmp_lev;
-                        }
-                    } else {
-                        return 0;
-                    }
-                }
-                // Names didn't match so let's check if one of the generic types could.
-                if (literalSearch === true) {
-                     if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                        return obj[GENERICS_DATA].some(
-                            function(name) {
-                                return name === val.name;
-                            });
-                    }
-                    return false;
-                }
-                lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
-                if (lev_distance <= MAX_LEV_DISTANCE) {
-                    // The generics didn't match but the name kinda did so we give it
-                    // a levenshtein distance value that isn't *this* good so it goes
-                    // into the search results but not too high.
-                    lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
-                } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                    // We can check if the type we're looking for is inside the generics!
-                    var olength = obj[GENERICS_DATA].length;
-                    for (x = 0; x < olength; ++x) {
-                        lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
-                                                lev_distance);
-                    }
-                }
-                // Now whatever happens, the returned distance is "less good" so we should mark it
-                // as such, and so we add 1 to the distance to make it "less good".
-                return lev_distance + 1;
-            }
-
-            function findArg(obj, val, literalSearch, typeFilter) {
-                var lev_distance = MAX_LEV_DISTANCE + 1;
-
-                if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) {
-                    var length = obj.type[INPUTS_DATA].length;
-                    for (var i = 0; i < length; i++) {
-                        var tmp = obj.type[INPUTS_DATA][i];
-                        if (typePassesFilter(typeFilter, tmp[1]) === false) {
-                            continue;
-                        }
-                        tmp = checkType(tmp, val, literalSearch);
-                        if (literalSearch === true) {
-                            if (tmp === true) {
-                                return true;
-                            }
-                            continue;
-                        }
-                        lev_distance = Math.min(tmp, lev_distance);
-                        if (lev_distance === 0) {
-                            return 0;
-                        }
-                    }
-                }
-                return literalSearch === true ? false : lev_distance;
-            }
-
-            function checkReturned(obj, val, literalSearch, typeFilter) {
-                var lev_distance = MAX_LEV_DISTANCE + 1;
-
-                if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
-                    var ret = obj.type[OUTPUT_DATA];
-                    if (typeof ret[0] === "string") {
-                        ret = [ret];
-                    }
-                    for (var x = 0, len = ret.length; x < len; ++x) {
-                        var tmp = ret[x];
-                        if (typePassesFilter(typeFilter, tmp[1]) === false) {
-                            continue;
-                        }
-                        tmp = checkType(tmp, val, literalSearch);
-                        if (literalSearch === true) {
-                            if (tmp === true) {
-                                return true;
-                            }
-                            continue;
-                        }
-                        lev_distance = Math.min(tmp, lev_distance);
-                        if (lev_distance === 0) {
-                            return 0;
-                        }
-                    }
-                }
-                return literalSearch === true ? false : lev_distance;
-            }
-
-            function checkPath(contains, lastElem, ty) {
-                if (contains.length === 0) {
-                    return 0;
-                }
-                var ret_lev = MAX_LEV_DISTANCE + 1;
-                var path = ty.path.split("::");
-
-                if (ty.parent && ty.parent.name) {
-                    path.push(ty.parent.name.toLowerCase());
-                }
-
-                var length = path.length;
-                var clength = contains.length;
-                if (clength > length) {
-                    return MAX_LEV_DISTANCE + 1;
-                }
-                for (var i = 0; i < length; ++i) {
-                    if (i + clength > length) {
-                        break;
-                    }
-                    var lev_total = 0;
-                    var aborted = false;
-                    for (var x = 0; x < clength; ++x) {
-                        var lev = levenshtein(path[i + x], contains[x]);
-                        if (lev > MAX_LEV_DISTANCE) {
-                            aborted = true;
-                            break;
-                        }
-                        lev_total += lev;
-                    }
-                    if (aborted === false) {
-                        ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
-                    }
-                }
-                return ret_lev;
-            }
-
-            function typePassesFilter(filter, type) {
-                // No filter
-                if (filter <= NO_TYPE_FILTER) return true;
-
-                // Exact match
-                if (filter === type) return true;
-
-                // Match related items
-                var name = itemTypes[type];
-                switch (itemTypes[filter]) {
-                    case "constant":
-                        return name === "associatedconstant";
-                    case "fn":
-                        return name === "method" || name === "tymethod";
-                    case "type":
-                        return name === "primitive" || name === "associatedtype";
-                    case "trait":
-                        return name === "traitalias";
-                }
-
-                // No match
-                return false;
-            }
-
-            function createAliasFromItem(item) {
-                return {
-                    crate: item.crate,
-                    name: item.name,
-                    path: item.path,
-                    desc: item.desc,
-                    ty: item.ty,
-                    parent: item.parent,
-                    type: item.type,
-                    is_alias: true,
-                };
-            }
-
-            function handleAliases(ret, query, filterCrates) {
-                // We separate aliases and crate aliases because we want to have current crate
-                // aliases to be before the others in the displayed results.
-                var aliases = [];
-                var crateAliases = [];
-                if (filterCrates !== undefined) {
-                    if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
-                        var query_aliases = ALIASES[filterCrates][query.search];
-                        var len = query_aliases.length;
-                        for (var i = 0; i < len; ++i) {
-                            aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
-                        }
-                    }
-                } else {
-                    Object.keys(ALIASES).forEach(function(crate) {
-                        if (ALIASES[crate][query.search]) {
-                            var pushTo = crate === window.currentCrate ? crateAliases : aliases;
-                            var query_aliases = ALIASES[crate][query.search];
-                            var len = query_aliases.length;
-                            for (var i = 0; i < len; ++i) {
-                                pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
-                            }
-                        }
-                    });
-                }
-
-                var sortFunc = function(aaa, bbb) {
-                    if (aaa.path < bbb.path) {
-                        return 1;
-                    } else if (aaa.path === bbb.path) {
-                        return 0;
-                    }
-                    return -1;
-                };
-                crateAliases.sort(sortFunc);
-                aliases.sort(sortFunc);
-
-                var pushFunc = function(alias) {
-                    alias.alias = query.raw;
-                    var res = buildHrefAndPath(alias);
-                    alias.displayPath = pathSplitter(res[0]);
-                    alias.fullPath = alias.displayPath + alias.name;
-                    alias.href = res[1];
-
-                    ret.others.unshift(alias);
-                    if (ret.others.length > MAX_RESULTS) {
-                        ret.others.pop();
-                    }
-                };
-                onEach(aliases, pushFunc);
-                onEach(crateAliases, pushFunc);
-            }
-
-            // quoted values mean literal search
-            var nSearchWords = searchWords.length;
-            var i, it;
-            var ty;
-            var fullId;
-            var returned;
-            var in_args;
-            var len;
-            if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
-                val.charAt(val.length - 1) === val.charAt(0))
-            {
-                val = extractGenerics(val.substr(1, val.length - 2));
-                for (i = 0; i < nSearchWords; ++i) {
-                    if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
-                        continue;
-                    }
-                    in_args = findArg(searchIndex[i], val, true, typeFilter);
-                    returned = checkReturned(searchIndex[i], val, true, typeFilter);
-                    ty = searchIndex[i];
-                    fullId = ty.id;
-
-                    if (searchWords[i] === val.name
-                        && typePassesFilter(typeFilter, searchIndex[i].ty)
-                        && results[fullId] === undefined) {
-                        results[fullId] = {
-                            id: i,
-                            index: -1,
-                            dontValidate: true,
-                        };
-                    }
-                    if (in_args === true && results_in_args[fullId] === undefined) {
-                        results_in_args[fullId] = {
-                            id: i,
-                            index: -1,
-                            dontValidate: true,
-                        };
-                    }
-                    if (returned === true && results_returned[fullId] === undefined) {
-                        results_returned[fullId] = {
-                            id: i,
-                            index: -1,
-                            dontValidate: true,
-                        };
-                    }
-                }
-                query.inputs = [val];
-                query.output = val;
-                query.search = val;
-            // searching by type
-            } else if (val.search("->") > -1) {
-                var trimmer = function(s) { return s.trim(); };
-                var parts = val.split("->").map(trimmer);
-                var input = parts[0];
-                // sort inputs so that order does not matter
-                var inputs = input.split(",").map(trimmer).sort();
-                for (i = 0, len = inputs.length; i < len; ++i) {
-                    inputs[i] = extractGenerics(inputs[i]);
-                }
-                var output = extractGenerics(parts[1]);
-
-                for (i = 0; i < nSearchWords; ++i) {
-                    if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
-                        continue;
-                    }
-                    var type = searchIndex[i].type;
-                    ty = searchIndex[i];
-                    if (!type) {
-                        continue;
-                    }
-                    fullId = ty.id;
-
-                    returned = checkReturned(ty, output, true, NO_TYPE_FILTER);
-                    if (output.name === "*" || returned === true) {
-                        in_args = false;
-                        var is_module = false;
-
-                        if (input === "*") {
-                            is_module = true;
-                        } else {
-                            var allFound = true;
-                            for (it = 0, len = inputs.length; allFound === true && it < len; it++) {
-                                allFound = checkType(type, inputs[it], true);
-                            }
-                            in_args = allFound;
-                        }
-                        if (in_args === true) {
-                            results_in_args[fullId] = {
-                                id: i,
-                                index: -1,
-                                dontValidate: true,
-                            };
-                        }
-                        if (returned === true) {
-                            results_returned[fullId] = {
-                                id: i,
-                                index: -1,
-                                dontValidate: true,
-                            };
-                        }
-                        if (is_module === true) {
-                            results[fullId] = {
-                                id: i,
-                                index: -1,
-                                dontValidate: true,
-                            };
-                        }
-                    }
-                }
-                query.inputs = inputs.map(function(input) {
-                    return input.name;
-                });
-                query.output = output.name;
-            } else {
-                query.inputs = [val];
-                query.output = val;
-                query.search = val;
-                // gather matching search results up to a certain maximum
-                val = val.replace(/\_/g, "");
-
-                var valGenerics = extractGenerics(val);
-
-                var paths = valLower.split("::");
-                removeEmptyStringsFromArray(paths);
-                val = paths[paths.length - 1];
-                var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
-
-                var lev, j;
-                for (j = 0; j < nSearchWords; ++j) {
-                    ty = searchIndex[j];
-                    if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) {
-                        continue;
-                    }
-                    var lev_add = 0;
-                    if (paths.length > 1) {
-                        lev = checkPath(contains, paths[paths.length - 1], ty);
-                        if (lev > MAX_LEV_DISTANCE) {
-                            continue;
-                        } else if (lev > 0) {
-                            lev_add = lev / 10;
-                        }
-                    }
-
-                    returned = MAX_LEV_DISTANCE + 1;
-                    in_args = MAX_LEV_DISTANCE + 1;
-                    var index = -1;
-                    // we want lev results to go lower than others
-                    lev = MAX_LEV_DISTANCE + 1;
-                    fullId = ty.id;
-
-                    if (searchWords[j].indexOf(split[i]) > -1 ||
-                        searchWords[j].indexOf(val) > -1 ||
-                        ty.normalizedName.indexOf(val) > -1)
-                    {
-                        // filter type: ... queries
-                        if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
-                            index = ty.normalizedName.indexOf(val);
-                        }
-                    }
-                    if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
-                        if (typePassesFilter(typeFilter, ty.ty) === false) {
-                            lev = MAX_LEV_DISTANCE + 1;
-                        } else {
-                            lev += 1;
-                        }
-                    }
-                    in_args = findArg(ty, valGenerics, false, typeFilter);
-                    returned = checkReturned(ty, valGenerics, false, typeFilter);
-
-                    lev += lev_add;
-                    if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
-                        if (val.length < 6) {
-                            lev -= 1;
-                        } else {
-                            lev = 0;
-                        }
-                    }
-                    if (in_args <= MAX_LEV_DISTANCE) {
-                        if (results_in_args[fullId] === undefined) {
-                            results_in_args[fullId] = {
-                                id: j,
-                                index: index,
-                                lev: in_args,
-                            };
-                        }
-                        results_in_args[fullId].lev =
-                            Math.min(results_in_args[fullId].lev, in_args);
-                    }
-                    if (returned <= MAX_LEV_DISTANCE) {
-                        if (results_returned[fullId] === undefined) {
-                            results_returned[fullId] = {
-                                id: j,
-                                index: index,
-                                lev: returned,
-                            };
-                        }
-                        results_returned[fullId].lev =
-                            Math.min(results_returned[fullId].lev, returned);
-                    }
-                    if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
-                        if (index !== -1 && paths.length < 2) {
-                            lev = 0;
-                        }
-                        if (results[fullId] === undefined) {
-                            results[fullId] = {
-                                id: j,
-                                index: index,
-                                lev: lev,
-                            };
-                        }
-                        results[fullId].lev = Math.min(results[fullId].lev, lev);
-                    }
-                }
-            }
-
-            var ret = {
-                "in_args": sortResults(results_in_args, true),
-                "returned": sortResults(results_returned, true),
-                "others": sortResults(results),
-            };
-            handleAliases(ret, query, filterCrates);
-            return ret;
-        }
-
-        /**
-         * Validate performs the following boolean logic. For example:
-         * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
-         * exists in (name || path || parent) OR => ("file" && "open") exists in
-         * (name || path )
-         *
-         * This could be written functionally, but I wanted to minimise
-         * functions on stack.
-         *
-         * @param  {[string]} name   [The name of the result]
-         * @param  {[string]} path   [The path of the result]
-         * @param  {[string]} keys   [The keys to be used (["file", "open"])]
-         * @param  {[object]} parent [The parent of the result]
-         * @return {[boolean]}       [Whether the result is valid or not]
-         */
-        function validateResult(name, path, keys, parent) {
-            for (var i = 0, len = keys.length; i < len; ++i) {
-                // each check is for validation so we negate the conditions and invalidate
-                if (!(
-                    // check for an exact name match
-                    name.indexOf(keys[i]) > -1 ||
-                    // then an exact path match
-                    path.indexOf(keys[i]) > -1 ||
-                    // next if there is a parent, check for exact parent match
-                    (parent !== undefined && parent.name !== undefined &&
-                        parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
-                    // lastly check to see if the name was a levenshtein match
-                    levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        function getQuery(raw) {
-            var matches, type, query;
-            query = raw;
-
-            matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
-            if (matches) {
-                type = matches[1].replace(/^const$/, "constant");
-                query = query.substring(matches[0].length);
-            }
-
-            return {
-                raw: raw,
-                query: query,
-                type: type,
-                id: query + type
-            };
-        }
-
-        function initSearchNav() {
-            var hoverTimeout;
-
-            var click_func = function(e) {
-                var el = e.target;
-                // to retrieve the real "owner" of the event.
-                while (el.tagName !== "TR") {
-                    el = el.parentNode;
-                }
-                var dst = e.target.getElementsByTagName("a");
-                if (dst.length < 1) {
-                    return;
-                }
-                dst = dst[0];
-                if (window.location.pathname === dst.pathname) {
-                    hideSearchResults();
-                    document.location.href = dst.href;
-                }
-            };
-            var mouseover_func = function(e) {
-                if (mouseMovedAfterSearch) {
-                    var el = e.target;
-                    // to retrieve the real "owner" of the event.
-                    while (el.tagName !== "TR") {
-                        el = el.parentNode;
-                    }
-                    clearTimeout(hoverTimeout);
-                    hoverTimeout = setTimeout(function() {
-                        onEachLazy(document.getElementsByClassName("search-results"), function(e) {
-                            onEachLazy(e.getElementsByClassName("result"), function(i_e) {
-                                removeClass(i_e, "highlighted");
-                            });
-                        });
-                        addClass(el, "highlighted");
-                    }, 20);
-                }
-            };
-            onEachLazy(document.getElementsByClassName("search-results"), function(e) {
-                onEachLazy(e.getElementsByClassName("result"), function(i_e) {
-                    i_e.onclick = click_func;
-                    i_e.onmouseover = mouseover_func;
-                });
-            });
-
-            search_input.onkeydown = function(e) {
-                // "actives" references the currently highlighted item in each search tab.
-                // Each array in "actives" represents a tab.
-                var actives = [[], [], []];
-                // "current" is used to know which tab we're looking into.
-                var current = 0;
-                onEachLazy(document.getElementById("results").childNodes, function(e) {
-                    onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) {
-                        actives[current].push(h_e);
-                    });
-                    current += 1;
-                });
-
-                if (e.which === 38) { // up
-                    if (e.ctrlKey) { // Going through result tabs.
-                        printTab(currentTab > 0 ? currentTab - 1 : 2);
-                    } else {
-                        if (!actives[currentTab].length ||
-                            !actives[currentTab][0].previousElementSibling) {
-                            return;
-                        }
-                        addClass(actives[currentTab][0].previousElementSibling, "highlighted");
-                        removeClass(actives[currentTab][0], "highlighted");
-                    }
-                    e.preventDefault();
-                } else if (e.which === 40) { // down
-                    if (e.ctrlKey) { // Going through result tabs.
-                        printTab(currentTab > 1 ? 0 : currentTab + 1);
-                    } else if (!actives[currentTab].length) {
-                        var results = document.getElementById("results").childNodes;
-                        if (results.length > 0) {
-                            var res = results[currentTab].getElementsByClassName("result");
-                            if (res.length > 0) {
-                                addClass(res[0], "highlighted");
-                            }
-                        }
-                    } else if (actives[currentTab][0].nextElementSibling) {
-                        addClass(actives[currentTab][0].nextElementSibling, "highlighted");
-                        removeClass(actives[currentTab][0], "highlighted");
-                    }
-                    e.preventDefault();
-                } else if (e.which === 13) { // return
-                    if (actives[currentTab].length) {
-                        document.location.href =
-                            actives[currentTab][0].getElementsByTagName("a")[0].href;
-                    }
-                } else if (e.which === 16) { // shift
-                    // Does nothing, it's just to avoid losing "focus" on the highlighted element.
-                } else if (actives[currentTab].length > 0) {
-                    removeClass(actives[currentTab][0], "highlighted");
-                }
-            };
-        }
-
-        function buildHrefAndPath(item) {
-            var displayPath;
-            var href;
-            var type = itemTypes[item.ty];
-            var name = item.name;
-            var path = item.path;
-
-            if (type === "mod") {
-                displayPath = path + "::";
-                href = window.rootPath + path.replace(/::/g, "/") + "/" +
-                       name + "/index.html";
-            } else if (type === "primitive" || type === "keyword") {
-                displayPath = "";
-                href = window.rootPath + path.replace(/::/g, "/") +
-                       "/" + type + "." + name + ".html";
-            } else if (type === "externcrate") {
-                displayPath = "";
-                href = window.rootPath + name + "/index.html";
-            } else if (item.parent !== undefined) {
-                var myparent = item.parent;
-                var anchor = "#" + type + "." + name;
-                var parentType = itemTypes[myparent.ty];
-                var pageType = parentType;
-                var pageName = myparent.name;
-
-                if (parentType === "primitive") {
-                    displayPath = myparent.name + "::";
-                } else if (type === "structfield" && parentType === "variant") {
-                    // Structfields belonging to variants are special: the
-                    // final path element is the enum name.
-                    var enumNameIdx = item.path.lastIndexOf("::");
-                    var enumName = item.path.substr(enumNameIdx + 2);
-                    path = item.path.substr(0, enumNameIdx);
-                    displayPath = path + "::" + enumName + "::" + myparent.name + "::";
-                    anchor = "#variant." + myparent.name + ".field." + name;
-                    pageType = "enum";
-                    pageName = enumName;
-                } else {
-                    displayPath = path + "::" + myparent.name + "::";
-                }
-                href = window.rootPath + path.replace(/::/g, "/") +
-                       "/" + pageType +
-                       "." + pageName +
-                       ".html" + anchor;
-            } else {
-                displayPath = item.path + "::";
-                href = window.rootPath + item.path.replace(/::/g, "/") +
-                       "/" + type + "." + name + ".html";
-            }
-            return [displayPath, href];
-        }
-
-        function escape(content) {
-            var h1 = document.createElement("h1");
-            h1.textContent = content;
-            return h1.innerHTML;
-        }
-
-        function pathSplitter(path) {
-            var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
-            if (tmp.endsWith("<span>")) {
-                return tmp.slice(0, tmp.length - 6);
-            }
-            return tmp;
-        }
-
-        function addTab(array, query, display) {
-            var extraStyle = "";
-            if (display === false) {
-                extraStyle = " style=\"display: none;\"";
-            }
-
-            var output = "";
-            var duplicates = {};
-            var length = 0;
-            if (array.length > 0) {
-                output = "<table class=\"search-results\"" + extraStyle + ">";
-
-                array.forEach(function(item) {
-                    var name, type;
-
-                    name = item.name;
-                    type = itemTypes[item.ty];
-
-                    if (item.is_alias !== true) {
-                        if (duplicates[item.fullPath]) {
-                            return;
-                        }
-                        duplicates[item.fullPath] = true;
-                    }
-                    length += 1;
-
-                    output += "<tr class=\"" + type + " result\"><td>" +
-                              "<a href=\"" + item.href + "\">" +
-                              (item.is_alias === true ?
-                               ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " +
-                                  "class=\"grey\"><i>&nbsp;- see&nbsp;</i></span>") : "") +
-                              item.displayPath + "<span class=\"" + type + "\">" +
-                              name + "</span></a></td><td>" +
-                              "<a href=\"" + item.href + "\">" +
-                              "<span class=\"desc\">" + item.desc +
-                              "&nbsp;</span></a></td></tr>";
-                });
-                output += "</table>";
-            } else {
-                output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" +
-                    "Try on <a href=\"https://duckduckgo.com/?q=" +
-                    encodeURIComponent("rust " + query.query) +
-                    "\">DuckDuckGo</a>?<br/><br/>" +
-                    "Or try looking in one of these:<ul><li>The <a " +
-                    "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
-                    " for technical details about the language.</li><li><a " +
-                    "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " +
-                    "Example</a> for expository code examples.</a></li><li>The <a " +
-                    "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " +
-                    "introductions to language features and the language itself.</li><li><a " +
-                    "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
-                    " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>";
-            }
-            return [output, length];
-        }
-
-        function makeTabHeader(tabNb, text, nbElems) {
-            if (currentTab === tabNb) {
-                return "<button class=\"selected\">" + text +
-                       " <div class=\"count\">(" + nbElems + ")</div></button>";
-            }
-            return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
-        }
-
-        function showResults(results) {
-            var search = getSearchElement();
-            if (results.others.length === 1
-                && getSettingValue("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);
-
-            currentResults = query.id;
-
-            var ret_others = addTab(results.others, query);
-            var ret_in_args = addTab(results.in_args, query, false);
-            var ret_returned = addTab(results.returned, query, false);
-
-            // Navigate to the relevant tab if the current tab is empty, like in case users search
-            // for "-> String". If they had selected another tab previously, they have to click on
-            // it again.
-            if ((currentTab === 0 && ret_others[1] === 0) ||
-                    (currentTab === 1 && ret_in_args[1] === 0) ||
-                    (currentTab === 2 && ret_returned[1] === 0)) {
-                if (ret_others[1] !== 0) {
-                    currentTab = 0;
-                } else if (ret_in_args[1] !== 0) {
-                    currentTab = 1;
-                } else if (ret_returned[1] !== 0) {
-                    currentTab = 2;
-                }
-            }
+    }
 
-            var output = "<h1>Results for " + escape(query.query) +
-                (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
-                "<div id=\"titles\">" +
-                makeTabHeader(0, "In Names", ret_others[1]) +
-                makeTabHeader(1, "In Parameters", ret_in_args[1]) +
-                makeTabHeader(2, "In Return Types", ret_returned[1]) +
-                "</div><div id=\"results\">" +
-                ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
-
-            search.innerHTML = output;
-            showSearchResults(search);
-            initSearchNav();
-            var elems = document.getElementById("titles").childNodes;
-            elems[0].onclick = function() { printTab(0); };
-            elems[1].onclick = function() { printTab(1); };
-            elems[2].onclick = function() { printTab(2); };
-            printTab(currentTab);
-        }
-
-        function execSearch(query, searchWords, filterCrates) {
-            function getSmallest(arrays, positions, notDuplicates) {
-                var start = null;
-
-                for (var it = 0, len = positions.length; it < len; ++it) {
-                    if (arrays[it].length > positions[it] &&
-                        (start === null || start > arrays[it][positions[it]].lev) &&
-                        !notDuplicates[arrays[it][positions[it]].fullPath]) {
-                        start = arrays[it][positions[it]].lev;
-                    }
-                }
-                return start;
+    function findParentElement(elem, tagName) {
+        do {
+            if (elem && elem.tagName === tagName) {
+                return elem;
             }
+            elem = elem.parentNode;
+        } while (elem);
+        return null;
+    }
 
-            function mergeArrays(arrays) {
-                var ret = [];
-                var positions = [];
-                var notDuplicates = {};
+    document.addEventListener("keypress", handleShortcut);
+    document.addEventListener("keydown", handleShortcut);
 
-                for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) {
-                    positions.push(0);
-                }
-                while (ret.length < MAX_RESULTS) {
-                    var smallest = getSmallest(arrays, positions, notDuplicates);
+    var handleSourceHighlight = (function() {
+        var prev_line_id = 0;
 
-                    if (smallest === null) {
-                        break;
-                    }
-                    for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) {
-                        if (arrays[x].length > positions[x] &&
-                                arrays[x][positions[x]].lev === smallest &&
-                                !notDuplicates[arrays[x][positions[x]].fullPath]) {
-                            ret.push(arrays[x][positions[x]]);
-                            notDuplicates[arrays[x][positions[x]].fullPath] = true;
-                            positions[x] += 1;
-                        }
-                    }
-                }
-                return ret;
+        var set_fragment = function(name) {
+            var x = window.scrollX,
+                y = window.scrollY;
+            if (searchState.browserSupportsHistoryApi()) {
+                history.replaceState(null, null, "#" + name);
+                highlightSourceLines();
+            } else {
+                location.replace("#" + name);
             }
+            // Prevent jumps when selecting one or many lines
+            window.scrollTo(x, y);
+        };
 
-            var queries = query.raw.split(",");
-            var results = {
-                "in_args": [],
-                "returned": [],
-                "others": [],
-            };
-
-            for (var i = 0, len = queries.length; i < len; ++i) {
-                query = queries[i].trim();
-                if (query.length !== 0) {
-                    var tmp = execQuery(getQuery(query), searchWords, filterCrates);
+        return function(ev) {
+            var cur_line_id = parseInt(ev.target.id, 10);
+            ev.preventDefault();
 
-                    results.in_args.push(tmp.in_args);
-                    results.returned.push(tmp.returned);
-                    results.others.push(tmp.others);
+            if (ev.shiftKey && prev_line_id) {
+                // Swap selection if needed
+                if (prev_line_id > cur_line_id) {
+                    var tmp = prev_line_id;
+                    prev_line_id = cur_line_id;
+                    cur_line_id = tmp;
                 }
-            }
-            if (queries.length > 1) {
-                return {
-                    "in_args": mergeArrays(results.in_args),
-                    "returned": mergeArrays(results.returned),
-                    "others": mergeArrays(results.others),
-                };
-            }
-            return {
-                "in_args": results.in_args[0],
-                "returned": results.returned[0],
-                "others": results.others[0],
-            };
-        }
-
-        function getFilterCrates() {
-            var elem = document.getElementById("crate-search");
-
-            if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) {
-                return elem.value;
-            }
-            return undefined;
-        }
 
-        function search(e, forced) {
-            var params = getQueryStringParams();
-            var query = getQuery(search_input.value.trim());
+                set_fragment(prev_line_id + "-" + cur_line_id);
+            } else {
+                prev_line_id = cur_line_id;
 
-            if (e) {
-                e.preventDefault();
+                set_fragment(cur_line_id);
             }
+        };
+    }());
 
-            if (query.query.length === 0) {
-                return;
-            }
-            if (forced !== true && query.id === currentResults) {
-                if (query.query.length > 0) {
-                    putBackSearch(search_input);
-                }
-                return;
+    document.addEventListener("click", function(ev) {
+        var helpElem = getHelpElement(false);
+        if (hasClass(ev.target, "help-button")) {
+            displayHelp(true, ev);
+        } else if (hasClass(ev.target, "collapse-toggle")) {
+            collapseDocs(ev.target, "toggle");
+        } else if (hasClass(ev.target.parentNode, "collapse-toggle")) {
+            collapseDocs(ev.target.parentNode, "toggle");
+        } else if (ev.target.tagName === "SPAN" && hasClass(ev.target.parentNode, "line-numbers")) {
+            handleSourceHighlight(ev);
+        } else if (helpElem && hasClass(helpElem, "hidden") === false) {
+            var is_inside_help_popup = ev.target !== helpElem && helpElem.contains(ev.target);
+            if (is_inside_help_popup === false) {
+                addClass(helpElem, "hidden");
+                removeClass(document.body, "blur");
             }
-
-            // Update document title to maintain a meaningful browser history
-            searchTitle = "Results for " + query.query + " - Rust";
-
-            // Because searching is incremental by character, only the most
-            // recent search query is added to the browser history.
-            if (browserSupportsHistoryApi()) {
-                var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) +
-                    window.location.hash;
-                if (!history.state && !params.search) {
-                    history.pushState(query, "", newURL);
-                } else {
-                    history.replaceState(query, "", newURL);
-                }
+        } else {
+            // Making a collapsed element visible on onhashchange seems
+            // too late
+            var a = findParentElement(ev.target, "A");
+            if (a && a.hash) {
+                expandSection(a.hash.replace(/^#/, ""));
             }
+        }
+    });
 
-            var filterCrates = getFilterCrates();
-            showResults(execSearch(query, index, filterCrates));
-        }
-
-        function buildIndex(rawSearchIndex) {
-            searchIndex = [];
-            var searchWords = [];
-            var i, word;
-            var currentIndex = 0;
-            var id = 0;
-
-            for (var crate in rawSearchIndex) {
-                if (!hasOwnProperty(rawSearchIndex, crate)) { continue; }
-
-                var crateSize = 0;
-
-                searchWords.push(crate);
-                var normalizedName = crate.indexOf("_") === -1
-                    ? crate
-                    : crate.replace(/_/g, "");
-                // This object should have exactly the same set of fields as the "row"
-                // object defined below. Your JavaScript runtime will thank you.
-                // https://mathiasbynens.be/notes/shapes-ics
-                var crateRow = {
-                    crate: crate,
-                    ty: 1, // == ExternCrate
-                    name: crate,
-                    path: "",
-                    desc: rawSearchIndex[crate].doc,
-                    parent: undefined,
-                    type: null,
-                    id: id,
-                    normalizedName: normalizedName,
-                };
-                id += 1;
-                searchIndex.push(crateRow);
-                currentIndex += 1;
-
-                // an array of (Number) item types
-                var itemTypes = rawSearchIndex[crate].t;
-                // an array of (String) item names
-                var itemNames = rawSearchIndex[crate].n;
-                // an array of (String) full paths (or empty string for previous path)
-                var itemPaths = rawSearchIndex[crate].q;
-                // an array of (String) descriptions
-                var itemDescs = rawSearchIndex[crate].d;
-                // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
-                var itemParentIdxs = rawSearchIndex[crate].i;
-                // an array of (Object | null) the type of the function, if any
-                var itemFunctionSearchTypes = rawSearchIndex[crate].f;
-                // an array of [(Number) item type,
-                //              (String) name]
-                var paths = rawSearchIndex[crate].p;
-                // a array of [(String) alias name
-                //             [Number] index to items]
-                var aliases = rawSearchIndex[crate].a;
-
-                // convert `rawPaths` entries into object form
-                var len = paths.length;
-                for (i = 0; i < len; ++i) {
-                    paths[i] = {ty: paths[i][0], name: paths[i][1]};
-                }
+    (function() {
+        var x = document.getElementsByClassName("version-selector");
+        if (x.length > 0) {
+            x[0].onchange = function() {
+                var i, match,
+                    url = document.location.href,
+                    stripped = "",
+                    len = window.rootPath.match(/\.\.\//g).length + 1;
 
-                // convert `item*` into an object form, and construct word indices.
-                //
-                // before any analysis is performed lets gather the search terms to
-                // search against apart from the rest of the data.  This is a quick
-                // operation that is cached for the life of the page state so that
-                // all other search operations have access to this cached data for
-                // faster analysis operations
-                len = itemTypes.length;
-                var lastPath = "";
                 for (i = 0; i < len; ++i) {
-                    // This object should have exactly the same set of fields as the "crateRow"
-                    // object defined above.
-                    if (typeof itemNames[i] === "string") {
-                        word = itemNames[i].toLowerCase();
-                        searchWords.push(word);
-                    } else {
-                        word = "";
-                        searchWords.push("");
-                    }
-                    var normalizedName = word.indexOf("_") === -1
-                        ? word
-                        : word.replace(/_/g, "");
-                    var row = {
-                        crate: crate,
-                        ty: itemTypes[i],
-                        name: itemNames[i],
-                        path: itemPaths[i] ? itemPaths[i] : lastPath,
-                        desc: itemDescs[i],
-                        parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
-                        type: itemFunctionSearchTypes[i],
-                        id: id,
-                        normalizedName: normalizedName,
-                    };
-                    id += 1;
-                    searchIndex.push(row);
-                    lastPath = row.path;
-                    crateSize += 1;
-                }
-
-                if (aliases) {
-                    ALIASES[crate] = {};
-                    var j, local_aliases;
-                    for (var alias_name in aliases) {
-                        if (!aliases.hasOwnProperty(alias_name)) { continue; }
-
-                        if (!ALIASES[crate].hasOwnProperty(alias_name)) {
-                            ALIASES[crate][alias_name] = [];
-                        }
-                        local_aliases = aliases[alias_name];
-                        for (j = 0, len = local_aliases.length; j < len; ++j) {
-                            ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
-                        }
-                    }
-                }
-                currentIndex += crateSize;
-            }
-            return searchWords;
-        }
-
-        function registerSearchEvents() {
-            var searchAfter500ms = function() {
-                clearInputTimeout();
-                if (search_input.value.length === 0) {
-                    if (browserSupportsHistoryApi()) {
-                        history.replaceState("", window.currentCrate + " - Rust",
-                            getNakedUrl() + window.location.hash);
+                    match = url.match(/\/[^\/]*$/);
+                    if (i < len - 1) {
+                        stripped = match[0] + stripped;
                     }
-                    hideSearchResults();
-                } else {
-                    searchTimeout = setTimeout(search, 500);
-                }
-            };
-            search_input.onkeyup = searchAfter500ms;
-            search_input.oninput = searchAfter500ms;
-            document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
-                e.preventDefault();
-                clearInputTimeout();
-                search();
-            };
-            search_input.onchange = function(e) {
-                if (e.target !== document.activeElement) {
-                    // To prevent doing anything when it's from a blur event.
-                    return;
+                    url = url.substring(0, url.length - match[0].length);
                 }
-                // Do NOT e.preventDefault() here. It will prevent pasting.
-                clearInputTimeout();
-                // zero-timeout necessary here because at the time of event handler execution the
-                // pasted content is not in the input field yet. Shouldn’t make any difference for
-                // change, though.
-                setTimeout(search, 0);
-            };
-            search_input.onpaste = search_input.onchange;
-
-            var selectCrate = document.getElementById("crate-search");
-            if (selectCrate) {
-                selectCrate.onchange = function() {
-                    updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value);
-                    search(undefined, true);
-                };
-            }
 
-            // Push and pop states are used to add search results to the browser
-            // history.
-            if (browserSupportsHistoryApi()) {
-                // Store the previous <title> so we can revert back to it later.
-                var previousTitle = document.title;
-
-                window.addEventListener("popstate", function(e) {
-                    var params = getQueryStringParams();
-                    // Revert to the previous title manually since the History
-                    // API ignores the title parameter.
-                    document.title = previousTitle;
-                    // When browsing forward to search results the previous
-                    // search will be repeated, so the currentResults are
-                    // cleared to ensure the search is successful.
-                    currentResults = null;
-                    // Synchronize search bar with query string state and
-                    // perform the search. This will empty the bar if there's
-                    // nothing there, which lets you really go back to a
-                    // previous state with nothing in the bar.
-                    if (params.search && params.search.length > 0) {
-                        search_input.value = params.search;
-                        // Some browsers fire "onpopstate" for every page load
-                        // (Chrome), while others fire the event only when actually
-                        // popping a state (Firefox), which is why search() is
-                        // called both here and at the end of the startSearch()
-                        // function.
-                        search(e);
-                    } else {
-                        search_input.value = "";
-                        // When browsing back from search results the main page
-                        // visibility must be reset.
-                        hideSearchResults();
-                    }
-                });
-            }
+                var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
+                url += "/" + selectedVersion + stripped;
 
-            // This is required in firefox to avoid this problem: Navigating to a search result
-            // with the keyboard, hitting enter, and then hitting back would take you back to
-            // the doc page, rather than the search that should overlay it.
-            // This was an interaction between the back-forward cache and our handlers
-            // that try to sync state between the URL and the search input. To work around it,
-            // do a small amount of re-init on page show.
-            window.onpageshow = function(){
-                var qSearch = getQueryStringParams().search;
-                if (search_input.value === "" && qSearch) {
-                    search_input.value = qSearch;
-                }
-                search();
+                document.location.href = url;
             };
         }
-
-        index = buildIndex(rawSearchIndex);
-        registerSearchEvents();
-        // If there's a search term in the URL, execute the search now.
-        if (getQueryStringParams().search) {
-            search();
-        }
-    };
+    }());
 
     function addSidebarCrates(crates) {
         // Draw a convenient sidebar of known crates if we have a listing
@@ -2216,6 +817,9 @@ function hideThemeButtonState() {
         block("foreigntype", "Foreign Types");
         block("keyword", "Keywords");
         block("traitalias", "Trait Aliases");
+
+        // `crates{version}.js` should always be loaded before this script, so we can use it safely.
+        addSidebarCrates(window.ALL_CRATES);
     };
 
     window.register_implementors = function(imp) {
@@ -2314,8 +918,10 @@ function hideThemeButtonState() {
             return;
         }
         if (hasClass(innerToggle, "will-expand")) {
-            updateLocalStorage("rustdoc-collapse", "false");
             removeClass(innerToggle, "will-expand");
+            onEachLazy(document.getElementsByTagName("details"), function(e) {
+                e.open = true;
+            });
             onEveryMatchingChild(innerToggle, "inner", function(e) {
                 e.innerHTML = labelForToggleButton(false);
             });
@@ -2326,8 +932,10 @@ function hideThemeButtonState() {
                 });
             }
         } else {
-            updateLocalStorage("rustdoc-collapse", "true");
             addClass(innerToggle, "will-expand");
+            onEachLazy(document.getElementsByTagName("details"), function(e) {
+                e.open = false;
+            });
             onEveryMatchingChild(innerToggle, "inner", function(e) {
                 var parent = e.parentNode;
                 var superParent = null;
@@ -2412,7 +1020,7 @@ function hideThemeButtonState() {
             if (hasClass(relatedDoc, "item-info")) {
                 relatedDoc = relatedDoc.nextElementSibling;
             }
-            if (hasClass(relatedDoc, "docblock") || hasClass(relatedDoc, "sub-variant")) {
+            if (hasClass(relatedDoc, "docblock")) {
                 if (mode === "toggle") {
                     if (hasClass(relatedDoc, "hidden-by-usual-hider")) {
                         action = "show";
@@ -2478,40 +1086,18 @@ function hideThemeButtonState() {
         }
     }
 
-    function collapser(e, collapse) {
+    function collapseNonInherent(e) {
         // inherent impl ids are like "impl" or impl-<number>'.
         // they will never be hidden by default.
         var n = e.parentElement;
         if (n.id.match(/^impl(?:-\d+)?$/) === null) {
             // Automatically minimize all non-inherent impls
-            if (collapse || hasClass(n, "impl")) {
+            if (hasClass(n, "impl")) {
                 collapseDocs(e, "hide");
             }
         }
     }
 
-    function autoCollapse(collapse) {
-        if (collapse) {
-            toggleAllDocs(true);
-        } else if (getSettingValue("auto-hide-trait-implementations") !== "false") {
-            var impl_list = document.getElementById("trait-implementations-list");
-
-            if (impl_list !== null) {
-                onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {
-                    collapser(e, collapse);
-                });
-            }
-
-            var blanket_list = document.getElementById("blanket-implementations-list");
-
-            if (blanket_list !== null) {
-                onEachLazy(blanket_list.getElementsByClassName("collapse-toggle"), function(e) {
-                    collapser(e, collapse);
-                });
-            }
-        }
-    }
-
     function insertAfter(newNode, referenceNode) {
         referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
     }
@@ -2569,6 +1155,23 @@ function hideThemeButtonState() {
         var toggle = createSimpleToggle(false);
         var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
         var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false";
+        var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
+        var hideTraitImplementations =
+            getSettingValue("auto-hide-trait-implementations") !== "false";
+
+        var impl_list = document.getElementById("trait-implementations-list");
+        if (impl_list !== null) {
+            onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {
+                collapseNonInherent(e);
+            });
+        }
+
+        var blanket_list = document.getElementById("blanket-implementations-list");
+        if (blanket_list !== null) {
+            onEachLazy(blanket_list.getElementsByClassName("collapse-toggle"), function(e) {
+                collapseNonInherent(e);
+            });
+        }
 
         var func = function(e) {
             var next = e.nextElementSibling;
@@ -2598,23 +1201,18 @@ function hideThemeButtonState() {
             if (!next) {
                 return;
             }
-            if (hasClass(e, "impl") &&
-                (next.getElementsByClassName("method").length > 0 ||
-                 next.getElementsByClassName("associatedconstant").length > 0)) {
-                var newToggle = toggle.cloneNode(true);
-                insertAfter(newToggle, e.childNodes[e.childNodes.length - 1]);
-                // In case the option "auto-collapse implementors" is not set to false, we collapse
-                // all implementors.
-                if (hideImplementors === true && e.parentNode.id === "implementors-list") {
-                    collapseDocs(newToggle, "hide");
-                }
-            }
         };
 
         onEachLazy(document.getElementsByClassName("method"), func);
         onEachLazy(document.getElementsByClassName("associatedconstant"), func);
-        onEachLazy(document.getElementsByClassName("impl"), funcImpl);
         var impl_call = function() {};
+        onEachLazy(document.getElementsByTagName("details"), function (e) {
+            var showLargeItem = !hideLargeItemContents && hasClass(e, "type-contents-toggle");
+            var showImplementor = !hideImplementors && hasClass(e, "implementors-toggle");
+            if (showLargeItem || showImplementor) {
+                e.open = true;
+            }
+        });
         if (hideMethodDocs === true) {
             impl_call = function(e, newToggle) {
                 if (e.id.match(/^impl(?:-\d+)?$/) === null) {
@@ -2675,7 +1273,7 @@ function hideThemeButtonState() {
         if (currentType) {
             currentType = currentType.getElementsByClassName("rust")[0];
             if (currentType) {
-                currentType.classList.forEach(function(item) {
+                onEachLazy(currentType.classList, function(item) {
                     if (item !== "main") {
                         className = item;
                         return true;
@@ -2683,18 +1281,7 @@ function hideThemeButtonState() {
                 });
             }
         }
-        var showItemDeclarations = getSettingValue("auto-hide-" + className);
-        if (showItemDeclarations === null) {
-            if (className === "enum" || className === "macro") {
-                showItemDeclarations = "false";
-            } else if (className === "struct" || className === "union" || className === "trait") {
-                showItemDeclarations = "true";
-            } else {
-                // In case we found an unknown type, we just use the "parent" value.
-                showItemDeclarations = getSettingValue("auto-hide-declarations");
-            }
-        }
-        showItemDeclarations = showItemDeclarations === "false";
+
         function buildToggleWrapper(e) {
             if (hasClass(e, "autohide")) {
                 var wrap = e.previousElementSibling;
@@ -2721,13 +1308,8 @@ function hideThemeButtonState() {
                 var extraClass;
 
                 if (hasClass(e, "type-decl")) {
-                    fontSize = "20px";
-                    otherMessage = "&nbsp;Show&nbsp;declaration";
-                    if (showItemDeclarations === false) {
-                        extraClass = "collapsed";
-                    }
-                } else if (hasClass(e, "sub-variant")) {
-                    otherMessage = "&nbsp;Show&nbsp;fields";
+                    // We do something special for these
+                    return;
                 } else if (hasClass(e, "non-exhaustive")) {
                     otherMessage = "&nbsp;This&nbsp;";
                     if (hasClass(e, "non-exhaustive-struct")) {
@@ -2750,11 +1332,8 @@ function hideThemeButtonState() {
                         otherMessage,
                         fontSize,
                         extraClass,
-                        hasClass(e, "type-decl") === false || showItemDeclarations === true),
+                        true),
                     e);
-                if (hasClass(e, "type-decl") === true && showItemDeclarations === true) {
-                    collapseDocs(e.previousSibling.childNodes[0], "toggle");
-                }
                 if (hasClass(e, "non-exhaustive") === true) {
                     collapseDocs(e.previousSibling.childNodes[0], "toggle");
                 }
@@ -2762,9 +1341,6 @@ function hideThemeButtonState() {
         }
 
         onEachLazy(document.getElementsByClassName("docblock"), buildToggleWrapper);
-        onEachLazy(document.getElementsByClassName("sub-variant"), buildToggleWrapper);
-
-        autoCollapse(getSettingValue("collapse") === "true");
 
         var pageId = getPageId();
         if (pageId !== null) {
@@ -2772,38 +1348,6 @@ function hideThemeButtonState() {
         }
     }());
 
-    function createToggleWrapper(tog) {
-        var span = document.createElement("span");
-        span.className = "toggle-label";
-        span.style.display = "none";
-        span.innerHTML = "&nbsp;Expand&nbsp;attributes";
-        tog.appendChild(span);
-
-        var wrapper = document.createElement("div");
-        wrapper.className = "toggle-wrapper toggle-attributes";
-        wrapper.appendChild(tog);
-        return wrapper;
-    }
-
-    (function() {
-        // To avoid checking on "rustdoc-item-attributes" value on every loop...
-        var itemAttributesFunc = function() {};
-        if (getSettingValue("auto-hide-attributes") !== "false") {
-            itemAttributesFunc = function(x) {
-                collapseDocs(x.previousSibling.childNodes[0], "toggle");
-            };
-        }
-        var attributesToggle = createToggleWrapper(createSimpleToggle(false));
-        onEachLazy(main.getElementsByClassName("attributes"), function(i_e) {
-            var attr_tog = attributesToggle.cloneNode(true);
-            if (hasClass(i_e, "top-attr") === true) {
-                addClass(attr_tog, "top-attr");
-            }
-            i_e.parentNode.insertBefore(attr_tog, i_e);
-            itemAttributesFunc(i_e);
-        });
-    }());
-
     (function() {
         // To avoid checking on "rustdoc-line-numbers" value on every loop...
         var lineNumbersFunc = function() {};
@@ -2847,60 +1391,6 @@ function hideThemeButtonState() {
         };
     });
 
-    // In the search display, allows to switch between tabs.
-    function printTab(nb) {
-        if (nb === 0 || nb === 1 || nb === 2) {
-            currentTab = nb;
-        }
-        var nb_copy = nb;
-        onEachLazy(document.getElementById("titles").childNodes, function(elem) {
-            if (nb_copy === 0) {
-                addClass(elem, "selected");
-            } else {
-                removeClass(elem, "selected");
-            }
-            nb_copy -= 1;
-        });
-        onEachLazy(document.getElementById("results").childNodes, function(elem) {
-            if (nb === 0) {
-                elem.style.display = "";
-            } else {
-                elem.style.display = "none";
-            }
-            nb -= 1;
-        });
-    }
-
-    function putBackSearch(search_input) {
-        var search = getSearchElement();
-        if (search_input.value !== "" && hasClass(search, "hidden")) {
-            showSearchResults(search);
-            if (browserSupportsHistoryApi()) {
-                var extra = "?search=" + encodeURIComponent(search_input.value);
-                history.replaceState(search_input.value, "",
-                    getNakedUrl() + extra + window.location.hash);
-            }
-            document.title = searchTitle;
-        }
-    }
-
-    function getSearchLoadingText() {
-        return "Loading search results...";
-    }
-
-    if (search_input) {
-        search_input.onfocus = function() {
-            putBackSearch(this);
-        };
-    }
-
-    var params = getQueryStringParams();
-    if (params && params.search) {
-        var search = getSearchElement();
-        search.innerHTML = "<h3 style=\"text-align: center;\">" + getSearchLoadingText() + "</h3>";
-        showSearchResults(search);
-    }
-
     var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
     if (sidebar_menu) {
         sidebar_menu.onclick = function() {
@@ -2924,39 +1414,15 @@ function hideThemeButtonState() {
             // errors in mobile browsers).
             if (e.tagName === "H2" || e.tagName === "H3") {
                 var nextTagName = e.nextElementSibling.tagName;
-                if (nextTagName == "H2" || nextTagName == "H3") {
+                if (nextTagName === "H2" || nextTagName === "H3") {
                     e.nextElementSibling.style.display = "flex";
-                } else {
+                } else if (nextTagName !== "DETAILS") {
                     e.nextElementSibling.style.display = "block";
                 }
             }
         });
     }
 
-    function addSearchOptions(crates) {
-        var elem = document.getElementById("crate-search");
-
-        if (!elem) {
-            return;
-        }
-        var savedCrate = getSettingValue("saved-filter-crate");
-        for (var i = 0, len = crates.length; i < len; ++i) {
-            var option = document.createElement("option");
-            option.value = crates[i];
-            option.innerText = crates[i];
-            elem.appendChild(option);
-            // Set the crate filter from saved storage, if the current page has the saved crate
-            // filter.
-            //
-            // If not, ignore the crate filter -- we want to support filtering for crates on sites
-            // like doc.rust-lang.org where the crates may differ from page to page while on the
-            // same domain.
-            if (crates[i] === savedCrate) {
-                elem.value = savedCrate;
-            }
-        }
-    };
-
     function buildHelperPopup() {
         var popup = document.createElement("aside");
         addClass(popup, "hidden");
@@ -3014,55 +1480,14 @@ function hideThemeButtonState() {
         container.appendChild(div_infos);
 
         popup.appendChild(container);
-        insertAfter(popup, getSearchElement());
+        insertAfter(popup, searchState.outputElement());
         // So that it's only built once and then it'll do nothing when called!
         buildHelperPopup = function() {};
     }
 
-    function loadScript(url) {
-        var script = document.createElement('script');
-        script.src = url;
-        document.head.append(script);
-    }
-
-    function setupSearchLoader() {
-        var searchLoaded = false;
-        function loadSearch() {
-            if (!searchLoaded) {
-                searchLoaded = true;
-                loadScript(window.searchJS);
-            }
-        }
-
-        // `crates{version}.js` should always be loaded before this script, so we can use it safely.
-        addSearchOptions(window.ALL_CRATES);
-        addSidebarCrates(window.ALL_CRATES);
-
-        search_input.addEventListener("focus", function() {
-            search_input.origPlaceholder = search_input.placeholder;
-            search_input.placeholder = "Type your search here.";
-            loadSearch();
-        });
-        search_input.addEventListener("blur", function() {
-            search_input.placeholder = search_input.origPlaceholder;
-        });
-        search_input.removeAttribute('disabled');
-
-        var crateSearchDropDown = document.getElementById("crate-search");
-        // `crateSearchDropDown` can be null in case there is only crate because in that case, the
-        // crate filter dropdown is removed.
-        if (crateSearchDropDown) {
-            crateSearchDropDown.addEventListener("focus", loadSearch);
-        }
-        var params = getQueryStringParams();
-        if (params.search !== undefined) {
-            loadSearch();
-        }
-    }
-
     onHashChange(null);
     window.onhashchange = onHashChange;
-    setupSearchLoader();
+    searchState.setup();
 }());
 
 function copy_path(but) {
index 705ae17f3eb32cce72832119904a7bd6abd87c4a..a024fa49b0e8bf11b6723a76ab1c11308533f1dd 100644 (file)
@@ -116,6 +116,8 @@ h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) {
        padding-bottom: 6px;
 }
 h1.fqn {
+       display: flex;
+       width: 100%;
        border-bottom: 1px dashed;
        margin-top: 0;
 }
@@ -458,6 +460,13 @@ nav.sub {
        font-weight: normal;
 }
 
+h1.fqn > .out-of-band {
+       float: unset;
+       flex: 1;
+       text-align: right;
+       margin-left: 8px;
+}
+
 h3.impl > .out-of-band {
        font-size: 21px;
 }
@@ -868,7 +877,8 @@ body.blur > :not(#help) {
                0 -1px 0 black;
 }
 
-.module-item .stab {
+.module-item .stab,
+.import-item .stab {
        border-radius: 3px;
        display: inline-block;
        font-size: 80%;
@@ -879,7 +889,8 @@ body.blur > :not(#help) {
        vertical-align: text-bottom;
 }
 
-.module-item.unstable {
+.module-item.unstable,
+.import-item.unstable {
        opacity: 0.65;
 }
 
@@ -967,6 +978,10 @@ a.test-arrow:hover{
        color: inherit;
 }
 
+.code-attribute {
+       font-weight: 300;
+}
+
 .collapse-toggle {
        font-weight: 300;
        position: absolute;
@@ -1031,10 +1046,11 @@ h3 > .collapse-toggle, h4 > .collapse-toggle {
 }
 
 .sub-variant, .sub-variant > h3 {
-       margin-top: 1px !important;
+       margin-top: 0px !important;
+       padding-top: 1px;
 }
 
-#main > .sub-variant > h3 {
+#main > details > .sub-variant > h3 {
        font-size: 15px;
        margin-left: 25px;
        margin-bottom: 5px;
@@ -1056,12 +1072,6 @@ h3 > .collapse-toggle, h4 > .collapse-toggle {
        margin-top: 3px;
 }
 
-.enum > .toggle-wrapper + .docblock, .struct > .toggle-wrapper + .docblock {
-       margin-left: 30px;
-       margin-bottom: 20px;
-       margin-top: 5px;
-}
-
 .docblock > .section-header:first-child {
        margin-left: 15px;
        margin-top: 0;
@@ -1071,30 +1081,10 @@ h3 > .collapse-toggle, h4 > .collapse-toggle {
        left: -10px;
 }
 
-.enum > .collapsed, .struct > .collapsed {
-       margin-bottom: 25px;
-}
-
 #main > .variant, #main > .structfield {
        display: block;
 }
 
-.attributes {
-       display: block;
-       margin-top: 0px !important;
-       margin-right: 0px;
-       margin-bottom: 0px !important;
-       margin-left: 30px;
-}
-.toggle-attributes.collapsed {
-       margin-bottom: 0;
-}
-.impl-items > .toggle-attributes {
-       margin-left: 20px;
-}
-.impl-items .attributes {
-       font-weight: 500;
-}
 
 :target > code {
        opacity: 1;
@@ -1470,10 +1460,6 @@ h4 > .notable-traits {
                padding: 0;
        }
 
-       .content .in-band {
-               width: 100%;
-       }
-
        .content h4 > .out-of-band {
                position: inherit;
        }
@@ -1587,6 +1573,10 @@ h4 > .notable-traits {
                left: -10px;
        }
 
+       .item-list > details.rustdoc-toggle > summary:not(.hideme)::before {
+               left: -10px;
+       }
+
        #all-types {
                margin: 10px;
        }
@@ -1781,16 +1771,56 @@ div.name.expand::before {
        top: 2px;
 }
 
-/* This part is to fix the "Expand attributes" part in the type declaration. */
-.type-decl > pre > .toggle-wrapper.toggle-attributes.top-attr {
-       margin-left: 0 !important;
+/* The hideme class is used on summary tags that contain a span with
+       placeholder text shown only when the toggle is closed. For instance,
+       "Expand description" or "Show methods". */
+details.rustdoc-toggle > summary.hideme {
+       cursor: pointer;
+}
+
+details.rustdoc-toggle > summary::-webkit-details-marker {
+       display: none;
+}
+
+details.rustdoc-toggle > summary.hideme > span {
+       margin-left: 9px;
+}
+
+details.rustdoc-toggle > summary::before {
+       content: "[+]";
+       font-weight: 300;
+       font-size: 0.8em;
+       letter-spacing: 1px;
+       cursor: pointer;
+}
+
+details.rustdoc-toggle > summary.hideme::before {
+       position: relative;
+}
+
+details.rustdoc-toggle > summary:not(.hideme)::before {
+       position: absolute;
+       left: -23px;
+}
+
+/* When a "hideme" summary is open and the "Expand description" or "Show
+       methods" text is hidden, we want the [-] toggle that remains to not
+       affect the layout of the items to its right. To do that, we use
+       absolute positioning. Note that we also set position: relative
+       on the parent <details> to make this work properly. */
+details.rustdoc-toggle[open] > summary.hideme {
+       position: absolute;
 }
-.type-decl > pre > .docblock.attributes.top-attr {
-       margin-left: 1.8em !important;
+
+details.rustdoc-toggle[open] {
+       position: relative;
 }
-.type-decl > pre > .toggle-attributes {
-       margin-left: 2.2em;
+
+details.rustdoc-toggle[open] > summary.hideme > span {
+       display: none;
 }
-.type-decl > pre > .docblock.attributes {
-       margin-left: 4em;
+
+details.rustdoc-toggle[open] > summary::before {
+       content: "[−]";
+       display: inline;
 }
diff --git a/src/librustdoc/html/static/search.js b/src/librustdoc/html/static/search.js
new file mode 100644 (file)
index 0000000..9fab435
--- /dev/null
@@ -0,0 +1,1515 @@
+(function() {
+// This mapping table should match the discriminants of
+// `rustdoc::html::item_type::ItemType` type in Rust.
+var itemTypes = ["mod",
+                    "externcrate",
+                    "import",
+                    "struct",
+                    "enum",
+                    "fn",
+                    "type",
+                    "static",
+                    "trait",
+                    "impl",
+                    "tymethod",
+                    "method",
+                    "structfield",
+                    "variant",
+                    "macro",
+                    "primitive",
+                    "associatedtype",
+                    "constant",
+                    "associatedconstant",
+                    "union",
+                    "foreigntype",
+                    "keyword",
+                    "existential",
+                    "attr",
+                    "derive",
+                    "traitalias"];
+
+// used for special search precedence
+var TY_PRIMITIVE = itemTypes.indexOf("primitive");
+var TY_KEYWORD = itemTypes.indexOf("keyword");
+
+// In the search display, allows to switch between tabs.
+function printTab(nb) {
+    if (nb === 0 || nb === 1 || nb === 2) {
+        searchState.currentTab = nb;
+    }
+    var nb_copy = nb;
+    onEachLazy(document.getElementById("titles").childNodes, function(elem) {
+        if (nb_copy === 0) {
+            addClass(elem, "selected");
+        } else {
+            removeClass(elem, "selected");
+        }
+        nb_copy -= 1;
+    });
+    onEachLazy(document.getElementById("results").childNodes, function(elem) {
+        if (nb === 0) {
+            elem.style.display = "";
+        } else {
+            elem.style.display = "none";
+        }
+        nb -= 1;
+    });
+}
+
+function removeEmptyStringsFromArray(x) {
+    for (var i = 0, len = x.length; i < len; ++i) {
+        if (x[i] === "") {
+            x.splice(i, 1);
+            i -= 1;
+        }
+    }
+}
+
+/**
+ * A function to compute the Levenshtein distance between two strings
+ * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
+ * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
+ * This code is an unmodified version of the code written by Marco de Wit
+ * and was found at http://stackoverflow.com/a/18514751/745719
+ */
+var levenshtein_row2 = [];
+function levenshtein(s1, s2) {
+    if (s1 === s2) {
+        return 0;
+    }
+    var s1_len = s1.length, s2_len = s2.length;
+    if (s1_len && s2_len) {
+        var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
+        while (i1 < s1_len) {
+            row[i1] = ++i1;
+        }
+        while (i2 < s2_len) {
+            c2 = s2.charCodeAt(i2);
+            a = i2;
+            ++i2;
+            b = i2;
+            for (i1 = 0; i1 < s1_len; ++i1) {
+                c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
+                a = row[i1];
+                b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
+                row[i1] = b;
+            }
+        }
+        return b;
+    }
+    return s1_len + s2_len;
+}
+
+window.initSearch = function(rawSearchIndex) {
+    var MAX_LEV_DISTANCE = 3;
+    var MAX_RESULTS = 200;
+    var GENERICS_DATA = 1;
+    var NAME = 0;
+    var INPUTS_DATA = 0;
+    var OUTPUT_DATA = 1;
+    var NO_TYPE_FILTER = -1;
+    var currentResults, index, searchIndex;
+    var ALIASES = {};
+    var params = searchState.getQueryStringParams();
+
+    // Populate search bar with query string search term when provided,
+    // but only if the input bar is empty. This avoid the obnoxious issue
+    // where you start trying to do a search, and the index loads, and
+    // suddenly your search is gone!
+    if (searchState.input.value === "") {
+        searchState.input.value = params.search || "";
+    }
+
+    /**
+     * Executes the query and builds an index of results
+     * @param  {[Object]} query      [The user query]
+     * @param  {[type]} searchWords  [The list of search words to query
+     *                                against]
+     * @param  {[type]} filterCrates [Crate to search in if defined]
+     * @return {[type]}              [A search index of results]
+     */
+    function execQuery(query, searchWords, filterCrates) {
+        function itemTypeFromName(typename) {
+            for (var i = 0, len = itemTypes.length; i < len; ++i) {
+                if (itemTypes[i] === typename) {
+                    return i;
+                }
+            }
+            return NO_TYPE_FILTER;
+        }
+
+        var valLower = query.query.toLowerCase(),
+            val = valLower,
+            typeFilter = itemTypeFromName(query.type),
+            results = {}, results_in_args = {}, results_returned = {},
+            split = valLower.split("::");
+
+        removeEmptyStringsFromArray(split);
+
+        function transformResults(results, isType) {
+            var out = [];
+            for (var i = 0, len = results.length; i < len; ++i) {
+                if (results[i].id > -1) {
+                    var obj = searchIndex[results[i].id];
+                    obj.lev = results[i].lev;
+                    if (isType !== true || obj.type) {
+                        var res = buildHrefAndPath(obj);
+                        obj.displayPath = pathSplitter(res[0]);
+                        obj.fullPath = obj.displayPath + obj.name;
+                        // To be sure than it some items aren't considered as duplicate.
+                        obj.fullPath += "|" + obj.ty;
+                        obj.href = res[1];
+                        out.push(obj);
+                        if (out.length >= MAX_RESULTS) {
+                            break;
+                        }
+                    }
+                }
+            }
+            return out;
+        }
+
+        function sortResults(results, isType) {
+            var ar = [];
+            for (var entry in results) {
+                if (hasOwnProperty(results, entry)) {
+                    ar.push(results[entry]);
+                }
+            }
+            results = ar;
+            var i, len, result;
+            for (i = 0, len = results.length; i < len; ++i) {
+                result = results[i];
+                result.word = searchWords[result.id];
+                result.item = searchIndex[result.id] || {};
+            }
+            // if there are no results then return to default and fail
+            if (results.length === 0) {
+                return [];
+            }
+
+            results.sort(function(aaa, bbb) {
+                var a, b;
+
+                // sort by exact match with regard to the last word (mismatch goes later)
+                a = (aaa.word !== val);
+                b = (bbb.word !== val);
+                if (a !== b) { return a - b; }
+
+                // Sort by non levenshtein results and then levenshtein results by the distance
+                // (less changes required to match means higher rankings)
+                a = (aaa.lev);
+                b = (bbb.lev);
+                if (a !== b) { return a - b; }
+
+                // sort by crate (non-current crate goes later)
+                a = (aaa.item.crate !== window.currentCrate);
+                b = (bbb.item.crate !== window.currentCrate);
+                if (a !== b) { return a - b; }
+
+                // sort by item name length (longer goes later)
+                a = aaa.word.length;
+                b = bbb.word.length;
+                if (a !== b) { return a - b; }
+
+                // sort by item name (lexicographically larger goes later)
+                a = aaa.word;
+                b = bbb.word;
+                if (a !== b) { return (a > b ? +1 : -1); }
+
+                // sort by index of keyword in item name (no literal occurrence goes later)
+                a = (aaa.index < 0);
+                b = (bbb.index < 0);
+                if (a !== b) { return a - b; }
+                // (later literal occurrence, if any, goes later)
+                a = aaa.index;
+                b = bbb.index;
+                if (a !== b) { return a - b; }
+
+                // special precedence for primitive and keyword pages
+                if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
+                    (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
+                    return -1;
+                }
+                if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
+                    (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
+                    return 1;
+                }
+
+                // sort by description (no description goes later)
+                a = (aaa.item.desc === "");
+                b = (bbb.item.desc === "");
+                if (a !== b) { return a - b; }
+
+                // sort by type (later occurrence in `itemTypes` goes later)
+                a = aaa.item.ty;
+                b = bbb.item.ty;
+                if (a !== b) { return a - b; }
+
+                // sort by path (lexicographically larger goes later)
+                a = aaa.item.path;
+                b = bbb.item.path;
+                if (a !== b) { return (a > b ? +1 : -1); }
+
+                // que sera, sera
+                return 0;
+            });
+
+            for (i = 0, len = results.length; i < len; ++i) {
+                var result = results[i];
+
+                // this validation does not make sense when searching by types
+                if (result.dontValidate) {
+                    continue;
+                }
+                var name = result.item.name.toLowerCase(),
+                    path = result.item.path.toLowerCase(),
+                    parent = result.item.parent;
+
+                if (isType !== true &&
+                    validateResult(name, path, split, parent) === false)
+                {
+                    result.id = -1;
+                }
+            }
+            return transformResults(results);
+        }
+
+        function extractGenerics(val) {
+            val = val.toLowerCase();
+            if (val.indexOf("<") !== -1) {
+                var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
+                return {
+                    name: val.substring(0, val.indexOf("<")),
+                    generics: values.split(/\s*,\s*/),
+                };
+            }
+            return {
+                name: val,
+                generics: [],
+            };
+        }
+
+        function getObjectNameFromId(id) {
+            if (typeof id === "number") {
+                return searchIndex[id].name;
+            }
+            return id;
+        }
+
+        function checkGenerics(obj, val) {
+            // The names match, but we need to be sure that all generics kinda
+            // match as well.
+            var tmp_lev, elem_name;
+            if (val.generics.length > 0) {
+                if (obj.length > GENERICS_DATA &&
+                      obj[GENERICS_DATA].length >= val.generics.length) {
+                    var elems = Object.create(null);
+                    var elength = object[GENERICS_DATA].length;
+                    for (var x = 0; x < elength; ++x) {
+                        elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
+                    }
+                    var total = 0;
+                    var done = 0;
+                    // We need to find the type that matches the most to remove it in order
+                    // to move forward.
+                    var vlength = val.generics.length;
+                    for (x = 0; x < vlength; ++x) {
+                        var lev = MAX_LEV_DISTANCE + 1;
+                        var firstGeneric = getObjectNameFromId(val.generics[x]);
+                        var match = null;
+                        if (elems[firstGeneric]) {
+                            match = firstGeneric;
+                            lev = 0;
+                        } else {
+                            for (elem_name in elems) {
+                                tmp_lev = levenshtein(elem_name, firstGeneric);
+                                if (tmp_lev < lev) {
+                                    lev = tmp_lev;
+                                    match = elem_name;
+                                }
+                            }
+                        }
+                        if (match !== null) {
+                            elems[match] -= 1;
+                            if (elems[match] == 0) {
+                                delete elems[match];
+                            }
+                            total += lev;
+                            done += 1;
+                        } else {
+                            return MAX_LEV_DISTANCE + 1;
+                        }
+                    }
+                    return Math.ceil(total / done);
+                }
+            }
+            return MAX_LEV_DISTANCE + 1;
+        }
+
+        // Check for type name and type generics (if any).
+        function checkType(obj, val, literalSearch) {
+            var lev_distance = MAX_LEV_DISTANCE + 1;
+            var len, x, firstGeneric;
+            if (obj[NAME] === val.name) {
+                if (literalSearch === true) {
+                    if (val.generics && val.generics.length !== 0) {
+                        if (obj.length > GENERICS_DATA &&
+                              obj[GENERICS_DATA].length >= val.generics.length) {
+                            var elems = Object.create(null);
+                            len = obj[GENERICS_DATA].length;
+                            for (x = 0; x < len; ++x) {
+                                elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
+                            }
+
+                            var allFound = true;
+                            len = val.generics.length;
+                            for (x = 0; x < len; ++x) {
+                                firstGeneric = getObjectNameFromId(val.generics[x]);
+                                if (elems[firstGeneric]) {
+                                    elems[firstGeneric] -= 1;
+                                } else {
+                                    allFound = false;
+                                    break;
+                                }
+                            }
+                            if (allFound === true) {
+                                return true;
+                            }
+                        } else {
+                            return false;
+                        }
+                    }
+                    return true;
+                }
+                // If the type has generics but don't match, then it won't return at this point.
+                // Otherwise, `checkGenerics` will return 0 and it'll return.
+                if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
+                    var tmp_lev = checkGenerics(obj, val);
+                    if (tmp_lev <= MAX_LEV_DISTANCE) {
+                        return tmp_lev;
+                    }
+                } else {
+                    return 0;
+                }
+            }
+            // Names didn't match so let's check if one of the generic types could.
+            if (literalSearch === true) {
+                 if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
+                    return obj[GENERICS_DATA].some(
+                        function(name) {
+                            return name === val.name;
+                        });
+                }
+                return false;
+            }
+            lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
+            if (lev_distance <= MAX_LEV_DISTANCE) {
+                // The generics didn't match but the name kinda did so we give it
+                // a levenshtein distance value that isn't *this* good so it goes
+                // into the search results but not too high.
+                lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
+            } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
+                // We can check if the type we're looking for is inside the generics!
+                var olength = obj[GENERICS_DATA].length;
+                for (x = 0; x < olength; ++x) {
+                    lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
+                                            lev_distance);
+                }
+            }
+            // Now whatever happens, the returned distance is "less good" so we should mark it
+            // as such, and so we add 1 to the distance to make it "less good".
+            return lev_distance + 1;
+        }
+
+        function findArg(obj, val, literalSearch, typeFilter) {
+            var lev_distance = MAX_LEV_DISTANCE + 1;
+
+            if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) {
+                var length = obj.type[INPUTS_DATA].length;
+                for (var i = 0; i < length; i++) {
+                    var tmp = obj.type[INPUTS_DATA][i];
+                    if (typePassesFilter(typeFilter, tmp[1]) === false) {
+                        continue;
+                    }
+                    tmp = checkType(tmp, val, literalSearch);
+                    if (literalSearch === true) {
+                        if (tmp === true) {
+                            return true;
+                        }
+                        continue;
+                    }
+                    lev_distance = Math.min(tmp, lev_distance);
+                    if (lev_distance === 0) {
+                        return 0;
+                    }
+                }
+            }
+            return literalSearch === true ? false : lev_distance;
+        }
+
+        function checkReturned(obj, val, literalSearch, typeFilter) {
+            var lev_distance = MAX_LEV_DISTANCE + 1;
+
+            if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
+                var ret = obj.type[OUTPUT_DATA];
+                if (typeof ret[0] === "string") {
+                    ret = [ret];
+                }
+                for (var x = 0, len = ret.length; x < len; ++x) {
+                    var tmp = ret[x];
+                    if (typePassesFilter(typeFilter, tmp[1]) === false) {
+                        continue;
+                    }
+                    tmp = checkType(tmp, val, literalSearch);
+                    if (literalSearch === true) {
+                        if (tmp === true) {
+                            return true;
+                        }
+                        continue;
+                    }
+                    lev_distance = Math.min(tmp, lev_distance);
+                    if (lev_distance === 0) {
+                        return 0;
+                    }
+                }
+            }
+            return literalSearch === true ? false : lev_distance;
+        }
+
+        function checkPath(contains, lastElem, ty) {
+            if (contains.length === 0) {
+                return 0;
+            }
+            var ret_lev = MAX_LEV_DISTANCE + 1;
+            var path = ty.path.split("::");
+
+            if (ty.parent && ty.parent.name) {
+                path.push(ty.parent.name.toLowerCase());
+            }
+
+            var length = path.length;
+            var clength = contains.length;
+            if (clength > length) {
+                return MAX_LEV_DISTANCE + 1;
+            }
+            for (var i = 0; i < length; ++i) {
+                if (i + clength > length) {
+                    break;
+                }
+                var lev_total = 0;
+                var aborted = false;
+                for (var x = 0; x < clength; ++x) {
+                    var lev = levenshtein(path[i + x], contains[x]);
+                    if (lev > MAX_LEV_DISTANCE) {
+                        aborted = true;
+                        break;
+                    }
+                    lev_total += lev;
+                }
+                if (aborted === false) {
+                    ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
+                }
+            }
+            return ret_lev;
+        }
+
+        function typePassesFilter(filter, type) {
+            // No filter
+            if (filter <= NO_TYPE_FILTER) return true;
+
+            // Exact match
+            if (filter === type) return true;
+
+            // Match related items
+            var name = itemTypes[type];
+            switch (itemTypes[filter]) {
+                case "constant":
+                    return name === "associatedconstant";
+                case "fn":
+                    return name === "method" || name === "tymethod";
+                case "type":
+                    return name === "primitive" || name === "associatedtype";
+                case "trait":
+                    return name === "traitalias";
+            }
+
+            // No match
+            return false;
+        }
+
+        function createAliasFromItem(item) {
+            return {
+                crate: item.crate,
+                name: item.name,
+                path: item.path,
+                desc: item.desc,
+                ty: item.ty,
+                parent: item.parent,
+                type: item.type,
+                is_alias: true,
+            };
+        }
+
+        function handleAliases(ret, query, filterCrates) {
+            // We separate aliases and crate aliases because we want to have current crate
+            // aliases to be before the others in the displayed results.
+            var aliases = [];
+            var crateAliases = [];
+            if (filterCrates !== undefined) {
+                if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
+                    var query_aliases = ALIASES[filterCrates][query.search];
+                    var len = query_aliases.length;
+                    for (var i = 0; i < len; ++i) {
+                        aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+                    }
+                }
+            } else {
+                Object.keys(ALIASES).forEach(function(crate) {
+                    if (ALIASES[crate][query.search]) {
+                        var pushTo = crate === window.currentCrate ? crateAliases : aliases;
+                        var query_aliases = ALIASES[crate][query.search];
+                        var len = query_aliases.length;
+                        for (var i = 0; i < len; ++i) {
+                            pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+                        }
+                    }
+                });
+            }
+
+            var sortFunc = function(aaa, bbb) {
+                if (aaa.path < bbb.path) {
+                    return 1;
+                } else if (aaa.path === bbb.path) {
+                    return 0;
+                }
+                return -1;
+            };
+            crateAliases.sort(sortFunc);
+            aliases.sort(sortFunc);
+
+            var pushFunc = function(alias) {
+                alias.alias = query.raw;
+                var res = buildHrefAndPath(alias);
+                alias.displayPath = pathSplitter(res[0]);
+                alias.fullPath = alias.displayPath + alias.name;
+                alias.href = res[1];
+
+                ret.others.unshift(alias);
+                if (ret.others.length > MAX_RESULTS) {
+                    ret.others.pop();
+                }
+            };
+            onEach(aliases, pushFunc);
+            onEach(crateAliases, pushFunc);
+        }
+
+        // quoted values mean literal search
+        var nSearchWords = searchWords.length;
+        var i, it;
+        var ty;
+        var fullId;
+        var returned;
+        var in_args;
+        var len;
+        if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
+            val.charAt(val.length - 1) === val.charAt(0))
+        {
+            val = extractGenerics(val.substr(1, val.length - 2));
+            for (i = 0; i < nSearchWords; ++i) {
+                if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
+                    continue;
+                }
+                in_args = findArg(searchIndex[i], val, true, typeFilter);
+                returned = checkReturned(searchIndex[i], val, true, typeFilter);
+                ty = searchIndex[i];
+                fullId = ty.id;
+
+                if (searchWords[i] === val.name
+                    && typePassesFilter(typeFilter, searchIndex[i].ty)
+                    && results[fullId] === undefined) {
+                    results[fullId] = {
+                        id: i,
+                        index: -1,
+                        dontValidate: true,
+                    };
+                }
+                if (in_args === true && results_in_args[fullId] === undefined) {
+                    results_in_args[fullId] = {
+                        id: i,
+                        index: -1,
+                        dontValidate: true,
+                    };
+                }
+                if (returned === true && results_returned[fullId] === undefined) {
+                    results_returned[fullId] = {
+                        id: i,
+                        index: -1,
+                        dontValidate: true,
+                    };
+                }
+            }
+            query.inputs = [val];
+            query.output = val;
+            query.search = val;
+        // searching by type
+        } else if (val.search("->") > -1) {
+            var trimmer = function(s) { return s.trim(); };
+            var parts = val.split("->").map(trimmer);
+            var input = parts[0];
+            // sort inputs so that order does not matter
+            var inputs = input.split(",").map(trimmer).sort();
+            for (i = 0, len = inputs.length; i < len; ++i) {
+                inputs[i] = extractGenerics(inputs[i]);
+            }
+            var output = extractGenerics(parts[1]);
+
+            for (i = 0; i < nSearchWords; ++i) {
+                if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
+                    continue;
+                }
+                var type = searchIndex[i].type;
+                ty = searchIndex[i];
+                if (!type) {
+                    continue;
+                }
+                fullId = ty.id;
+
+                returned = checkReturned(ty, output, true, NO_TYPE_FILTER);
+                if (output.name === "*" || returned === true) {
+                    in_args = false;
+                    var is_module = false;
+
+                    if (input === "*") {
+                        is_module = true;
+                    } else {
+                        var allFound = true;
+                        for (it = 0, len = inputs.length; allFound === true && it < len; it++) {
+                            allFound = checkType(type, inputs[it], true);
+                        }
+                        in_args = allFound;
+                    }
+                    if (in_args === true) {
+                        results_in_args[fullId] = {
+                            id: i,
+                            index: -1,
+                            dontValidate: true,
+                        };
+                    }
+                    if (returned === true) {
+                        results_returned[fullId] = {
+                            id: i,
+                            index: -1,
+                            dontValidate: true,
+                        };
+                    }
+                    if (is_module === true) {
+                        results[fullId] = {
+                            id: i,
+                            index: -1,
+                            dontValidate: true,
+                        };
+                    }
+                }
+            }
+            query.inputs = inputs.map(function(input) {
+                return input.name;
+            });
+            query.output = output.name;
+        } else {
+            query.inputs = [val];
+            query.output = val;
+            query.search = val;
+            // gather matching search results up to a certain maximum
+            val = val.replace(/\_/g, "");
+
+            var valGenerics = extractGenerics(val);
+
+            var paths = valLower.split("::");
+            removeEmptyStringsFromArray(paths);
+            val = paths[paths.length - 1];
+            var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
+
+            var lev, j;
+            for (j = 0; j < nSearchWords; ++j) {
+                ty = searchIndex[j];
+                if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) {
+                    continue;
+                }
+                var lev_add = 0;
+                if (paths.length > 1) {
+                    lev = checkPath(contains, paths[paths.length - 1], ty);
+                    if (lev > MAX_LEV_DISTANCE) {
+                        continue;
+                    } else if (lev > 0) {
+                        lev_add = lev / 10;
+                    }
+                }
+
+                returned = MAX_LEV_DISTANCE + 1;
+                in_args = MAX_LEV_DISTANCE + 1;
+                var index = -1;
+                // we want lev results to go lower than others
+                lev = MAX_LEV_DISTANCE + 1;
+                fullId = ty.id;
+
+                if (searchWords[j].indexOf(split[i]) > -1 ||
+                    searchWords[j].indexOf(val) > -1 ||
+                    ty.normalizedName.indexOf(val) > -1)
+                {
+                    // filter type: ... queries
+                    if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
+                        index = ty.normalizedName.indexOf(val);
+                    }
+                }
+                if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
+                    if (typePassesFilter(typeFilter, ty.ty) === false) {
+                        lev = MAX_LEV_DISTANCE + 1;
+                    } else {
+                        lev += 1;
+                    }
+                }
+                in_args = findArg(ty, valGenerics, false, typeFilter);
+                returned = checkReturned(ty, valGenerics, false, typeFilter);
+
+                lev += lev_add;
+                if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
+                    if (val.length < 6) {
+                        lev -= 1;
+                    } else {
+                        lev = 0;
+                    }
+                }
+                if (in_args <= MAX_LEV_DISTANCE) {
+                    if (results_in_args[fullId] === undefined) {
+                        results_in_args[fullId] = {
+                            id: j,
+                            index: index,
+                            lev: in_args,
+                        };
+                    }
+                    results_in_args[fullId].lev =
+                        Math.min(results_in_args[fullId].lev, in_args);
+                }
+                if (returned <= MAX_LEV_DISTANCE) {
+                    if (results_returned[fullId] === undefined) {
+                        results_returned[fullId] = {
+                            id: j,
+                            index: index,
+                            lev: returned,
+                        };
+                    }
+                    results_returned[fullId].lev =
+                        Math.min(results_returned[fullId].lev, returned);
+                }
+                if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
+                    if (index !== -1 && paths.length < 2) {
+                        lev = 0;
+                    }
+                    if (results[fullId] === undefined) {
+                        results[fullId] = {
+                            id: j,
+                            index: index,
+                            lev: lev,
+                        };
+                    }
+                    results[fullId].lev = Math.min(results[fullId].lev, lev);
+                }
+            }
+        }
+
+        var ret = {
+            "in_args": sortResults(results_in_args, true),
+            "returned": sortResults(results_returned, true),
+            "others": sortResults(results),
+        };
+        handleAliases(ret, query, filterCrates);
+        return ret;
+    }
+
+    /**
+     * Validate performs the following boolean logic. For example:
+     * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
+     * exists in (name || path || parent) OR => ("file" && "open") exists in
+     * (name || path )
+     *
+     * This could be written functionally, but I wanted to minimise
+     * functions on stack.
+     *
+     * @param  {[string]} name   [The name of the result]
+     * @param  {[string]} path   [The path of the result]
+     * @param  {[string]} keys   [The keys to be used (["file", "open"])]
+     * @param  {[object]} parent [The parent of the result]
+     * @return {boolean}       [Whether the result is valid or not]
+     */
+    function validateResult(name, path, keys, parent) {
+        for (var i = 0, len = keys.length; i < len; ++i) {
+            // each check is for validation so we negate the conditions and invalidate
+            if (!(
+                // check for an exact name match
+                name.indexOf(keys[i]) > -1 ||
+                // then an exact path match
+                path.indexOf(keys[i]) > -1 ||
+                // next if there is a parent, check for exact parent match
+                (parent !== undefined && parent.name !== undefined &&
+                    parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
+                // lastly check to see if the name was a levenshtein match
+                levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function getQuery(raw) {
+        var matches, type, query;
+        query = raw;
+
+        matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
+        if (matches) {
+            type = matches[1].replace(/^const$/, "constant");
+            query = query.substring(matches[0].length);
+        }
+
+        return {
+            raw: raw,
+            query: query,
+            type: type,
+            id: query + type
+        };
+    }
+
+    function initSearchNav() {
+        var hoverTimeout;
+
+        var click_func = function(e) {
+            var el = e.target;
+            // to retrieve the real "owner" of the event.
+            while (el.tagName !== "TR") {
+                el = el.parentNode;
+            }
+            var dst = e.target.getElementsByTagName("a");
+            if (dst.length < 1) {
+                return;
+            }
+            dst = dst[0];
+            if (window.location.pathname === dst.pathname) {
+                searchState.hideResults();
+                document.location.href = dst.href;
+            }
+        };
+        var mouseover_func = function(e) {
+            if (searchState.mouseMovedAfterSearch) {
+                var el = e.target;
+                // to retrieve the real "owner" of the event.
+                while (el.tagName !== "TR") {
+                    el = el.parentNode;
+                }
+                clearTimeout(hoverTimeout);
+                hoverTimeout = setTimeout(function() {
+                    onEachLazy(document.getElementsByClassName("search-results"), function(e) {
+                        onEachLazy(e.getElementsByClassName("result"), function(i_e) {
+                            removeClass(i_e, "highlighted");
+                        });
+                    });
+                    addClass(el, "highlighted");
+                }, 20);
+            }
+        };
+        onEachLazy(document.getElementsByClassName("search-results"), function(e) {
+            onEachLazy(e.getElementsByClassName("result"), function(i_e) {
+                i_e.onclick = click_func;
+                i_e.onmouseover = mouseover_func;
+            });
+        });
+
+        searchState.input.onkeydown = function(e) {
+            // "actives" references the currently highlighted item in each search tab.
+            // Each array in "actives" represents a tab.
+            var actives = [[], [], []];
+            // "current" is used to know which tab we're looking into.
+            var current = 0;
+            onEachLazy(document.getElementById("results").childNodes, function(e) {
+                onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) {
+                    actives[current].push(h_e);
+                });
+                current += 1;
+            });
+            var SHIFT = 16;
+            var CTRL = 17;
+            var ALT = 18;
+
+            var currentTab = searchState.currentTab;
+            if (e.which === 38) { // up
+                if (e.ctrlKey) { // Going through result tabs.
+                    printTab(currentTab > 0 ? currentTab - 1 : 2);
+                } else {
+                    if (!actives[currentTab].length ||
+                        !actives[currentTab][0].previousElementSibling) {
+                        return;
+                    }
+                    addClass(actives[currentTab][0].previousElementSibling, "highlighted");
+                    removeClass(actives[currentTab][0], "highlighted");
+                }
+                e.preventDefault();
+            } else if (e.which === 40) { // down
+                if (e.ctrlKey) { // Going through result tabs.
+                    printTab(currentTab > 1 ? 0 : currentTab + 1);
+                } else if (!actives[currentTab].length) {
+                    var results = document.getElementById("results").childNodes;
+                    if (results.length > 0) {
+                        var res = results[currentTab].getElementsByClassName("result");
+                        if (res.length > 0) {
+                            addClass(res[0], "highlighted");
+                        }
+                    }
+                } else if (actives[currentTab][0].nextElementSibling) {
+                    addClass(actives[currentTab][0].nextElementSibling, "highlighted");
+                    removeClass(actives[currentTab][0], "highlighted");
+                }
+                e.preventDefault();
+            } else if (e.which === 13) { // return
+                if (actives[currentTab].length) {
+                    var elem = actives[currentTab][0].getElementsByTagName("a")[0];
+                    document.location.href = elem.href;
+                }
+            } else if ([SHIFT, CTRL, ALT].indexOf(e.which) !== -1) {
+                // Does nothing, it's just to avoid losing "focus" on the highlighted element.
+            } else if (actives[currentTab].length > 0) {
+                removeClass(actives[currentTab][0], "highlighted");
+            }
+        };
+    }
+
+    function buildHrefAndPath(item) {
+        var displayPath;
+        var href;
+        var type = itemTypes[item.ty];
+        var name = item.name;
+        var path = item.path;
+
+        if (type === "mod") {
+            displayPath = path + "::";
+            href = window.rootPath + path.replace(/::/g, "/") + "/" +
+                   name + "/index.html";
+        } else if (type === "primitive" || type === "keyword") {
+            displayPath = "";
+            href = window.rootPath + path.replace(/::/g, "/") +
+                   "/" + type + "." + name + ".html";
+        } else if (type === "externcrate") {
+            displayPath = "";
+            href = window.rootPath + name + "/index.html";
+        } else if (item.parent !== undefined) {
+            var myparent = item.parent;
+            var anchor = "#" + type + "." + name;
+            var parentType = itemTypes[myparent.ty];
+            var pageType = parentType;
+            var pageName = myparent.name;
+
+            if (parentType === "primitive") {
+                displayPath = myparent.name + "::";
+            } else if (type === "structfield" && parentType === "variant") {
+                // Structfields belonging to variants are special: the
+                // final path element is the enum name.
+                var enumNameIdx = item.path.lastIndexOf("::");
+                var enumName = item.path.substr(enumNameIdx + 2);
+                path = item.path.substr(0, enumNameIdx);
+                displayPath = path + "::" + enumName + "::" + myparent.name + "::";
+                anchor = "#variant." + myparent.name + ".field." + name;
+                pageType = "enum";
+                pageName = enumName;
+            } else {
+                displayPath = path + "::" + myparent.name + "::";
+            }
+            href = window.rootPath + path.replace(/::/g, "/") +
+                   "/" + pageType +
+                   "." + pageName +
+                   ".html" + anchor;
+        } else {
+            displayPath = item.path + "::";
+            href = window.rootPath + item.path.replace(/::/g, "/") +
+                   "/" + type + "." + name + ".html";
+        }
+        return [displayPath, href];
+    }
+
+    function escape(content) {
+        var h1 = document.createElement("h1");
+        h1.textContent = content;
+        return h1.innerHTML;
+    }
+
+    function pathSplitter(path) {
+        var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
+        if (tmp.endsWith("<span>")) {
+            return tmp.slice(0, tmp.length - 6);
+        }
+        return tmp;
+    }
+
+    function addTab(array, query, display) {
+        var extraStyle = "";
+        if (display === false) {
+            extraStyle = " style=\"display: none;\"";
+        }
+
+        var output = "";
+        var duplicates = {};
+        var length = 0;
+        if (array.length > 0) {
+            output = "<table class=\"search-results\"" + extraStyle + ">";
+
+            array.forEach(function(item) {
+                var name, type;
+
+                name = item.name;
+                type = itemTypes[item.ty];
+
+                if (item.is_alias !== true) {
+                    if (duplicates[item.fullPath]) {
+                        return;
+                    }
+                    duplicates[item.fullPath] = true;
+                }
+                length += 1;
+
+                output += "<tr class=\"" + type + " result\"><td>" +
+                          "<a href=\"" + item.href + "\">" +
+                          (item.is_alias === true ?
+                           ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " +
+                              "class=\"grey\"><i>&nbsp;- see&nbsp;</i></span>") : "") +
+                          item.displayPath + "<span class=\"" + type + "\">" +
+                          name + "</span></a></td><td>" +
+                          "<a href=\"" + item.href + "\">" +
+                          "<span class=\"desc\">" + item.desc +
+                          "&nbsp;</span></a></td></tr>";
+            });
+            output += "</table>";
+        } else {
+            output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" +
+                "Try on <a href=\"https://duckduckgo.com/?q=" +
+                encodeURIComponent("rust " + query.query) +
+                "\">DuckDuckGo</a>?<br/><br/>" +
+                "Or try looking in one of these:<ul><li>The <a " +
+                "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
+                " for technical details about the language.</li><li><a " +
+                "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " +
+                "Example</a> for expository code examples.</a></li><li>The <a " +
+                "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " +
+                "introductions to language features and the language itself.</li><li><a " +
+                "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
+                " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>";
+        }
+        return [output, length];
+    }
+
+    function makeTabHeader(tabNb, text, nbElems) {
+        if (searchState.currentTab === tabNb) {
+            return "<button class=\"selected\">" + text +
+                   " <div class=\"count\">(" + nbElems + ")</div></button>";
+        }
+        return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
+    }
+
+    function showResults(results) {
+        var search = searchState.outputElement();
+        if (results.others.length === 1
+            && getSettingValue("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 !== searchState.loadingText))
+        {
+            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(searchState.input.value);
+
+        currentResults = query.id;
+
+        var ret_others = addTab(results.others, query);
+        var ret_in_args = addTab(results.in_args, query, false);
+        var ret_returned = addTab(results.returned, query, false);
+
+        // Navigate to the relevant tab if the current tab is empty, like in case users search
+        // for "-> String". If they had selected another tab previously, they have to click on
+        // it again.
+        var currentTab = searchState.currentTab;
+        if ((currentTab === 0 && ret_others[1] === 0) ||
+                (currentTab === 1 && ret_in_args[1] === 0) ||
+                (currentTab === 2 && ret_returned[1] === 0)) {
+            if (ret_others[1] !== 0) {
+                currentTab = 0;
+            } else if (ret_in_args[1] !== 0) {
+                currentTab = 1;
+            } else if (ret_returned[1] !== 0) {
+                currentTab = 2;
+            }
+        }
+
+        var output = "<h1>Results for " + escape(query.query) +
+            (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
+            "<div id=\"titles\">" +
+            makeTabHeader(0, "In Names", ret_others[1]) +
+            makeTabHeader(1, "In Parameters", ret_in_args[1]) +
+            makeTabHeader(2, "In Return Types", ret_returned[1]) +
+            "</div><div id=\"results\">" +
+            ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
+
+        search.innerHTML = output;
+        searchState.showResults(search);
+        initSearchNav();
+        var elems = document.getElementById("titles").childNodes;
+        elems[0].onclick = function() { printTab(0); };
+        elems[1].onclick = function() { printTab(1); };
+        elems[2].onclick = function() { printTab(2); };
+        printTab(currentTab);
+    }
+
+    function execSearch(query, searchWords, filterCrates) {
+        function getSmallest(arrays, positions, notDuplicates) {
+            var start = null;
+
+            for (var it = 0, len = positions.length; it < len; ++it) {
+                if (arrays[it].length > positions[it] &&
+                    (start === null || start > arrays[it][positions[it]].lev) &&
+                    !notDuplicates[arrays[it][positions[it]].fullPath]) {
+                    start = arrays[it][positions[it]].lev;
+                }
+            }
+            return start;
+        }
+
+        function mergeArrays(arrays) {
+            var ret = [];
+            var positions = [];
+            var notDuplicates = {};
+
+            for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) {
+                positions.push(0);
+            }
+            while (ret.length < MAX_RESULTS) {
+                var smallest = getSmallest(arrays, positions, notDuplicates);
+
+                if (smallest === null) {
+                    break;
+                }
+                for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) {
+                    if (arrays[x].length > positions[x] &&
+                            arrays[x][positions[x]].lev === smallest &&
+                            !notDuplicates[arrays[x][positions[x]].fullPath]) {
+                        ret.push(arrays[x][positions[x]]);
+                        notDuplicates[arrays[x][positions[x]].fullPath] = true;
+                        positions[x] += 1;
+                    }
+                }
+            }
+            return ret;
+        }
+
+        var queries = query.raw.split(",");
+        var results = {
+            "in_args": [],
+            "returned": [],
+            "others": [],
+        };
+
+        for (var i = 0, len = queries.length; i < len; ++i) {
+            query = queries[i].trim();
+            if (query.length !== 0) {
+                var tmp = execQuery(getQuery(query), searchWords, filterCrates);
+
+                results.in_args.push(tmp.in_args);
+                results.returned.push(tmp.returned);
+                results.others.push(tmp.others);
+            }
+        }
+        if (queries.length > 1) {
+            return {
+                "in_args": mergeArrays(results.in_args),
+                "returned": mergeArrays(results.returned),
+                "others": mergeArrays(results.others),
+            };
+        }
+        return {
+            "in_args": results.in_args[0],
+            "returned": results.returned[0],
+            "others": results.others[0],
+        };
+    }
+
+    function getFilterCrates() {
+        var elem = document.getElementById("crate-search");
+
+        if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) {
+            return elem.value;
+        }
+        return undefined;
+    }
+
+    function search(e, forced) {
+        var params = searchState.getQueryStringParams();
+        var query = getQuery(searchState.input.value.trim());
+
+        if (e) {
+            e.preventDefault();
+        }
+
+        if (query.query.length === 0) {
+            return;
+        }
+        if (forced !== true && query.id === currentResults) {
+            if (query.query.length > 0) {
+                searchState.putBackSearch(searchState.input);
+            }
+            return;
+        }
+
+        // Update document title to maintain a meaningful browser history
+        searchState.title = "Results for " + query.query + " - Rust";
+
+        // Because searching is incremental by character, only the most
+        // recent search query is added to the browser history.
+        if (searchState.browserSupportsHistoryApi()) {
+            var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) +
+                window.location.hash;
+            if (!history.state && !params.search) {
+                history.pushState(query, "", newURL);
+            } else {
+                history.replaceState(query, "", newURL);
+            }
+        }
+
+        var filterCrates = getFilterCrates();
+        showResults(execSearch(query, index, filterCrates));
+    }
+
+    function buildIndex(rawSearchIndex) {
+        searchIndex = [];
+        var searchWords = [];
+        var i, word;
+        var currentIndex = 0;
+        var id = 0;
+
+        for (var crate in rawSearchIndex) {
+            if (!hasOwnProperty(rawSearchIndex, crate)) { continue; }
+
+            var crateSize = 0;
+
+            searchWords.push(crate);
+            var normalizedName = crate.indexOf("_") === -1
+                ? crate
+                : crate.replace(/_/g, "");
+            // This object should have exactly the same set of fields as the "row"
+            // object defined below. Your JavaScript runtime will thank you.
+            // https://mathiasbynens.be/notes/shapes-ics
+            var crateRow = {
+                crate: crate,
+                ty: 1, // == ExternCrate
+                name: crate,
+                path: "",
+                desc: rawSearchIndex[crate].doc,
+                parent: undefined,
+                type: null,
+                id: id,
+                normalizedName: normalizedName,
+            };
+            id += 1;
+            searchIndex.push(crateRow);
+            currentIndex += 1;
+
+            // an array of (Number) item types
+            var itemTypes = rawSearchIndex[crate].t;
+            // an array of (String) item names
+            var itemNames = rawSearchIndex[crate].n;
+            // an array of (String) full paths (or empty string for previous path)
+            var itemPaths = rawSearchIndex[crate].q;
+            // an array of (String) descriptions
+            var itemDescs = rawSearchIndex[crate].d;
+            // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
+            var itemParentIdxs = rawSearchIndex[crate].i;
+            // an array of (Object | null) the type of the function, if any
+            var itemFunctionSearchTypes = rawSearchIndex[crate].f;
+            // an array of [(Number) item type,
+            //              (String) name]
+            var paths = rawSearchIndex[crate].p;
+            // a array of [(String) alias name
+            //             [Number] index to items]
+            var aliases = rawSearchIndex[crate].a;
+
+            // convert `rawPaths` entries into object form
+            var len = paths.length;
+            for (i = 0; i < len; ++i) {
+                paths[i] = {ty: paths[i][0], name: paths[i][1]};
+            }
+
+            // convert `item*` into an object form, and construct word indices.
+            //
+            // before any analysis is performed lets gather the search terms to
+            // search against apart from the rest of the data.  This is a quick
+            // operation that is cached for the life of the page state so that
+            // all other search operations have access to this cached data for
+            // faster analysis operations
+            len = itemTypes.length;
+            var lastPath = "";
+            for (i = 0; i < len; ++i) {
+                // This object should have exactly the same set of fields as the "crateRow"
+                // object defined above.
+                if (typeof itemNames[i] === "string") {
+                    word = itemNames[i].toLowerCase();
+                    searchWords.push(word);
+                } else {
+                    word = "";
+                    searchWords.push("");
+                }
+                var normalizedName = word.indexOf("_") === -1
+                    ? word
+                    : word.replace(/_/g, "");
+                var row = {
+                    crate: crate,
+                    ty: itemTypes[i],
+                    name: itemNames[i],
+                    path: itemPaths[i] ? itemPaths[i] : lastPath,
+                    desc: itemDescs[i],
+                    parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
+                    type: itemFunctionSearchTypes[i],
+                    id: id,
+                    normalizedName: normalizedName,
+                };
+                id += 1;
+                searchIndex.push(row);
+                lastPath = row.path;
+                crateSize += 1;
+            }
+
+            if (aliases) {
+                ALIASES[crate] = {};
+                var j, local_aliases;
+                for (var alias_name in aliases) {
+                    if (!aliases.hasOwnProperty(alias_name)) { continue; }
+
+                    if (!ALIASES[crate].hasOwnProperty(alias_name)) {
+                        ALIASES[crate][alias_name] = [];
+                    }
+                    local_aliases = aliases[alias_name];
+                    for (j = 0, len = local_aliases.length; j < len; ++j) {
+                        ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
+                    }
+                }
+            }
+            currentIndex += crateSize;
+        }
+        return searchWords;
+    }
+
+    function registerSearchEvents() {
+        var searchAfter500ms = function() {
+            searchState.clearInputTimeout();
+            if (searchState.input.value.length === 0) {
+                if (searchState.browserSupportsHistoryApi()) {
+                    history.replaceState("", window.currentCrate + " - Rust",
+                        getNakedUrl() + window.location.hash);
+                }
+                searchState.hideResults();
+            } else {
+                searchState.timeout = setTimeout(search, 500);
+            }
+        };
+        searchState.input.onkeyup = searchAfter500ms;
+        searchState.input.oninput = searchAfter500ms;
+        document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
+            e.preventDefault();
+            searchState.clearInputTimeout();
+            search();
+        };
+        searchState.input.onchange = function(e) {
+            if (e.target !== document.activeElement) {
+                // To prevent doing anything when it's from a blur event.
+                return;
+            }
+            // Do NOT e.preventDefault() here. It will prevent pasting.
+            searchState.clearInputTimeout();
+            // zero-timeout necessary here because at the time of event handler execution the
+            // pasted content is not in the input field yet. Shouldn’t make any difference for
+            // change, though.
+            setTimeout(search, 0);
+        };
+        searchState.input.onpaste = searchState.input.onchange;
+
+        var selectCrate = document.getElementById("crate-search");
+        if (selectCrate) {
+            selectCrate.onchange = function() {
+                updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value);
+                search(undefined, true);
+            };
+        }
+
+        // Push and pop states are used to add search results to the browser
+        // history.
+        if (searchState.browserSupportsHistoryApi()) {
+            // Store the previous <title> so we can revert back to it later.
+            var previousTitle = document.title;
+
+            window.addEventListener("popstate", function(e) {
+                var params = searchState.getQueryStringParams();
+                // Revert to the previous title manually since the History
+                // API ignores the title parameter.
+                document.title = previousTitle;
+                // When browsing forward to search results the previous
+                // search will be repeated, so the currentResults are
+                // cleared to ensure the search is successful.
+                currentResults = null;
+                // Synchronize search bar with query string state and
+                // perform the search. This will empty the bar if there's
+                // nothing there, which lets you really go back to a
+                // previous state with nothing in the bar.
+                if (params.search && params.search.length > 0) {
+                    searchState.input.value = params.search;
+                    // Some browsers fire "onpopstate" for every page load
+                    // (Chrome), while others fire the event only when actually
+                    // popping a state (Firefox), which is why search() is
+                    // called both here and at the end of the startSearch()
+                    // function.
+                    search(e);
+                } else {
+                    searchState.input.value = "";
+                    // When browsing back from search results the main page
+                    // visibility must be reset.
+                    searchState.hideResults();
+                }
+            });
+        }
+
+        // This is required in firefox to avoid this problem: Navigating to a search result
+        // with the keyboard, hitting enter, and then hitting back would take you back to
+        // the doc page, rather than the search that should overlay it.
+        // This was an interaction between the back-forward cache and our handlers
+        // that try to sync state between the URL and the search input. To work around it,
+        // do a small amount of re-init on page show.
+        window.onpageshow = function(){
+            var qSearch = searchState.getQueryStringParams().search;
+            if (searchState.input.value === "" && qSearch) {
+                searchState.input.value = qSearch;
+            }
+            search();
+        };
+    }
+
+    index = buildIndex(rawSearchIndex);
+    registerSearchEvents();
+    // If there's a search term in the URL, execute the search now.
+    if (searchState.getQueryStringParams().search) {
+        search();
+    }
+};
+
+if (window.searchIndex !== undefined) {
+  initSearch(window.searchIndex);
+}
+
+})();
index c68128516d252df82d1f873452bd17c8ca8e62c6..2ed87fdedaec686616e46c3b6435bd5d3b63384d 100644 (file)
@@ -1,6 +1,5 @@
 // From rust:
 /* global resourcesSuffix */
-
 var darkThemes = ["dark", "ayu"];
 window.currentTheme = document.getElementById("themeStyle");
 window.mainTheme = document.getElementById("mainThemeStyle");
index b24f4035ca868d6056109970f5f65fca1e0b36b7..72396ec6b76bd936d7647b916b92ee8287165e5f 100644 (file)
@@ -224,7 +224,9 @@ a {
        color: #39AFD7;
 }
 
-.collapse-toggle {
+.collapse-toggle,
+details.rustdoc-toggle > summary.hideme > span,
+details.rustdoc-toggle > summary::before {
        color: #999;
 }
 
@@ -250,7 +252,8 @@ a {
        color: #929292;
 }
 
-.module-item .stab {
+.module-item .stab,
+.import-item .stab {
        color: #000;
 }
 
@@ -327,7 +330,8 @@ a.test-arrow:hover {
        color: #c5c5c5;
 }
 
-.toggle-label {
+.toggle-label,
+.code-attribute {
        color: #999;
 }
 
index e863ed03f515696172151eccc67d1dd624713480..b2003b5274120c6237c1fd05de865a2ecb3e42cc 100644 (file)
@@ -186,7 +186,9 @@ a.test-arrow {
        color: #dedede;
 }
 
-.collapse-toggle {
+.collapse-toggle,
+details.rustdoc-toggle > summary.hideme > span,
+details.rustdoc-toggle > summary::before {
        color: #999;
 }
 
@@ -215,7 +217,8 @@ a.test-arrow {
        box-shadow: 0 0 8px 4px #078dd8;
 }
 
-.module-item .stab {
+.module-item .stab,
+.import-item .stab {
        color: #ddd;
 }
 
@@ -272,7 +275,8 @@ a.test-arrow:hover{
        background-color: #4e8bca;
 }
 
-.toggle-label {
+.toggle-label,
+.code-attribute {
        color: #999;
 }
 
index 9335dd96d299af9e4d69858d6a1380f64c0a8f68..04187773b64db85b6feb8b4af1e705bad406f4fa 100644 (file)
@@ -184,7 +184,9 @@ a.test-arrow {
        color: #f5f5f5;
 }
 
-.collapse-toggle {
+.collapse-toggle,
+details.rustdoc-toggle > summary.hideme > span,
+details.rustdoc-toggle > summary::before {
        color: #999;
 }
 
@@ -213,7 +215,8 @@ a.test-arrow {
        box-shadow: 0 0 8px #078dd8;
 }
 
-.module-item .stab {
+.module-item .stab,
+.import-item .stab {
        color: #000;
 }
 
@@ -265,7 +268,8 @@ a.test-arrow:hover{
        background-color: #4e8bca;
 }
 
-.toggle-label {
+.toggle-label,
+.code-attribute {
        color: #999;
 }
 
index b3ac865d55ea1c4390c3625bc7b75990afe5a675..2b73bd5d52ee614988b79b14eea08600ed5f2f5d 100644 (file)
@@ -24,6 +24,9 @@
 /// including search behavior and docblock folding, among others.
 crate static MAIN_JS: &str = include_str!("static/main.js");
 
+/// The file contents of `search.js`, which contains the search behavior.
+crate static SEARCH_JS: &str = include_str!("static/search.js");
+
 /// The file contents of `settings.js`, which contains the JavaScript used to handle the settings
 /// page.
 crate static SETTINGS_JS: &str = include_str!("static/settings.js");
diff --git a/src/librustdoc/html/tests.rs b/src/librustdoc/html/tests.rs
new file mode 100644 (file)
index 0000000..5d537da
--- /dev/null
@@ -0,0 +1,44 @@
+use crate::html::format::href_relative_parts;
+
+fn assert_relative_path(expected: &[&str], relative_to_fqp: &[&str], fqp: &[&str]) {
+    let relative_to_fqp: Vec<String> = relative_to_fqp.iter().copied().map(String::from).collect();
+    let fqp: Vec<String> = fqp.iter().copied().map(String::from).collect();
+    assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp));
+}
+
+#[test]
+fn href_relative_parts_basic() {
+    let relative_to_fqp = &["std", "vec"];
+    let fqp = &["std", "iter"];
+    assert_relative_path(&["..", "iter"], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_parent_module() {
+    let relative_to_fqp = &["std", "vec"];
+    let fqp = &["std"];
+    assert_relative_path(&[".."], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_different_crate() {
+    let relative_to_fqp = &["std", "vec"];
+    let fqp = &["core", "iter"];
+    assert_relative_path(&["..", "..", "core", "iter"], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_same_module() {
+    let relative_to_fqp = &["std", "vec"];
+    let fqp = &["std", "vec"];
+    assert_relative_path(&[], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_child_module() {
+    let relative_to_fqp = &["std"];
+    let fqp = &["std", "vec"];
+    assert_relative_path(&["vec"], relative_to_fqp, fqp);
+}
+#[test]
+fn href_relative_parts_root() {
+    let relative_to_fqp = &[];
+    let fqp = &["std"];
+    assert_relative_path(&["std"], relative_to_fqp, fqp);
+}
index 10d5b9807b0104b3933347bc544a1cbaf2773f90..2d8c347c3c1678575188ef24b404290670862e21 100644 (file)
@@ -10,7 +10,6 @@
 use rustc_hir::def::CtorKind;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::{DefId, CRATE_DEF_INDEX};
-use rustc_span::symbol::Symbol;
 use rustc_span::Pos;
 
 use rustdoc_json_types::*;
@@ -34,10 +33,18 @@ pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
                 did.map(|did| (link.clone(), from_def_id(did)))
             })
             .collect();
-        let clean::Item { span, name, attrs, kind, visibility, def_id } = item;
-        let inner = match *kind {
+        let docs = item.attrs.collapsed_doc_value();
+        let attrs = item
+            .attrs
+            .other_attrs
+            .iter()
+            .map(rustc_ast_pretty::pprust::attribute_to_string)
+            .collect();
+        let span = item.span(self.tcx);
+        let clean::Item { name, attrs: _, kind: _, visibility, def_id, cfg: _ } = item;
+        let inner = match *item.kind {
             clean::StrippedItem(_) => return None,
-            kind => from_clean_item_kind(kind, self.tcx, &name),
+            _ => from_clean_item(item, self.tcx),
         };
         Some(Item {
             id: from_def_id(def_id),
@@ -45,12 +52,8 @@ pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
             name: name.map(|sym| sym.to_string()),
             span: self.convert_span(span),
             visibility: self.convert_visibility(visibility),
-            docs: attrs.collapsed_doc_value(),
-            attrs: attrs
-                .other_attrs
-                .iter()
-                .map(rustc_ast_pretty::pprust::attribute_to_string)
-                .collect(),
+            docs,
+            attrs,
             deprecation: deprecation.map(from_deprecation),
             inner,
             links,
@@ -172,10 +175,12 @@ fn from_tcx(kind: clean::TypeBindingKind, tcx: TyCtxt<'_>) -> Self {
     Id(format!("{}:{}", did.krate.as_u32(), u32::from(did.index)))
 }
 
-fn from_clean_item_kind(item: clean::ItemKind, tcx: TyCtxt<'_>, name: &Option<Symbol>) -> ItemEnum {
+fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
     use clean::ItemKind::*;
-    match item {
-        ModuleItem(m) => ItemEnum::Module(m.into_tcx(tcx)),
+    let name = item.name;
+    let is_crate = item.is_crate();
+    match *item.kind {
+        ModuleItem(m) => ItemEnum::Module(Module { is_crate, items: ids(m.items) }),
         ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
         StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
         UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
@@ -214,12 +219,6 @@ fn from_clean_item_kind(item: clean::ItemKind, tcx: TyCtxt<'_>, name: &Option<Sy
     }
 }
 
-impl FromWithTcx<clean::Module> for Module {
-    fn from_tcx(module: clean::Module, _tcx: TyCtxt<'_>) -> Self {
-        Module { is_crate: module.is_crate, items: ids(module.items) }
-    }
-}
-
 impl FromWithTcx<clean::Struct> for Struct {
     fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self {
         let clean::Struct { struct_type, generics, fields, fields_stripped } = struct_;
@@ -422,7 +421,7 @@ fn from_tcx(bare_decl: clean::BareFunctionDecl, tcx: TyCtxt<'_>) -> Self {
 
 impl FromWithTcx<clean::FnDecl> for FnDecl {
     fn from_tcx(decl: clean::FnDecl, tcx: TyCtxt<'_>) -> Self {
-        let clean::FnDecl { inputs, output, c_variadic, attrs: _ } = decl;
+        let clean::FnDecl { inputs, output, c_variadic } = decl;
         FnDecl {
             inputs: inputs
                 .values
@@ -464,6 +463,7 @@ fn from_tcx(impl_: clean::Impl, tcx: TyCtxt<'_>) -> Self {
             negative_polarity,
             synthetic,
             blanket_impl,
+            span: _span,
         } = impl_;
         Impl {
             is_unsafe: unsafety == rustc_hir::Unsafety::Unsafe,
index db3a0c5ceb167cc0d684ead3c7c455d650de8a28..96ea4b6c3b8c19665d5e0b1b2977e3d4efd1573b 100644 (file)
@@ -14,7 +14,6 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
-use rustc_span::{edition::Edition, Symbol};
 
 use rustdoc_json_types as types;
 
@@ -134,7 +133,6 @@ fn descr() -> &'static str {
     fn init(
         krate: clean::Crate,
         options: RenderOptions,
-        _edition: Edition,
         cache: Cache,
         tcx: TyCtxt<'tcx>,
     ) -> Result<(Self, clean::Crate), Error> {
@@ -183,32 +181,11 @@ fn item(&mut self, item: clean::Item) -> Result<(), Error> {
         Ok(())
     }
 
-    fn mod_item_in(&mut self, item: &clean::Item, _module_name: &str) -> Result<(), Error> {
-        use clean::types::ItemKind::*;
-        if let ModuleItem(m) = &*item.kind {
-            for item in &m.items {
-                match &*item.kind {
-                    // These don't have names so they don't get added to the output by default
-                    ImportItem(_) => self.item(item.clone()).unwrap(),
-                    ExternCrateItem { .. } => self.item(item.clone()).unwrap(),
-                    ImplItem(i) => i.items.iter().for_each(|i| self.item(i.clone()).unwrap()),
-                    _ => {}
-                }
-            }
-        }
-        self.item(item.clone()).unwrap();
-        Ok(())
-    }
-
-    fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> {
-        Ok(())
+    fn mod_item_in(&mut self, _item: &clean::Item) -> Result<(), Error> {
+        unreachable!("RUN_ON_MODULE = false should never call mod_item_in")
     }
 
-    fn after_krate(
-        &mut self,
-        _crate_name: Symbol,
-        _diag: &rustc_errors::Handler,
-    ) -> Result<(), Error> {
+    fn after_krate(&mut self) -> Result<(), Error> {
         debug!("Done with crate");
         let mut index = (*self.index).clone().into_inner();
         index.extend(self.get_trait_items());
index b9c4bbdceb27643206086cda1a9f10d1188208e9..26aaf0db6f6204edf85b8f4d67d74ddb37c57bfa 100644 (file)
@@ -289,7 +289,13 @@ fn opts() -> Vec<RustcOptGroup> {
         stable("cfg", |o| o.optmulti("", "cfg", "pass a --cfg to rustc", "")),
         stable("extern", |o| o.optmulti("", "extern", "pass an --extern to rustc", "NAME[=PATH]")),
         unstable("extern-html-root-url", |o| {
-            o.optmulti("", "extern-html-root-url", "base URL to use for dependencies", "NAME=URL")
+            o.optmulti(
+                "",
+                "extern-html-root-url",
+                "base URL to use for dependencies; for example, \
+                 \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html",
+                "NAME=URL",
+            )
         }),
         stable("plugin-path", |o| o.optmulti("", "plugin-path", "removed", "DIR")),
         stable("C", |o| {
@@ -650,14 +656,13 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
     krate: clean::Crate,
     renderopts: config::RenderOptions,
     cache: formats::cache::Cache,
-    diag: &rustc_errors::Handler,
-    edition: rustc_span::edition::Edition,
     tcx: TyCtxt<'tcx>,
 ) -> MainResult {
-    match formats::run_format::<T>(krate, renderopts, cache, &diag, edition, tcx) {
+    match formats::run_format::<T>(krate, renderopts, cache, tcx) {
         Ok(_) => Ok(()),
         Err(e) => {
-            let mut msg = diag.struct_err(&format!("couldn't generate documentation: {}", e.error));
+            let mut msg =
+                tcx.sess.struct_err(&format!("couldn't generate documentation: {}", e.error));
             let file = e.file.display().to_string();
             if file.is_empty() {
                 msg.emit()
@@ -686,7 +691,6 @@ 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 create the Handler ahead of time because it's not Send
-    let diag_opts = (options.error_format, options.edition, options.debugging_opts.clone());
     let show_coverage = options.show_coverage;
     let run_check = options.run_check;
 
@@ -752,28 +756,12 @@ fn main_options(options: config::Options) -> MainResult {
                 }
 
                 info!("going to format");
-                let (error_format, edition, debugging_options) = diag_opts;
-                let diag = core::new_handler(error_format, None, &debugging_options);
                 match output_format {
                     config::OutputFormat::Html => sess.time("render_html", || {
-                        run_renderer::<html::render::Context<'_>>(
-                            krate,
-                            render_opts,
-                            cache,
-                            &diag,
-                            edition,
-                            tcx,
-                        )
+                        run_renderer::<html::render::Context<'_>>(krate, render_opts, cache, tcx)
                     }),
                     config::OutputFormat::Json => sess.time("render_json", || {
-                        run_renderer::<json::JsonRenderer<'_>>(
-                            krate,
-                            render_opts,
-                            cache,
-                            &diag,
-                            edition,
-                            tcx,
-                        )
+                        run_renderer::<json::JsonRenderer<'_>>(krate, render_opts, cache, tcx)
                     }),
                 }
             })
index ac0d74cbbb9d009cabec9f4a6b9d5c0ec0c4b172..56ef15eb8842017b1320425fb719f4cf8390895c 100644 (file)
@@ -1,4 +1,4 @@
-use super::{span_of_attrs, Pass};
+use super::Pass;
 use crate::clean::*;
 use crate::core::DocContext;
 use crate::fold::DocFolder;
@@ -69,8 +69,7 @@ fn fold_item(&mut self, item: Item) -> Option<Item> {
         if !dox.is_empty() {
             let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
                 let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
-                    .or_else(|| span_of_attrs(&item.attrs))
-                    .unwrap_or(item.span.inner());
+                    .unwrap_or_else(|| item.attr_span(cx.tcx));
                 cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, |lint| {
                     lint.build(msg)
                         .note("bare URLs are not automatically turned into clickable links")
index fdac33fd60e2aef31de0c1d84b5cf6c99fcd2796..c8b82fb1563dfa63d801ba92320c873bc36f028c 100644 (file)
@@ -211,7 +211,7 @@ fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
                     None,
                 );
 
-                let filename = i.span.filename(self.ctx.sess());
+                let filename = i.span(self.ctx.tcx).filename(self.ctx.sess());
                 let has_doc_example = tests.found_tests != 0;
                 let hir_id = self.ctx.tcx.hir().local_def_id_to_hir_id(i.def_id.expect_local());
                 let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
index f98f524ca4072547e84ee19a12212c9340ce0dac..68a66806e04765994cf30f6ef8d63dd09639e755 100644 (file)
@@ -9,7 +9,7 @@
 use crate::core::DocContext;
 use crate::fold::DocFolder;
 use crate::html::markdown::{self, RustCodeBlock};
-use crate::passes::{span_of_attrs, Pass};
+use crate::passes::Pass;
 
 crate const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
     name: "check-code-block-syntax",
@@ -86,7 +86,7 @@ fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeB
                 // We couldn't calculate the span of the markdown block that had the error, so our
                 // diagnostics are going to be a bit lacking.
                 let mut diag = self.cx.sess().struct_span_warn(
-                    super::span_of_attrs(&item.attrs).unwrap_or(item.span.inner()),
+                    item.attr_span(self.cx.tcx),
                     "doc comment contains an invalid Rust code block",
                 );
 
@@ -110,7 +110,7 @@ fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeB
 impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> {
     fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
         if let Some(dox) = &item.attrs.collapsed_doc_value() {
-            let sp = span_of_attrs(&item.attrs).unwrap_or(item.span.inner());
+            let sp = item.attr_span(self.cx.tcx);
             let extra = crate::html::markdown::ExtraInfo::new_did(self.cx.tcx, item.def_id, sp);
             for code_block in markdown::rust_code_blocks(&dox, &extra) {
                 self.check_rust_syntax(&item, &dox, code_block);
index 4ce7c70d4b57e2718919182ae7b02b51d1781d69..f1064756fdde7c928859b1c732aceeece6676950 100644 (file)
@@ -37,8 +37,6 @@
 use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
 use crate::passes::Pass;
 
-use super::span_of_attrs;
-
 mod early;
 crate use early::IntraLinkCrateLoader;
 
@@ -855,7 +853,9 @@ fn fold_item(&mut self, item: Item) -> Option<Item> {
             }
         });
 
-        if item.is_mod() && item.attrs.inner_docs {
+        let inner_docs = item.inner_docs(self.cx.tcx);
+
+        if item.is_mod() && inner_docs {
             self.mod_ids.push(item.def_id);
         }
 
@@ -882,7 +882,7 @@ fn fold_item(&mut self, item: Item) -> Option<Item> {
         }
 
         Some(if item.is_mod() {
-            if !item.attrs.inner_docs {
+            if !inner_docs {
                 self.mod_ids.push(item.def_id);
             }
 
@@ -1052,6 +1052,8 @@ fn resolve_link(
             };
         let mut path_str = &*path_str;
 
+        let inner_docs = item.inner_docs(self.cx.tcx);
+
         // In order to correctly resolve intra-doc links we need to
         // pick a base AST node to work from.  If the documentation for
         // this module came from an inner comment (//!) then we anchor
@@ -1063,11 +1065,8 @@ fn resolve_link(
         // we've already pushed this node onto the resolution stack but
         // for outer comments we explicitly try and resolve against the
         // parent_node first.
-        let base_node = if item.is_mod() && item.attrs.inner_docs {
-            self.mod_ids.last().copied()
-        } else {
-            parent_node
-        };
+        let base_node =
+            if item.is_mod() && inner_docs { self.mod_ids.last().copied() } else { parent_node };
 
         let mut module_id = if let Some(id) = base_node {
             id
@@ -1242,7 +1241,7 @@ fn resolve_link(
                             &ori_link.range,
                             &item.attrs,
                         )
-                        .unwrap_or_else(|| span_of_attrs(&item.attrs).unwrap_or(item.span.inner()));
+                        .unwrap_or_else(|| item.attr_span(self.cx.tcx));
 
                         rustc_session::parse::feature_err(
                             &self.cx.tcx.sess.parse_sess,
@@ -1695,13 +1694,12 @@ fn report_diagnostic(
         }
     };
 
-    let attrs = &item.attrs;
-    let sp = span_of_attrs(attrs).unwrap_or(item.span.inner());
+    let sp = item.attr_span(tcx);
 
     tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
         let mut diag = lint.build(msg);
 
-        let span = super::source_span_for_markdown_range(tcx, dox, link_range, attrs);
+        let span = super::source_span_for_markdown_range(tcx, dox, link_range, &item.attrs);
 
         if let Some(sp) = span {
             diag.set_span(sp);
@@ -1957,20 +1955,28 @@ fn split(path: &str) -> Option<(&str, &str)> {
 
 /// Report an anchor failure.
 fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: AnchorFailure) {
-    let msg = match failure {
+    let (msg, anchor_idx) = match failure {
         AnchorFailure::MultipleAnchors => {
-            format!("`{}` contains multiple anchors", diag_info.ori_link)
+            (format!("`{}` contains multiple anchors", diag_info.ori_link), 1)
         }
-        AnchorFailure::RustdocAnchorConflict(res) => format!(
-            "`{}` contains an anchor, but links to {kind}s are already anchored",
-            diag_info.ori_link,
-            kind = res.descr(),
+        AnchorFailure::RustdocAnchorConflict(res) => (
+            format!(
+                "`{}` contains an anchor, but links to {kind}s are already anchored",
+                diag_info.ori_link,
+                kind = res.descr(),
+            ),
+            0,
         ),
     };
 
     report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| {
-        if let Some(sp) = sp {
-            diag.span_label(sp, "contains invalid anchor");
+        if let Some(mut sp) = sp {
+            if let Some((fragment_offset, _)) =
+                diag_info.ori_link.char_indices().filter(|(_, x)| *x == '#').nth(anchor_idx)
+            {
+                sp = sp.with_lo(sp.lo() + rustc_span::BytePos(fragment_offset as _));
+            }
+            diag.span_label(sp, "invalid anchor");
         }
         if let AnchorFailure::RustdocAnchorConflict(Res::Primitive(_)) = failure {
             diag.note("this restriction may be lifted in a future release");
index b36358489900ed6f2a3b9de2d9189f8cc4e2bd3f..c8d2263d81d5404017d2ba2d79673b8f0b3da1b2 100644 (file)
@@ -3,7 +3,7 @@
 //! - MISSING_DOC_CODE_EXAMPLES: this lint is **UNSTABLE** and looks for public items missing doctests
 //! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doctests.
 
-use super::{span_of_attrs, Pass};
+use super::Pass;
 use crate::clean;
 use crate::clean::*;
 use crate::core::DocContext;
@@ -97,7 +97,7 @@ fn add_test(&mut self, _: String, config: LangString, _: usize) {
     if tests.found_tests == 0 && cx.tcx.sess.is_nightly_build() {
         if should_have_doc_example(cx, &item) {
             debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
-            let sp = span_of_attrs(&item.attrs).unwrap_or(item.span.inner());
+            let sp = item.attr_span(cx.tcx);
             cx.tcx.struct_span_lint_hir(
                 crate::lint::MISSING_DOC_CODE_EXAMPLES,
                 hir_id,
@@ -109,7 +109,7 @@ fn add_test(&mut self, _: String, config: LangString, _: usize) {
         cx.tcx.struct_span_lint_hir(
             crate::lint::PRIVATE_DOC_TESTS,
             hir_id,
-            span_of_attrs(&item.attrs).unwrap_or(item.span.inner()),
+            item.attr_span(cx.tcx),
             |lint| lint.build("documentation test in private item").emit(),
         );
     }
index 881feb0d87a47304defe03598a15a48612f98e8d..f29d38e3e078a51251edb7668d72fa0465f0a8ef 100644 (file)
@@ -1,4 +1,4 @@
-use super::{span_of_attrs, Pass};
+use super::Pass;
 use crate::clean::*;
 use crate::core::DocContext;
 use crate::fold::DocFolder;
@@ -181,7 +181,7 @@ fn fold_item(&mut self, item: Item) -> Option<Item> {
                 let sp = match super::source_span_for_markdown_range(tcx, &dox, range, &item.attrs)
                 {
                     Some(sp) => sp,
-                    None => span_of_attrs(&item.attrs).unwrap_or(item.span.inner()),
+                    None => item.attr_span(tcx),
                 };
                 tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| {
                     lint.build(msg).emit()
index 2369ff78b1ce05eee76299ab1e9383da4b919fbc..f5a362bfbe8c6edf9e966b0863a87127821c38b5 100644 (file)
@@ -24,7 +24,7 @@ impl DocFolder for CfgPropagator {
     fn fold_item(&mut self, mut item: Item) -> Option<Item> {
         let old_parent_cfg = self.parent_cfg.clone();
 
-        let new_cfg = match (self.parent_cfg.take(), item.attrs.cfg.take()) {
+        let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) {
             (None, None) => None,
             (Some(rc), None) | (None, Some(rc)) => Some(rc),
             (Some(mut a), Some(b)) => {
@@ -34,7 +34,7 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
             }
         };
         self.parent_cfg = new_cfg.clone();
-        item.attrs.cfg = new_cfg;
+        item.cfg = new_cfg;
 
         let result = self.fold_item_recur(item);
         self.parent_cfg = old_parent_cfg;
index c742d32cb62eab09756d992a712d76ebeedcbf53..54c4ed22f1c4be4e1588e58aacee5fbdfb288476 100644 (file)
@@ -2,8 +2,8 @@
 use rustc_span::symbol::sym;
 use std::mem;
 
-use crate::clean::Item;
-use crate::clean::{self, AttributesExt, NestedAttributesExt};
+use crate::clean;
+use crate::clean::{Item, NestedAttributesExt};
 use crate::core::DocContext;
 use crate::fold::{DocFolder, StripItem};
 use crate::passes::{ImplStripper, Pass};
index ca30d8f0d46239b0a920a3d4e32b98cf427b8efe..c9071eea78b7988aeaa6b0621d3663956990f53a 100644 (file)
@@ -71,14 +71,14 @@ fn store_path(&mut self, did: DefId) {
     }
 
     crate fn visit(mut self, krate: &'tcx hir::Crate<'_>) -> Module<'tcx> {
+        let span = krate.item.inner;
         let mut top_level_module = self.visit_mod_contents(
-            krate.item.inner,
-            &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public },
+            span,
+            &Spanned { span, node: hir::VisibilityKind::Public },
             hir::CRATE_HIR_ID,
             &krate.item,
             self.cx.tcx.crate_name,
         );
-        top_level_module.is_crate = true;
         // Attach the crate's exported macros to the top-level module.
         // In the case of macros 2.0 (`pub macro`), and for built-in `derive`s or attributes as
         // well (_e.g._, `Copy`), these are wrongly bundled in there too, so we need to fix that by
@@ -130,7 +130,7 @@ fn store_path(&mut self, did: DefId) {
     fn visit_mod_contents(
         &mut self,
         span: Span,
-        vis: &'tcx hir::Visibility<'_>,
+        vis: &hir::Visibility<'_>,
         id: hir::HirId,
         m: &'tcx hir::Mod<'tcx>,
         name: Symbol,
@@ -150,7 +150,7 @@ fn visit_mod_contents(
         om
     }
 
-    /// Tries to resolve the target of a `crate use` statement and inlines the
+    /// Tries to resolve the target of a `pub use` statement and inlines the
     /// target if it is defined locally and would not be documented otherwise,
     /// or when it is specifically requested with `please_inline`.
     /// (the latter is the case when the import is marked `doc(inline)`)
@@ -183,7 +183,7 @@ fn maybe_inline_local(
             || use_attrs.lists(sym::doc).has_word(sym::hidden);
 
         // For cross-crate impl inlining we need to know whether items are
-        // reachable in documentation -- a previously nonreachable item can be
+        // reachable in documentation -- a previously unreachable item can be
         // made reachable by cross-crate inlining which we're checking here.
         // (this is done here because we need to know this upfront).
         if !res_did.is_local() && !is_no_inline {
index 5ef9f9948fca7cb39dd6c1935ca4e819fb7a0db2..0ed6038a318e34e3d76a9e55bdebc4cfd17f902a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 5ef9f9948fca7cb39dd6c1935ca4e819fb7a0db2
+Subproject commit 0ed6038a318e34e3d76a9e55bdebc4cfd17f902a
diff --git a/src/test/codegen/issue-84268.rs b/src/test/codegen/issue-84268.rs
new file mode 100644 (file)
index 0000000..7ca1954
--- /dev/null
@@ -0,0 +1,23 @@
+// compile-flags: -O --crate-type=rlib
+#![feature(platform_intrinsics, repr_simd)]
+
+extern "platform-intrinsic" {
+    fn simd_fabs<T>(x: T) -> T;
+    fn simd_eq<T, U>(x: T, y: T) -> U;
+}
+
+#[repr(simd)]
+pub struct V([f32; 4]);
+
+#[repr(simd)]
+pub struct M([i32; 4]);
+
+#[no_mangle]
+// CHECK-LABEL: @is_infinite
+pub fn is_infinite(v: V) -> M {
+    // CHECK: fabs
+    // CHECK: cmp oeq
+    unsafe {
+        simd_eq(simd_fabs(v), V([f32::INFINITY; 4]))
+    }
+}
index 0a687078cd8eb3c7c05063fe368c447ab6b600b6..e7bb2327a6e03b807fe5480736358d47ec9263ad 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @fabs_32x2
 #[no_mangle]
 pub unsafe fn fabs_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.fabs.v2f32
+    // CHECK: call <2 x float> @llvm.fabs.v2f32
     simd_fabs(a)
 }
 
 // CHECK-LABEL: @fabs_32x4
 #[no_mangle]
 pub unsafe fn fabs_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.fabs.v4f32
+    // CHECK: call <4 x float> @llvm.fabs.v4f32
     simd_fabs(a)
 }
 
 // CHECK-LABEL: @fabs_32x8
 #[no_mangle]
 pub unsafe fn fabs_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.fabs.v8f32
+    // CHECK: call <8 x float> @llvm.fabs.v8f32
     simd_fabs(a)
 }
 
 // CHECK-LABEL: @fabs_32x16
 #[no_mangle]
 pub unsafe fn fabs_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.fabs.v16f32
+    // CHECK: call <16 x float> @llvm.fabs.v16f32
     simd_fabs(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @fabs_64x4
 #[no_mangle]
 pub unsafe fn fabs_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.fabs.v4f64
+    // CHECK: call <4 x double> @llvm.fabs.v4f64
     simd_fabs(a)
 }
 
 // CHECK-LABEL: @fabs_64x2
 #[no_mangle]
 pub unsafe fn fabs_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.fabs.v2f64
+    // CHECK: call <2 x double> @llvm.fabs.v2f64
     simd_fabs(a)
 }
 
 // CHECK-LABEL: @fabs_64x8
 #[no_mangle]
 pub unsafe fn fabs_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.fabs.v8f64
+    // CHECK: call <8 x double> @llvm.fabs.v8f64
     simd_fabs(a)
 }
index 9d47339d163cc362f45e59a1be1ff06801e87b6f..e33482d7556265705422b66307f880d26928734c 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @ceil_32x2
 #[no_mangle]
 pub unsafe fn ceil_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.ceil.v2f32
+    // CHECK: call <2 x float> @llvm.ceil.v2f32
     simd_ceil(a)
 }
 
 // CHECK-LABEL: @ceil_32x4
 #[no_mangle]
 pub unsafe fn ceil_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.ceil.v4f32
+    // CHECK: call <4 x float> @llvm.ceil.v4f32
     simd_ceil(a)
 }
 
 // CHECK-LABEL: @ceil_32x8
 #[no_mangle]
 pub unsafe fn ceil_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.ceil.v8f32
+    // CHECK: call <8 x float> @llvm.ceil.v8f32
     simd_ceil(a)
 }
 
 // CHECK-LABEL: @ceil_32x16
 #[no_mangle]
 pub unsafe fn ceil_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.ceil.v16f32
+    // CHECK: call <16 x float> @llvm.ceil.v16f32
     simd_ceil(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @ceil_64x4
 #[no_mangle]
 pub unsafe fn ceil_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.ceil.v4f64
+    // CHECK: call <4 x double> @llvm.ceil.v4f64
     simd_ceil(a)
 }
 
 // CHECK-LABEL: @ceil_64x2
 #[no_mangle]
 pub unsafe fn ceil_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.ceil.v2f64
+    // CHECK: call <2 x double> @llvm.ceil.v2f64
     simd_ceil(a)
 }
 
 // CHECK-LABEL: @ceil_64x8
 #[no_mangle]
 pub unsafe fn ceil_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.ceil.v8f64
+    // CHECK: call <8 x double> @llvm.ceil.v8f64
     simd_ceil(a)
 }
index 770b2a730377b6be40f1e4f505d2aa8497beacb1..0f52952bc0c9cbc6e71992fb59dad7fe2fb1f910 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @fcos_32x2
 #[no_mangle]
 pub unsafe fn fcos_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.cos.v2f32
+    // CHECK: call <2 x float> @llvm.cos.v2f32
     simd_fcos(a)
 }
 
 // CHECK-LABEL: @fcos_32x4
 #[no_mangle]
 pub unsafe fn fcos_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.cos.v4f32
+    // CHECK: call <4 x float> @llvm.cos.v4f32
     simd_fcos(a)
 }
 
 // CHECK-LABEL: @fcos_32x8
 #[no_mangle]
 pub unsafe fn fcos_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.cos.v8f32
+    // CHECK: call <8 x float> @llvm.cos.v8f32
     simd_fcos(a)
 }
 
 // CHECK-LABEL: @fcos_32x16
 #[no_mangle]
 pub unsafe fn fcos_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.cos.v16f32
+    // CHECK: call <16 x float> @llvm.cos.v16f32
     simd_fcos(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @fcos_64x4
 #[no_mangle]
 pub unsafe fn fcos_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.cos.v4f64
+    // CHECK: call <4 x double> @llvm.cos.v4f64
     simd_fcos(a)
 }
 
 // CHECK-LABEL: @fcos_64x2
 #[no_mangle]
 pub unsafe fn fcos_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.cos.v2f64
+    // CHECK: call <2 x double> @llvm.cos.v2f64
     simd_fcos(a)
 }
 
 // CHECK-LABEL: @fcos_64x8
 #[no_mangle]
 pub unsafe fn fcos_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.cos.v8f64
+    // CHECK: call <8 x double> @llvm.cos.v8f64
     simd_fcos(a)
 }
index 33c8605066686f028d971a22de2e1e63562cee45..1154acf6924a07eac5db8fd2a082744cca980422 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @exp_32x2
 #[no_mangle]
 pub unsafe fn exp_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.exp.v2f32
+    // CHECK: call <2 x float> @llvm.exp.v2f32
     simd_fexp(a)
 }
 
 // CHECK-LABEL: @exp_32x4
 #[no_mangle]
 pub unsafe fn exp_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.exp.v4f32
+    // CHECK: call <4 x float> @llvm.exp.v4f32
     simd_fexp(a)
 }
 
 // CHECK-LABEL: @exp_32x8
 #[no_mangle]
 pub unsafe fn exp_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.exp.v8f32
+    // CHECK: call <8 x float> @llvm.exp.v8f32
     simd_fexp(a)
 }
 
 // CHECK-LABEL: @exp_32x16
 #[no_mangle]
 pub unsafe fn exp_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.exp.v16f32
+    // CHECK: call <16 x float> @llvm.exp.v16f32
     simd_fexp(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @exp_64x4
 #[no_mangle]
 pub unsafe fn exp_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.exp.v4f64
+    // CHECK: call <4 x double> @llvm.exp.v4f64
     simd_fexp(a)
 }
 
 // CHECK-LABEL: @exp_64x2
 #[no_mangle]
 pub unsafe fn exp_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.exp.v2f64
+    // CHECK: call <2 x double> @llvm.exp.v2f64
     simd_fexp(a)
 }
 
 // CHECK-LABEL: @exp_64x8
 #[no_mangle]
 pub unsafe fn exp_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.exp.v8f64
+    // CHECK: call <8 x double> @llvm.exp.v8f64
     simd_fexp(a)
 }
index f7a8986242d17e80608cc43e9cc2d6b4c61e6697..929dc9ac8dfead5d4ae6018676141def6e366f19 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @exp2_32x2
 #[no_mangle]
 pub unsafe fn exp2_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.exp2.v2f32
+    // CHECK: call <2 x float> @llvm.exp2.v2f32
     simd_fexp2(a)
 }
 
 // CHECK-LABEL: @exp2_32x4
 #[no_mangle]
 pub unsafe fn exp2_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.exp2.v4f32
+    // CHECK: call <4 x float> @llvm.exp2.v4f32
     simd_fexp2(a)
 }
 
 // CHECK-LABEL: @exp2_32x8
 #[no_mangle]
 pub unsafe fn exp2_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.exp2.v8f32
+    // CHECK: call <8 x float> @llvm.exp2.v8f32
     simd_fexp2(a)
 }
 
 // CHECK-LABEL: @exp2_32x16
 #[no_mangle]
 pub unsafe fn exp2_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.exp2.v16f32
+    // CHECK: call <16 x float> @llvm.exp2.v16f32
     simd_fexp2(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @exp2_64x4
 #[no_mangle]
 pub unsafe fn exp2_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.exp2.v4f64
+    // CHECK: call <4 x double> @llvm.exp2.v4f64
     simd_fexp2(a)
 }
 
 // CHECK-LABEL: @exp2_64x2
 #[no_mangle]
 pub unsafe fn exp2_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.exp2.v2f64
+    // CHECK: call <2 x double> @llvm.exp2.v2f64
     simd_fexp2(a)
 }
 
 // CHECK-LABEL: @exp2_64x8
 #[no_mangle]
 pub unsafe fn exp2_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.exp2.v8f64
+    // CHECK: call <8 x double> @llvm.exp2.v8f64
     simd_fexp2(a)
 }
index a4070317a62bb8cf4f4f5dfcaa07c0b3ef0d85f7..56ca644f6bd91cce00d679d54eadf0289bcf47b4 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @floor_32x2
 #[no_mangle]
 pub unsafe fn floor_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.floor.v2f32
+    // CHECK: call <2 x float> @llvm.floor.v2f32
     simd_floor(a)
 }
 
 // CHECK-LABEL: @floor_32x4
 #[no_mangle]
 pub unsafe fn floor_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.floor.v4f32
+    // CHECK: call <4 x float> @llvm.floor.v4f32
     simd_floor(a)
 }
 
 // CHECK-LABEL: @floor_32x8
 #[no_mangle]
 pub unsafe fn floor_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.floor.v8f32
+    // CHECK: call <8 x float> @llvm.floor.v8f32
     simd_floor(a)
 }
 
 // CHECK-LABEL: @floor_32x16
 #[no_mangle]
 pub unsafe fn floor_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.floor.v16f32
+    // CHECK: call <16 x float> @llvm.floor.v16f32
     simd_floor(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @floor_64x4
 #[no_mangle]
 pub unsafe fn floor_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.floor.v4f64
+    // CHECK: call <4 x double> @llvm.floor.v4f64
     simd_floor(a)
 }
 
 // CHECK-LABEL: @floor_64x2
 #[no_mangle]
 pub unsafe fn floor_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.floor.v2f64
+    // CHECK: call <2 x double> @llvm.floor.v2f64
     simd_floor(a)
 }
 
 // CHECK-LABEL: @floor_64x8
 #[no_mangle]
 pub unsafe fn floor_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.floor.v8f64
+    // CHECK: call <8 x double> @llvm.floor.v8f64
     simd_floor(a)
 }
index 0800a498cb718f74661e7ba22d3a57df5f05bd2d..fd65cb72baa4c146bc24293336391c7f11f957eb 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @fma_32x2
 #[no_mangle]
 pub unsafe fn fma_32x2(a: f32x2, b: f32x2, c: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.fma.v2f32
+    // CHECK: call <2 x float> @llvm.fma.v2f32
     simd_fma(a, b, c)
 }
 
 // CHECK-LABEL: @fma_32x4
 #[no_mangle]
 pub unsafe fn fma_32x4(a: f32x4, b: f32x4, c: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.fma.v4f32
+    // CHECK: call <4 x float> @llvm.fma.v4f32
     simd_fma(a, b, c)
 }
 
 // CHECK-LABEL: @fma_32x8
 #[no_mangle]
 pub unsafe fn fma_32x8(a: f32x8, b: f32x8, c: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.fma.v8f32
+    // CHECK: call <8 x float> @llvm.fma.v8f32
     simd_fma(a, b, c)
 }
 
 // CHECK-LABEL: @fma_32x16
 #[no_mangle]
 pub unsafe fn fma_32x16(a: f32x16, b: f32x16, c: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.fma.v16f32
+    // CHECK: call <16 x float> @llvm.fma.v16f32
     simd_fma(a, b, c)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @fma_64x4
 #[no_mangle]
 pub unsafe fn fma_64x4(a: f64x4, b: f64x4, c: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.fma.v4f64
+    // CHECK: call <4 x double> @llvm.fma.v4f64
     simd_fma(a, b, c)
 }
 
 // CHECK-LABEL: @fma_64x2
 #[no_mangle]
 pub unsafe fn fma_64x2(a: f64x2, b: f64x2, c: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.fma.v2f64
+    // CHECK: call <2 x double> @llvm.fma.v2f64
     simd_fma(a, b, c)
 }
 
 // CHECK-LABEL: @fma_64x8
 #[no_mangle]
 pub unsafe fn fma_64x8(a: f64x8, b: f64x8, c: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.fma.v8f64
+    // CHECK: call <8 x double> @llvm.fma.v8f64
     simd_fma(a, b, c)
 }
index adc44ffd811758a49eacb72ee52974097b5ddaac..adc1919256e76ac88d93e6c4f87c925d7b87d374 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @fsqrt_32x2
 #[no_mangle]
 pub unsafe fn fsqrt_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.sqrt.v2f32
+    // CHECK: call <2 x float> @llvm.sqrt.v2f32
     simd_fsqrt(a)
 }
 
 // CHECK-LABEL: @fsqrt_32x4
 #[no_mangle]
 pub unsafe fn fsqrt_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.sqrt.v4f32
+    // CHECK: call <4 x float> @llvm.sqrt.v4f32
     simd_fsqrt(a)
 }
 
 // CHECK-LABEL: @fsqrt_32x8
 #[no_mangle]
 pub unsafe fn fsqrt_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.sqrt.v8f32
+    // CHECK: call <8 x float> @llvm.sqrt.v8f32
     simd_fsqrt(a)
 }
 
 // CHECK-LABEL: @fsqrt_32x16
 #[no_mangle]
 pub unsafe fn fsqrt_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.sqrt.v16f32
+    // CHECK: call <16 x float> @llvm.sqrt.v16f32
     simd_fsqrt(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @fsqrt_64x4
 #[no_mangle]
 pub unsafe fn fsqrt_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.sqrt.v4f64
+    // CHECK: call <4 x double> @llvm.sqrt.v4f64
     simd_fsqrt(a)
 }
 
 // CHECK-LABEL: @fsqrt_64x2
 #[no_mangle]
 pub unsafe fn fsqrt_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.sqrt.v2f64
+    // CHECK: call <2 x double> @llvm.sqrt.v2f64
     simd_fsqrt(a)
 }
 
 // CHECK-LABEL: @fsqrt_64x8
 #[no_mangle]
 pub unsafe fn fsqrt_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.sqrt.v8f64
+    // CHECK: call <8 x double> @llvm.sqrt.v8f64
     simd_fsqrt(a)
 }
index 9c236f196362ec6276509d829e18c2b8bf5a7ae7..c072519c0d65dfaa0a0044924c4c5a01dbc23b35 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @log_32x2
 #[no_mangle]
 pub unsafe fn log_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.log.v2f32
+    // CHECK: call <2 x float> @llvm.log.v2f32
     simd_flog(a)
 }
 
 // CHECK-LABEL: @log_32x4
 #[no_mangle]
 pub unsafe fn log_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.log.v4f32
+    // CHECK: call <4 x float> @llvm.log.v4f32
     simd_flog(a)
 }
 
 // CHECK-LABEL: @log_32x8
 #[no_mangle]
 pub unsafe fn log_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.log.v8f32
+    // CHECK: call <8 x float> @llvm.log.v8f32
     simd_flog(a)
 }
 
 // CHECK-LABEL: @log_32x16
 #[no_mangle]
 pub unsafe fn log_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.log.v16f32
+    // CHECK: call <16 x float> @llvm.log.v16f32
     simd_flog(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @log_64x4
 #[no_mangle]
 pub unsafe fn log_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.log.v4f64
+    // CHECK: call <4 x double> @llvm.log.v4f64
     simd_flog(a)
 }
 
 // CHECK-LABEL: @log_64x2
 #[no_mangle]
 pub unsafe fn log_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.log.v2f64
+    // CHECK: call <2 x double> @llvm.log.v2f64
     simd_flog(a)
 }
 
 // CHECK-LABEL: @log_64x8
 #[no_mangle]
 pub unsafe fn log_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.log.v8f64
+    // CHECK: call <8 x double> @llvm.log.v8f64
     simd_flog(a)
 }
index a922161affab600411039669e036f796b35eeb89..5fd64899507822b3c0c6c6bd9932b92edb652cdf 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @log10_32x2
 #[no_mangle]
 pub unsafe fn log10_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.log10.v2f32
+    // CHECK: call <2 x float> @llvm.log10.v2f32
     simd_flog10(a)
 }
 
 // CHECK-LABEL: @log10_32x4
 #[no_mangle]
 pub unsafe fn log10_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.log10.v4f32
+    // CHECK: call <4 x float> @llvm.log10.v4f32
     simd_flog10(a)
 }
 
 // CHECK-LABEL: @log10_32x8
 #[no_mangle]
 pub unsafe fn log10_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.log10.v8f32
+    // CHECK: call <8 x float> @llvm.log10.v8f32
     simd_flog10(a)
 }
 
 // CHECK-LABEL: @log10_32x16
 #[no_mangle]
 pub unsafe fn log10_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.log10.v16f32
+    // CHECK: call <16 x float> @llvm.log10.v16f32
     simd_flog10(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @log10_64x4
 #[no_mangle]
 pub unsafe fn log10_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.log10.v4f64
+    // CHECK: call <4 x double> @llvm.log10.v4f64
     simd_flog10(a)
 }
 
 // CHECK-LABEL: @log10_64x2
 #[no_mangle]
 pub unsafe fn log10_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.log10.v2f64
+    // CHECK: call <2 x double> @llvm.log10.v2f64
     simd_flog10(a)
 }
 
 // CHECK-LABEL: @log10_64x8
 #[no_mangle]
 pub unsafe fn log10_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.log10.v8f64
+    // CHECK: call <8 x double> @llvm.log10.v8f64
     simd_flog10(a)
 }
index 9624acb383fbd17f42bd34c1fd36aab833bd98e7..35175f0ca572be5aa5d38c91bf9445a1893b5ea8 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @log2_32x2
 #[no_mangle]
 pub unsafe fn log2_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.log2.v2f32
+    // CHECK: call <2 x float> @llvm.log2.v2f32
     simd_flog2(a)
 }
 
 // CHECK-LABEL: @log2_32x4
 #[no_mangle]
 pub unsafe fn log2_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.log2.v4f32
+    // CHECK: call <4 x float> @llvm.log2.v4f32
     simd_flog2(a)
 }
 
 // CHECK-LABEL: @log2_32x8
 #[no_mangle]
 pub unsafe fn log2_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.log2.v8f32
+    // CHECK: call <8 x float> @llvm.log2.v8f32
     simd_flog2(a)
 }
 
 // CHECK-LABEL: @log2_32x16
 #[no_mangle]
 pub unsafe fn log2_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.log2.v16f32
+    // CHECK: call <16 x float> @llvm.log2.v16f32
     simd_flog2(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @log2_64x4
 #[no_mangle]
 pub unsafe fn log2_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.log2.v4f64
+    // CHECK: call <4 x double> @llvm.log2.v4f64
     simd_flog2(a)
 }
 
 // CHECK-LABEL: @log2_64x2
 #[no_mangle]
 pub unsafe fn log2_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.log2.v2f64
+    // CHECK: call <2 x double> @llvm.log2.v2f64
     simd_flog2(a)
 }
 
 // CHECK-LABEL: @log2_64x8
 #[no_mangle]
 pub unsafe fn log2_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.log2.v8f64
+    // CHECK: call <8 x double> @llvm.log2.v8f64
     simd_flog2(a)
 }
index 6639e5d652b04be215b89d43e2c65e4926cdadd6..3b8d611ab675bf4e9f0008800e2bb05aec4f20f8 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @fpow_32x2
 #[no_mangle]
 pub unsafe fn fpow_32x2(a: f32x2, b: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.pow.v2f32
+    // CHECK: call <2 x float> @llvm.pow.v2f32
     simd_fpow(a, b)
 }
 
 // CHECK-LABEL: @fpow_32x4
 #[no_mangle]
 pub unsafe fn fpow_32x4(a: f32x4, b: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.pow.v4f32
+    // CHECK: call <4 x float> @llvm.pow.v4f32
     simd_fpow(a, b)
 }
 
 // CHECK-LABEL: @fpow_32x8
 #[no_mangle]
 pub unsafe fn fpow_32x8(a: f32x8, b: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.pow.v8f32
+    // CHECK: call <8 x float> @llvm.pow.v8f32
     simd_fpow(a, b)
 }
 
 // CHECK-LABEL: @fpow_32x16
 #[no_mangle]
 pub unsafe fn fpow_32x16(a: f32x16, b: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.pow.v16f32
+    // CHECK: call <16 x float> @llvm.pow.v16f32
     simd_fpow(a, b)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @fpow_64x4
 #[no_mangle]
 pub unsafe fn fpow_64x4(a: f64x4, b: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.pow.v4f64
+    // CHECK: call <4 x double> @llvm.pow.v4f64
     simd_fpow(a, b)
 }
 
 // CHECK-LABEL: @fpow_64x2
 #[no_mangle]
 pub unsafe fn fpow_64x2(a: f64x2, b: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.pow.v2f64
+    // CHECK: call <2 x double> @llvm.pow.v2f64
     simd_fpow(a, b)
 }
 
 // CHECK-LABEL: @fpow_64x8
 #[no_mangle]
 pub unsafe fn fpow_64x8(a: f64x8, b: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.pow.v8f64
+    // CHECK: call <8 x double> @llvm.pow.v8f64
     simd_fpow(a, b)
 }
index 5e82ea023d8e094070734651abdf6938a1e4ca11..e80c50c1076741b87813a386ea090a0d33e723bd 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @fpowi_32x2
 #[no_mangle]
 pub unsafe fn fpowi_32x2(a: f32x2, b: i32) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.powi.v2f32
+    // CHECK: call <2 x float> @llvm.powi.v2f32
     simd_fpowi(a, b)
 }
 
 // CHECK-LABEL: @fpowi_32x4
 #[no_mangle]
 pub unsafe fn fpowi_32x4(a: f32x4, b: i32) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.powi.v4f32
+    // CHECK: call <4 x float> @llvm.powi.v4f32
     simd_fpowi(a, b)
 }
 
 // CHECK-LABEL: @fpowi_32x8
 #[no_mangle]
 pub unsafe fn fpowi_32x8(a: f32x8, b: i32) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.powi.v8f32
+    // CHECK: call <8 x float> @llvm.powi.v8f32
     simd_fpowi(a, b)
 }
 
 // CHECK-LABEL: @fpowi_32x16
 #[no_mangle]
 pub unsafe fn fpowi_32x16(a: f32x16, b: i32) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.powi.v16f32
+    // CHECK: call <16 x float> @llvm.powi.v16f32
     simd_fpowi(a, b)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @fpowi_64x4
 #[no_mangle]
 pub unsafe fn fpowi_64x4(a: f64x4, b: i32) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.powi.v4f64
+    // CHECK: call <4 x double> @llvm.powi.v4f64
     simd_fpowi(a, b)
 }
 
 // CHECK-LABEL: @fpowi_64x2
 #[no_mangle]
 pub unsafe fn fpowi_64x2(a: f64x2, b: i32) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.powi.v2f64
+    // CHECK: call <2 x double> @llvm.powi.v2f64
     simd_fpowi(a, b)
 }
 
 // CHECK-LABEL: @fpowi_64x8
 #[no_mangle]
 pub unsafe fn fpowi_64x8(a: f64x8, b: i32) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.powi.v8f64
+    // CHECK: call <8 x double> @llvm.powi.v8f64
     simd_fpowi(a, b)
 }
index 8ca2ca86076492709a79d971b7b2fa26c871768c..9e3fab49aff63fb9ea14c54916383b7b8dda420c 100644 (file)
@@ -32,28 +32,28 @@ pub struct f32x16(pub f32, pub f32, pub f32, pub f32,
 // CHECK-LABEL: @fsin_32x2
 #[no_mangle]
 pub unsafe fn fsin_32x2(a: f32x2) -> f32x2 {
-    // CHECK: call fast <2 x float> @llvm.sin.v2f32
+    // CHECK: call <2 x float> @llvm.sin.v2f32
     simd_fsin(a)
 }
 
 // CHECK-LABEL: @fsin_32x4
 #[no_mangle]
 pub unsafe fn fsin_32x4(a: f32x4) -> f32x4 {
-    // CHECK: call fast <4 x float> @llvm.sin.v4f32
+    // CHECK: call <4 x float> @llvm.sin.v4f32
     simd_fsin(a)
 }
 
 // CHECK-LABEL: @fsin_32x8
 #[no_mangle]
 pub unsafe fn fsin_32x8(a: f32x8) -> f32x8 {
-    // CHECK: call fast <8 x float> @llvm.sin.v8f32
+    // CHECK: call <8 x float> @llvm.sin.v8f32
     simd_fsin(a)
 }
 
 // CHECK-LABEL: @fsin_32x16
 #[no_mangle]
 pub unsafe fn fsin_32x16(a: f32x16) -> f32x16 {
-    // CHECK: call fast <16 x float> @llvm.sin.v16f32
+    // CHECK: call <16 x float> @llvm.sin.v16f32
     simd_fsin(a)
 }
 
@@ -73,20 +73,20 @@ pub struct f64x8(pub f64, pub f64, pub f64, pub f64,
 // CHECK-LABEL: @fsin_64x4
 #[no_mangle]
 pub unsafe fn fsin_64x4(a: f64x4) -> f64x4 {
-    // CHECK: call fast <4 x double> @llvm.sin.v4f64
+    // CHECK: call <4 x double> @llvm.sin.v4f64
     simd_fsin(a)
 }
 
 // CHECK-LABEL: @fsin_64x2
 #[no_mangle]
 pub unsafe fn fsin_64x2(a: f64x2) -> f64x2 {
-    // CHECK: call fast <2 x double> @llvm.sin.v2f64
+    // CHECK: call <2 x double> @llvm.sin.v2f64
     simd_fsin(a)
 }
 
 // CHECK-LABEL: @fsin_64x8
 #[no_mangle]
 pub unsafe fn fsin_64x8(a: f64x8) -> f64x8 {
-    // CHECK: call fast <8 x double> @llvm.sin.v8f64
+    // CHECK: call <8 x double> @llvm.sin.v8f64
     simd_fsin(a)
 }
diff --git a/src/test/codegen/wasm_casts_nontrapping.rs b/src/test/codegen/wasm_casts_nontrapping.rs
deleted file mode 100644 (file)
index bd6073d..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-// only-wasm32
-// compile-flags: -C target-feature=+nontrapping-fptoint
-#![crate_type = "lib"]
-
-// CHECK-LABEL: @cast_f64_i64
-#[no_mangle]
-pub fn cast_f64_i64(a: f64) -> i64 {
-    // CHECK: tail call i64 @llvm.wasm.trunc.saturate.signed.i64.f64(double {{.*}})
-    // CHECK-NEXT: ret i64 {{.*}}
-    a as _
-}
-
-// CHECK-LABEL: @cast_f64_i32
-#[no_mangle]
-pub fn cast_f64_i32(a: f64) -> i32 {
-    // CHECK: tail call i32 @llvm.wasm.trunc.saturate.signed.i32.f64(double {{.*}})
-    // CHECK-NEXT: ret i32 {{.*}}
-    a as _
-}
-
-// CHECK-LABEL: @cast_f32_i64
-#[no_mangle]
-pub fn cast_f32_i64(a: f32) -> i64 {
-    // CHECK: tail call i64 @llvm.wasm.trunc.saturate.signed.i64.f32(float {{.*}})
-    // CHECK-NEXT: ret i64 {{.*}}
-    a as _
-}
-
-// CHECK-LABEL: @cast_f32_i32
-#[no_mangle]
-pub fn cast_f32_i32(a: f32) -> i32 {
-    // CHECK: tail call i32 @llvm.wasm.trunc.saturate.signed.i32.f32(float {{.*}})
-    // CHECK-NEXT: ret i32 {{.*}}
-    a as _
-}
-
-
-// CHECK-LABEL: @cast_f64_u64
-#[no_mangle]
-pub fn cast_f64_u64(a: f64) -> u64 {
-    // CHECK: tail call i64 @llvm.wasm.trunc.saturate.unsigned.i64.f64(double {{.*}})
-    // CHECK-NEXT: ret i64 {{.*}}
-    a as _
-}
-
-// CHECK-LABEL: @cast_f64_u32
-#[no_mangle]
-pub fn cast_f64_u32(a: f64) -> u32 {
-    // CHECK: tail call i32 @llvm.wasm.trunc.saturate.unsigned.i32.f64(double {{.*}})
-    // CHECK-NEXT: ret i32 {{.*}}
-    a as _
-}
-
-// CHECK-LABEL: @cast_f32_u64
-#[no_mangle]
-pub fn cast_f32_u64(a: f32) -> u64 {
-    // CHECK: tail call i64 @llvm.wasm.trunc.saturate.unsigned.i64.f32(float {{.*}})
-    // CHECK-NEXT: ret i64 {{.*}}
-    a as _
-}
-
-// CHECK-LABEL: @cast_f32_u32
-#[no_mangle]
-pub fn cast_f32_u32(a: f32) -> u32 {
-    // CHECK: tail call i32 @llvm.wasm.trunc.saturate.unsigned.i32.f32(float {{.*}})
-    // CHECK-NEXT: ret i32 {{.*}}
-    a as _
-}
-
-// CHECK-LABEL: @cast_f32_u8
-#[no_mangle]
-pub fn cast_f32_u8(a: f32) -> u8 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptoui float {{.*}} to i8
-    // CHECK-NEXT: select i1 {{.*}}, i8 {{.*}}, i8 {{.*}}
-    // CHECK-NEXT: ret i8 {{.*}}
-    a as _
-}
-
-
-
-// CHECK-LABEL: @cast_unchecked_f64_i64
-#[no_mangle]
-pub unsafe fn cast_unchecked_f64_i64(a: f64) -> i64 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptosi double {{.*}} to i64
-    // CHECK-NEXT: ret i64 {{.*}}
-    a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f64_i32
-#[no_mangle]
-pub unsafe fn cast_unchecked_f64_i32(a: f64) -> i32 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptosi double {{.*}} to i32
-    // CHECK-NEXT: ret i32 {{.*}}
-    a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_i64
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_i64(a: f32) -> i64 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptosi float {{.*}} to i64
-    // CHECK-NEXT: ret i64 {{.*}}
-    a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_i32
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_i32(a: f32) -> i32 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptosi float {{.*}} to i32
-    // CHECK-NEXT: ret i32 {{.*}}
-    a.to_int_unchecked()
-}
-
-
-// CHECK-LABEL: @cast_unchecked_f64_u64
-#[no_mangle]
-pub unsafe fn cast_unchecked_f64_u64(a: f64) -> u64 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptoui double {{.*}} to i64
-    // CHECK-NEXT: ret i64 {{.*}}
-    a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f64_u32
-#[no_mangle]
-pub unsafe fn cast_unchecked_f64_u32(a: f64) -> u32 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptoui double {{.*}} to i32
-    // CHECK-NEXT: ret i32 {{.*}}
-    a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_u64
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_u64(a: f32) -> u64 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptoui float {{.*}} to i64
-    // CHECK-NEXT: ret i64 {{.*}}
-    a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_u32
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_u32(a: f32) -> u32 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptoui float {{.*}} to i32
-    // CHECK-NEXT: ret i32 {{.*}}
-    a.to_int_unchecked()
-}
-
-// CHECK-LABEL: @cast_unchecked_f32_u8
-#[no_mangle]
-pub unsafe fn cast_unchecked_f32_u8(a: f32) -> u8 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptoui float {{.*}} to i8
-    // CHECK-NEXT: ret i8 {{.*}}
-    a.to_int_unchecked()
-}
index ed51faa7be12f7978eb9246d53fdfdf9332144e9..baf130a87917972c569e8b17edda7d95c282a58e 100644 (file)
@@ -1,5 +1,6 @@
 // only-wasm32
 // compile-flags: -C target-feature=-nontrapping-fptoint
+// min-llvm-version: 12.0
 #![crate_type = "lib"]
 
 // CHECK-LABEL: @cast_f64_i64
@@ -7,7 +8,7 @@
 pub fn cast_f64_i64(a: f64) -> i64 {
     // CHECK-NOT: fptosi double {{.*}} to i64
     // CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
-    // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptosi.sat.i64.f64{{.*}}
     a as _
 }
 
@@ -16,7 +17,7 @@ pub fn cast_f64_i64(a: f64) -> i64 {
 pub fn cast_f64_i32(a: f64) -> i32 {
     // CHECK-NOT: fptosi double {{.*}} to i32
     // CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}}
-    // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptosi.sat.i32.f64{{.*}}
     a as _
 }
 
@@ -25,7 +26,7 @@ pub fn cast_f64_i32(a: f64) -> i32 {
 pub fn cast_f32_i64(a: f32) -> i64 {
     // CHECK-NOT: fptosi float {{.*}} to i64
     // CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
-    // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptosi.sat.i64.f32{{.*}}
     a as _
 }
 
@@ -34,7 +35,7 @@ pub fn cast_f32_i64(a: f32) -> i64 {
 pub fn cast_f32_i32(a: f32) -> i32 {
     // CHECK-NOT: fptosi float {{.*}} to i32
     // CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}}
-    // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptosi.sat.i32.f32{{.*}}
     a as _
 }
 
@@ -43,7 +44,7 @@ pub fn cast_f32_i32(a: f32) -> i32 {
 pub fn cast_f64_u64(a: f64) -> u64 {
     // CHECK-NOT: fptoui double {{.*}} to i64
     // CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
-    // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i64.f64{{.*}}
     a as _
 }
 
@@ -52,7 +53,7 @@ pub fn cast_f64_u64(a: f64) -> u64 {
 pub fn cast_f64_u32(a: f64) -> u32 {
     // CHECK-NOT: fptoui double {{.*}} to i32
     // CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}}
-    // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i32.f64{{.*}}
     a as _
 }
 
@@ -61,7 +62,7 @@ pub fn cast_f64_u32(a: f64) -> u32 {
 pub fn cast_f32_u64(a: f32) -> u64 {
     // CHECK-NOT: fptoui float {{.*}} to i64
     // CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
-    // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i64.f32{{.*}}
     a as _
 }
 
@@ -70,16 +71,16 @@ pub fn cast_f32_u64(a: f32) -> u64 {
 pub fn cast_f32_u32(a: f32) -> u32 {
     // CHECK-NOT: fptoui float {{.*}} to i32
     // CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}}
-    // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i32.f32{{.*}}
     a as _
 }
 
 // CHECK-LABEL: @cast_f32_u8
 #[no_mangle]
 pub fn cast_f32_u8(a: f32) -> u8 {
-    // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}}
-    // CHECK: fptoui float {{.*}} to i8
-    // CHECK-NEXT: select i1 {{.*}}, i8 {{.*}}, i8 {{.*}}
+    // CHECK-NOT: fptoui float {{.*}} to i8
+    // CHECK-NOT: select i1 {{.*}}, i8 {{.*}}, i8 {{.*}}
+    // CHECK: {{.*}} call {{.*}} @llvm.fptoui.sat.i8.f32{{.*}}
     a as _
 }
 
index 5382dd1a3234b4b5427afa611f57b80c68fa93ac..5c68a88f2fb1b2a2ece9b009a61d5c57e9450c55 100644 (file)
@@ -2,8 +2,6 @@
 
 // compile-flags:-g
 
-#![feature(non_ascii_idents)]
-
 // This test checks whether debuginfo generation can handle multi-byte UTF-8
 // characters at the end of a block. There's no need to do anything in the
 // debugger -- just make sure that the compiler doesn't crash.
diff --git a/src/test/incremental/issue-84252-global-alloc.rs b/src/test/incremental/issue-84252-global-alloc.rs
new file mode 100644 (file)
index 0000000..d2438df
--- /dev/null
@@ -0,0 +1,12 @@
+// revisions: cfail1 cfail2
+// build-pass
+
+#![crate_type="lib"]
+#![crate_type="cdylib"]
+
+#[allow(unused_imports)]
+use std::alloc::System;
+
+#[cfg(cfail1)]
+#[global_allocator]
+static ALLOC: System = System;
index e0a5937c24686931a805b0ede3534b61469c2cc3..ae9487473d0ca9bbf761601ffa6c7fbde242ab59 100644 (file)
@@ -1,6 +1,6 @@
     1|       |#![allow(unused_assignments, dead_code)]
     2|       |
-    3|       |// compile-flags: --edition=2018 -C opt-level=1 # fix in rustc_mir/monomorphize/partitioning/mod.rs
+    3|       |// compile-flags: --edition=2018 -C opt-level=1
     4|       |
     5|      1|async fn c(x: u8) -> u8 {
     6|      1|    if x == 8 {
diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.async2.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.async2.txt
new file mode 100644 (file)
index 0000000..8a44543
--- /dev/null
@@ -0,0 +1,115 @@
+    1|       |// compile-flags: --edition=2018
+    2|       |
+    3|       |use core::{
+    4|       |    future::Future,
+    5|       |    marker::Send,
+    6|       |    pin::Pin,
+    7|       |};
+    8|       |
+    9|      1|fn non_async_func() {
+   10|      1|    println!("non_async_func was covered");
+   11|      1|    let b = true;
+   12|      1|    if b {
+   13|      1|        println!("non_async_func println in block");
+   14|      1|    }
+   15|      1|}
+   16|       |
+   17|       |// FIXME(#83985): The auto-generated closure in an async function is failing to include
+   18|       |// the println!() and `let` assignment lines in the coverage code region(s), as it does in the
+   19|       |// non-async function above, unless the `println!()` is inside a covered block.
+   20|      1|async fn async_func() {
+   21|      1|    println!("async_func was covered");
+   22|      1|    let b = true;
+   23|      1|    if b {
+   24|      1|        println!("async_func println in block");
+   25|      1|    }
+                   ^0
+   26|      1|}
+   27|       |
+   28|       |// FIXME(#83985): As above, this async function only has the `println!()` macro call, which is not
+   29|       |// showing coverage, so the entire async closure _appears_ uncovered; but this is not exactly true.
+   30|       |// It's only certain kinds of lines and/or their context that results in missing coverage.
+   31|      1|async fn async_func_just_println() {
+   32|      1|    println!("async_func_just_println was covered");
+   33|      1|}
+   34|       |
+   35|      1|fn main() {
+   36|      1|    println!("codecovsample::main");
+   37|      1|
+   38|      1|    non_async_func();
+   39|      1|
+   40|      1|    executor::block_on(async_func());
+   41|      1|    executor::block_on(async_func_just_println());
+   42|      1|}
+   43|       |
+   44|       |mod executor {
+   45|       |    use core::{
+   46|       |        future::Future,
+   47|       |        pin::Pin,
+   48|       |        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+   49|       |    };
+   50|       |
+   51|      2|    pub fn block_on<F: Future>(mut future: F) -> F::Output {
+   52|      2|        let mut future = unsafe { Pin::new_unchecked(&mut future) };
+   53|      2|        use std::hint::unreachable_unchecked;
+   54|      2|        static VTABLE: RawWakerVTable = RawWakerVTable::new(
+   55|      2|            |_| unsafe { unreachable_unchecked() }, // clone
+                              ^0
+   56|      2|            |_| unsafe { unreachable_unchecked() }, // wake
+                              ^0
+   57|      2|            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
+                              ^0
+   58|      2|            |_| (),
+   59|      2|        );
+   60|      2|        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+   61|      2|        let mut context = Context::from_waker(&waker);
+   62|       |
+   63|       |        loop {
+   64|      2|            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+   65|      2|                break val;
+   66|      0|            }
+   67|       |        }
+   68|      2|    }
+  ------------------
+  | async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func::{closure#0}>>:
+  |   51|      1|    pub fn block_on<F: Future>(mut future: F) -> F::Output {
+  |   52|      1|        let mut future = unsafe { Pin::new_unchecked(&mut future) };
+  |   53|      1|        use std::hint::unreachable_unchecked;
+  |   54|      1|        static VTABLE: RawWakerVTable = RawWakerVTable::new(
+  |   55|      1|            |_| unsafe { unreachable_unchecked() }, // clone
+  |   56|      1|            |_| unsafe { unreachable_unchecked() }, // wake
+  |   57|      1|            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
+  |   58|      1|            |_| (),
+  |   59|      1|        );
+  |   60|      1|        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+  |   61|      1|        let mut context = Context::from_waker(&waker);
+  |   62|       |
+  |   63|       |        loop {
+  |   64|      1|            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+  |   65|      1|                break val;
+  |   66|      0|            }
+  |   67|       |        }
+  |   68|      1|    }
+  ------------------
+  | async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func_just_println::{closure#0}>>:
+  |   51|      1|    pub fn block_on<F: Future>(mut future: F) -> F::Output {
+  |   52|      1|        let mut future = unsafe { Pin::new_unchecked(&mut future) };
+  |   53|      1|        use std::hint::unreachable_unchecked;
+  |   54|      1|        static VTABLE: RawWakerVTable = RawWakerVTable::new(
+  |   55|      1|            |_| unsafe { unreachable_unchecked() }, // clone
+  |   56|      1|            |_| unsafe { unreachable_unchecked() }, // wake
+  |   57|      1|            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
+  |   58|      1|            |_| (),
+  |   59|      1|        );
+  |   60|      1|        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+  |   61|      1|        let mut context = Context::from_waker(&waker);
+  |   62|       |
+  |   63|       |        loop {
+  |   64|      1|            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+  |   65|      1|                break val;
+  |   66|      0|            }
+  |   67|       |        }
+  |   68|      1|    }
+  ------------------
+   69|       |}
+
diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.continue.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.continue.txt
new file mode 100644 (file)
index 0000000..28e0f19
--- /dev/null
@@ -0,0 +1,75 @@
+    1|       |#![allow(unused_assignments, unused_variables)]
+    2|       |
+    3|      1|fn main() {
+    4|      1|    let is_true = std::env::args().len() == 1;
+    5|      1|
+    6|      1|    let mut x = 0;
+    7|     11|    for _ in 0..10 {
+                      ^10
+    8|     10|        match is_true {
+    9|       |            true => {
+   10|     10|                continue;
+   11|       |            }
+   12|      0|            _ => {
+   13|      0|                x = 1;
+   14|      0|            }
+   15|      0|        }
+   16|      0|        x = 3;
+   17|       |    }
+   18|     11|    for _ in 0..10 {
+                      ^10
+   19|     10|        match is_true {
+   20|      0|            false => {
+   21|      0|                x = 1;
+   22|      0|            }
+   23|       |            _ => {
+   24|     10|                continue;
+   25|       |            }
+   26|       |        }
+   27|      0|        x = 3;
+   28|       |    }
+   29|     11|    for _ in 0..10 {
+                      ^10
+   30|     10|        match is_true {
+   31|     10|            true => {
+   32|     10|                x = 1;
+   33|     10|            }
+   34|       |            _ => {
+   35|      0|                continue;
+   36|       |            }
+   37|       |        }
+   38|     10|        x = 3;
+   39|       |    }
+   40|     11|    for _ in 0..10 {
+                      ^10
+   41|     10|        if is_true {
+   42|     10|            continue;
+   43|      0|        }
+   44|      0|        x = 3;
+   45|       |    }
+   46|     11|    for _ in 0..10 {
+                      ^10
+   47|     10|        match is_true {
+   48|      0|            false => {
+   49|      0|                x = 1;
+   50|      0|            }
+   51|     10|            _ => {
+   52|     10|                let _ = x;
+   53|     10|            }
+   54|       |        }
+   55|     10|        x = 3;
+   56|       |    }
+   57|      1|    for _ in 0..10 {
+   58|      1|        match is_true {
+   59|      0|            false => {
+   60|      0|                x = 1;
+   61|      0|            }
+   62|       |            _ => {
+   63|      1|                break;
+   64|       |            }
+   65|       |        }
+   66|      0|        x = 3;
+   67|       |    }
+   68|       |    let _ = x;
+   69|      1|}
+
diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-83601.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-83601.txt
new file mode 100644 (file)
index 0000000..46f3add
--- /dev/null
@@ -0,0 +1,22 @@
+    1|       |// Shows that rust-lang/rust/83601 is resolved
+    2|       |
+    3|      3|#[derive(Debug, PartialEq, Eq)]
+                              ^2
+  ------------------
+  | <issue_83601::Foo as core::cmp::PartialEq>::eq:
+  |    3|      2|#[derive(Debug, PartialEq, Eq)]
+  ------------------
+  | Unexecuted instantiation: <issue_83601::Foo as core::cmp::PartialEq>::ne
+  ------------------
+    4|       |struct Foo(u32);
+    5|       |
+    6|      1|fn main() {
+    7|      1|    let bar = Foo(1);
+    8|      0|    assert_eq!(bar, Foo(1));
+    9|      1|    let baz = Foo(0);
+   10|      0|    assert_ne!(baz, Foo(1));
+   11|      1|    println!("{:?}", Foo(1));
+   12|      1|    println!("{:?}", bar);
+   13|      1|    println!("{:?}", baz);
+   14|      1|}
+
diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_crate.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_crate.txt
new file mode 100644 (file)
index 0000000..c4a7b0c
--- /dev/null
@@ -0,0 +1,18 @@
+    1|       |// Enables `no_coverage` on the entire crate
+    2|       |#![feature(no_coverage)]
+    3|       |
+    4|       |#[no_coverage]
+    5|       |fn do_not_add_coverage_1() {
+    6|       |    println!("called but not covered");
+    7|       |}
+    8|       |
+    9|       |#[no_coverage]
+   10|       |fn do_not_add_coverage_2() {
+   11|       |    println!("called but not covered");
+   12|       |}
+   13|       |
+   14|      1|fn main() {
+   15|      1|    do_not_add_coverage_1();
+   16|      1|    do_not_add_coverage_2();
+   17|      1|}
+
diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt
new file mode 100644 (file)
index 0000000..16eaf7c
--- /dev/null
@@ -0,0 +1,19 @@
+    1|       |// Enables `no_coverage` on individual functions
+    2|       |
+    3|       |#[feature(no_coverage)]
+    4|       |#[no_coverage]
+    5|       |fn do_not_add_coverage_1() {
+    6|       |    println!("called but not covered");
+    7|       |}
+    8|       |
+    9|       |#[no_coverage]
+   10|       |#[feature(no_coverage)]
+   11|       |fn do_not_add_coverage_2() {
+   12|       |    println!("called but not covered");
+   13|       |}
+   14|       |
+   15|      1|fn main() {
+   16|      1|    do_not_add_coverage_1();
+   17|      1|    do_not_add_coverage_2();
+   18|      1|}
+
index 4e4dde46b344b8cef40a5b8d5b3bde2f594052b7..fc266653349509c511b999abb50f9584401d74e3 100644 (file)
@@ -2,7 +2,7 @@
     2|       |// structure of this test.
     3|       |
     4|      2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
-                       ^0            ^0      ^0 ^0  ^1       ^1 ^0^0
+                       ^0            ^0      ^0     ^1       ^1 ^0^0
   ------------------
   | Unexecuted instantiation: <partial_eq::Version as core::cmp::PartialEq>::ne
   ------------------
index c9ebffde039d842ecd660154a5383e5abf2cf917..9fca52451ed57fbcbb03fe1ea16df0384fc7ed71 100644 (file)
@@ -9,7 +9,7 @@
     9|       |    }
    10|      6|}
    11|       |
-   12|      1|fn main() -> Result<(),()> {
+   12|      1|fn test1() -> Result<(),()> {
    13|      1|    let mut
    14|      1|        countdown = 10
    15|       |    ;
    34|       |    }
    35|      0|    Ok(())
    36|      1|}
+   37|       |
+   38|       |struct Thing1;
+   39|       |impl Thing1 {
+   40|     18|    fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> {
+   41|     18|        if return_error {
+   42|      1|            Err(())
+   43|       |        } else {
+   44|     17|            Ok(Thing2{})
+   45|       |        }
+   46|     18|    }
+   47|       |}
+   48|       |
+   49|       |struct Thing2;
+   50|       |impl Thing2 {
+   51|     17|    fn call(&self, return_error: bool) -> Result<u32,()> {
+   52|     17|        if return_error {
+   53|      2|            Err(())
+   54|       |        } else {
+   55|     15|            Ok(57)
+   56|       |        }
+   57|     17|    }
+   58|       |}
+   59|       |
+   60|      1|fn test2() -> Result<(),()> {
+   61|      1|    let thing1 = Thing1{};
+   62|      1|    let mut
+   63|      1|        countdown = 10
+   64|       |    ;
+   65|       |    for
+   66|      6|        _
+   67|       |    in
+   68|      6|        0..10
+   69|       |    {
+   70|      6|        countdown
+   71|      6|            -= 1
+   72|      6|        ;
+   73|      6|        if
+   74|      6|            countdown < 5
+   75|       |        {
+   76|      1|            thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail");
+                                                            ^0
+   77|      1|            thing1
+   78|      1|                .
+   79|      1|                get_thing_2(/*return_error=*/ false)
+   80|      0|                ?
+   81|       |                .
+   82|      1|                call(/*return_error=*/ true)
+   83|      1|                .
+   84|      1|                expect_err(
+   85|      1|                    "call should fail"
+   86|      1|                );
+   87|      1|            let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?;
+                              ^0                                                ^0                          ^0
+   88|      0|            assert_eq!(val, 57);
+   89|      0|            let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?;
+   90|      0|            assert_eq!(val, 57);
+   91|       |        }
+   92|       |        else
+   93|       |        {
+   94|      5|            let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?;
+                                                                               ^0                             ^0
+   95|      5|            assert_eq!(val, 57);
+   96|      5|            let val = thing1
+   97|      5|                .get_thing_2(/*return_error=*/ false)?
+                                                                   ^0
+   98|      5|                .call(/*return_error=*/ false)?;
+                                                            ^0
+   99|      5|            assert_eq!(val, 57);
+  100|      5|            let val = thing1
+  101|      5|                .get_thing_2(/*return_error=*/ false)
+  102|      0|                ?
+  103|      5|                .call(/*return_error=*/ false)
+  104|      0|                ?
+  105|       |                ;
+  106|      5|            assert_eq!(val, 57);
+  107|       |        }
+  108|       |    }
+  109|      0|    Ok(())
+  110|      1|}
+  111|       |
+  112|      1|fn main() -> Result<(),()> {
+  113|      1|    test1().expect_err("test1 should fail");
+  114|      1|    test2()
+  115|      1|    ?
+  116|       |    ;
+  117|      0|    Ok(())
+  118|      1|}
 
index cdcbd8fca948243c33c80a3c902b11bb84f6ecc3..f5beb9ef24a0e4b355772651115ce519a316b16f 100644 (file)
    18|      2|    println!("used_only_from_bin_crate_generic_function with {:?}", arg);
    19|      2|}
   ------------------
-  | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
+  | used_crate::used_only_from_bin_crate_generic_function::<&str>:
   |   17|      1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
   |   18|      1|    println!("used_only_from_bin_crate_generic_function with {:?}", arg);
   |   19|      1|}
   ------------------
-  | used_crate::used_only_from_bin_crate_generic_function::<&str>:
+  | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
   |   17|      1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
   |   18|      1|    println!("used_only_from_bin_crate_generic_function with {:?}", arg);
   |   19|      1|}
index 67bf696d0729f13c0176f7f94ae5cd389b502138..a6e387747068abedd73e2360cdcad9d4e6e598ec 100644 (file)
@@ -1,6 +1,6 @@
 #![allow(unused_assignments, dead_code)]
 
-// compile-flags: --edition=2018 -C opt-level=1 # fix in rustc_mir/monomorphize/partitioning/mod.rs
+// compile-flags: --edition=2018 -C opt-level=1
 
 async fn c(x: u8) -> u8 {
     if x == 8 {
diff --git a/src/test/run-make-fulldeps/coverage/async2.rs b/src/test/run-make-fulldeps/coverage/async2.rs
new file mode 100644 (file)
index 0000000..6171d95
--- /dev/null
@@ -0,0 +1,69 @@
+// compile-flags: --edition=2018
+
+use core::{
+    future::Future,
+    marker::Send,
+    pin::Pin,
+};
+
+fn non_async_func() {
+    println!("non_async_func was covered");
+    let b = true;
+    if b {
+        println!("non_async_func println in block");
+    }
+}
+
+// FIXME(#83985): The auto-generated closure in an async function is failing to include
+// the println!() and `let` assignment lines in the coverage code region(s), as it does in the
+// non-async function above, unless the `println!()` is inside a covered block.
+async fn async_func() {
+    println!("async_func was covered");
+    let b = true;
+    if b {
+        println!("async_func println in block");
+    }
+}
+
+// FIXME(#83985): As above, this async function only has the `println!()` macro call, which is not
+// showing coverage, so the entire async closure _appears_ uncovered; but this is not exactly true.
+// It's only certain kinds of lines and/or their context that results in missing coverage.
+async fn async_func_just_println() {
+    println!("async_func_just_println was covered");
+}
+
+fn main() {
+    println!("codecovsample::main");
+
+    non_async_func();
+
+    executor::block_on(async_func());
+    executor::block_on(async_func_just_println());
+}
+
+mod executor {
+    use core::{
+        future::Future,
+        pin::Pin,
+        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+    };
+
+    pub fn block_on<F: Future>(mut future: F) -> F::Output {
+        let mut future = unsafe { Pin::new_unchecked(&mut future) };
+        use std::hint::unreachable_unchecked;
+        static VTABLE: RawWakerVTable = RawWakerVTable::new(
+            |_| unsafe { unreachable_unchecked() }, // clone
+            |_| unsafe { unreachable_unchecked() }, // wake
+            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
+            |_| (),
+        );
+        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+        let mut context = Context::from_waker(&waker);
+
+        loop {
+            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+                break val;
+            }
+        }
+    }
+}
diff --git a/src/test/run-make-fulldeps/coverage/continue.rs b/src/test/run-make-fulldeps/coverage/continue.rs
new file mode 100644 (file)
index 0000000..624aa98
--- /dev/null
@@ -0,0 +1,69 @@
+#![allow(unused_assignments, unused_variables)]
+
+fn main() {
+    let is_true = std::env::args().len() == 1;
+
+    let mut x = 0;
+    for _ in 0..10 {
+        match is_true {
+            true => {
+                continue;
+            }
+            _ => {
+                x = 1;
+            }
+        }
+        x = 3;
+    }
+    for _ in 0..10 {
+        match is_true {
+            false => {
+                x = 1;
+            }
+            _ => {
+                continue;
+            }
+        }
+        x = 3;
+    }
+    for _ in 0..10 {
+        match is_true {
+            true => {
+                x = 1;
+            }
+            _ => {
+                continue;
+            }
+        }
+        x = 3;
+    }
+    for _ in 0..10 {
+        if is_true {
+            continue;
+        }
+        x = 3;
+    }
+    for _ in 0..10 {
+        match is_true {
+            false => {
+                x = 1;
+            }
+            _ => {
+                let _ = x;
+            }
+        }
+        x = 3;
+    }
+    for _ in 0..10 {
+        match is_true {
+            false => {
+                x = 1;
+            }
+            _ => {
+                break;
+            }
+        }
+        x = 3;
+    }
+    let _ = x;
+}
diff --git a/src/test/run-make-fulldeps/coverage/issue-83601.rs b/src/test/run-make-fulldeps/coverage/issue-83601.rs
new file mode 100644 (file)
index 0000000..0b72a81
--- /dev/null
@@ -0,0 +1,14 @@
+// Shows that rust-lang/rust/83601 is resolved
+
+#[derive(Debug, PartialEq, Eq)]
+struct Foo(u32);
+
+fn main() {
+    let bar = Foo(1);
+    assert_eq!(bar, Foo(1));
+    let baz = Foo(0);
+    assert_ne!(baz, Foo(1));
+    println!("{:?}", Foo(1));
+    println!("{:?}", bar);
+    println!("{:?}", baz);
+}
diff --git a/src/test/run-make-fulldeps/coverage/no_cov_crate.rs b/src/test/run-make-fulldeps/coverage/no_cov_crate.rs
new file mode 100644 (file)
index 0000000..300570d
--- /dev/null
@@ -0,0 +1,17 @@
+// Enables `no_coverage` on the entire crate
+#![feature(no_coverage)]
+
+#[no_coverage]
+fn do_not_add_coverage_1() {
+    println!("called but not covered");
+}
+
+#[no_coverage]
+fn do_not_add_coverage_2() {
+    println!("called but not covered");
+}
+
+fn main() {
+    do_not_add_coverage_1();
+    do_not_add_coverage_2();
+}
diff --git a/src/test/run-make-fulldeps/coverage/no_cov_func.rs b/src/test/run-make-fulldeps/coverage/no_cov_func.rs
new file mode 100644 (file)
index 0000000..e19a2c4
--- /dev/null
@@ -0,0 +1,18 @@
+// Enables `no_coverage` on individual functions
+
+#[feature(no_coverage)]
+#[no_coverage]
+fn do_not_add_coverage_1() {
+    println!("called but not covered");
+}
+
+#[no_coverage]
+#[feature(no_coverage)]
+fn do_not_add_coverage_2() {
+    println!("called but not covered");
+}
+
+fn main() {
+    do_not_add_coverage_1();
+    do_not_add_coverage_2();
+}
index 13c455172dd11554563ecc4ec755c6e339b2000c..cd0acf7230222022229660ebdaccb8357afe9ca8 100644 (file)
@@ -9,7 +9,7 @@ fn call(return_error: bool) -> Result<(),()> {
     }
 }
 
-fn main() -> Result<(),()> {
+fn test1() -> Result<(),()> {
     let mut
         countdown = 10
     ;
@@ -34,3 +34,85 @@ fn main() -> Result<(),()> {
     }
     Ok(())
 }
+
+struct Thing1;
+impl Thing1 {
+    fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> {
+        if return_error {
+            Err(())
+        } else {
+            Ok(Thing2{})
+        }
+    }
+}
+
+struct Thing2;
+impl Thing2 {
+    fn call(&self, return_error: bool) -> Result<u32,()> {
+        if return_error {
+            Err(())
+        } else {
+            Ok(57)
+        }
+    }
+}
+
+fn test2() -> Result<(),()> {
+    let thing1 = Thing1{};
+    let mut
+        countdown = 10
+    ;
+    for
+        _
+    in
+        0..10
+    {
+        countdown
+            -= 1
+        ;
+        if
+            countdown < 5
+        {
+            thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail");
+            thing1
+                .
+                get_thing_2(/*return_error=*/ false)
+                ?
+                .
+                call(/*return_error=*/ true)
+                .
+                expect_err(
+                    "call should fail"
+                );
+            let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?;
+            assert_eq!(val, 57);
+            let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?;
+            assert_eq!(val, 57);
+        }
+        else
+        {
+            let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?;
+            assert_eq!(val, 57);
+            let val = thing1
+                .get_thing_2(/*return_error=*/ false)?
+                .call(/*return_error=*/ false)?;
+            assert_eq!(val, 57);
+            let val = thing1
+                .get_thing_2(/*return_error=*/ false)
+                ?
+                .call(/*return_error=*/ false)
+                ?
+                ;
+            assert_eq!(val, 57);
+        }
+    }
+    Ok(())
+}
+
+fn main() -> Result<(),()> {
+    test1().expect_err("test1 should fail");
+    test2()
+    ?
+    ;
+    Ok(())
+}
diff --git a/src/test/rustdoc-gui/hash-item-expansion.goml b/src/test/rustdoc-gui/hash-item-expansion.goml
new file mode 100644 (file)
index 0000000..d736d15
--- /dev/null
@@ -0,0 +1,18 @@
+// This test ensures that the element corresponding to the hash is displayed.
+goto: file://|DOC_PATH|/struct.Foo.html#method.borrow
+// In the blanket implementations list, "Borrow" is the second one, hence the ":nth(2)".
+assert: ("#blanket-implementations-list > details:nth-child(2)", "open", "")
+// Please note the "\" below is needed because otherwise ".borrow" would be interpreted as
+// a class selector.
+assert: ("#method\.borrow", {"display": "flex"})
+// We first check that the impl block is open by default.
+assert: ("#implementations + details", "open", "")
+// We collapse it.
+click: "#implementations + details > summary"
+// We check that it was collapsed as expected.
+assert-false: ("#implementations + details", "open", "")
+// To ensure that we will click on the currently hidden method.
+assert: (".sidebar-links > a", "must_use")
+click: ".sidebar-links > a"
+// We check that the impl block was opened as expected so that we can see the method.
+assert: ("#implementations + details", "open", "")
diff --git a/src/test/rustdoc-gui/impl-default-expansion.goml b/src/test/rustdoc-gui/impl-default-expansion.goml
new file mode 100644 (file)
index 0000000..686d98f
--- /dev/null
@@ -0,0 +1,3 @@
+// This test ensures that the impl blocks are open by default.
+goto: file://|DOC_PATH|/struct.Foo.html
+assert: ("#main > details.implementors-toggle", "open", "")
index c1e161e123588cdbe22622be63152f55d5297ef8..eeba3e3f9070df5b75ea8ff101f5fd106c64c4be 100644 (file)
@@ -29,7 +29,9 @@ pub fn foo() {}
 
 impl Foo {
     #[must_use]
-    pub fn must_use(&self) -> bool { true }
+    pub fn must_use(&self) -> bool {
+        true
+    }
 }
 
 /// Just a normal enum.
@@ -85,3 +87,7 @@ pub trait AnotherOne {
 /// let x = 12;
 /// ```
 pub fn check_list_code_block() {}
+
+pub enum AnEnum {
+    WithVariants { and: usize, sub: usize, variants: usize },
+}
diff --git a/src/test/rustdoc-gui/nojs-attr-pos.goml b/src/test/rustdoc-gui/nojs-attr-pos.goml
deleted file mode 100644 (file)
index 35daa4c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-// Check that the attributes are well positioned when javascript is disabled (since
-// there is no toggle to display)
-javascript: false
-goto: file://|DOC_PATH|/struct.Foo.html
-assert: (".attributes", {"margin-left": "0px"})
index 42a8832185ae68c2fd3d3b155ae26725c6c54058..d63e1ee60b3c5754db791ac38ed27345a59ee1bc 100644 (file)
@@ -2,7 +2,9 @@ error: `prim@usize#x` contains an anchor, but links to builtin types are already
   --> $DIR/anchors.rs:47:6
    |
 LL | /// [prim@usize#x]
-   |      ^^^^^^^^^^^^ contains invalid anchor
+   |      ^^^^^^^^^^--
+   |                |
+   |                invalid anchor
    |
 note: the lint level is defined here
   --> $DIR/anchors.rs:1:9
@@ -16,25 +18,33 @@ error: `Foo::f#hola` contains an anchor, but links to fields are already anchore
   --> $DIR/anchors.rs:25:15
    |
 LL | /// Or maybe [Foo::f#hola].
-   |               ^^^^^^^^^^^ contains invalid anchor
+   |               ^^^^^^-----
+   |                     |
+   |                     invalid anchor
 
 error: `hello#people#!` contains multiple anchors
   --> $DIR/anchors.rs:31:28
    |
 LL | /// Another anchor error: [hello#people#!].
-   |                            ^^^^^^^^^^^^^^ contains invalid anchor
+   |                            ^^^^^^^^^^^^--
+   |                                        |
+   |                                        invalid anchor
 
 error: `Enum::A#whatever` contains an anchor, but links to variants are already anchored
   --> $DIR/anchors.rs:37:28
    |
 LL | /// Damn enum's variants: [Enum::A#whatever].
-   |                            ^^^^^^^^^^^^^^^^ contains invalid anchor
+   |                            ^^^^^^^---------
+   |                                   |
+   |                                   invalid anchor
 
 error: `u32#hello` contains an anchor, but links to builtin types are already anchored
   --> $DIR/anchors.rs:43:6
    |
 LL | /// [u32#hello]
-   |      ^^^^^^^^^ contains invalid anchor
+   |      ^^^------
+   |         |
+   |         invalid anchor
    |
    = note: this restriction may be lifted in a future release
    = note: see https://github.com/rust-lang/rust/issues/83083 for more information
index c0241b98b78c1f24904361ecc1020871a2d9bebd..6addb010e078f21a9d8825767c11a2d6a84828ff 100644 (file)
@@ -2,7 +2,9 @@ warning: `with#anchor#error` contains multiple anchors
   --> $DIR/double-anchor.rs:5:18
    |
 LL | /// docs [label][with#anchor#error]
-   |                  ^^^^^^^^^^^^^^^^^ contains invalid anchor
+   |                  ^^^^^^^^^^^------
+   |                             |
+   |                             invalid anchor
    |
    = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
 
index 82fa7cf9e6057948a03efcc02b4afe301f5dee23..8fda171002ba045f1166a80b5a652df20473ff27 100644 (file)
@@ -6,14 +6,14 @@ pub trait Index<I: ?Sized> {
     type Output: ?Sized;
     // @has - '//*[@id="tymethod.index"]//code' \
     //      "fn index<'a>(&'a self, index: I) -> &'a Self::Output"
-    // @has - '//*[@id="tymethod.index"]//code//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' \
+    // @has - '//*[@id="tymethod.index"]//code//a[@href="trait.Index.html#associatedtype.Output"]' \
     //      "Output"
     fn index<'a>(&'a self, index: I) -> &'a Self::Output;
 }
 
 // @has assoc_types/fn.use_output.html
 // @has - '//*[@class="rust fn"]' '-> &T::Output'
-// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' 'Output'
+// @has - '//*[@class="rust fn"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output'
 pub fn use_output<T: Index<usize>>(obj: &T, index: usize) -> &T::Output {
     obj.index(index)
 }
@@ -24,12 +24,12 @@ pub trait Feed {
 
 // @has assoc_types/fn.use_input.html
 // @has - '//*[@class="rust fn"]' 'T::Input'
-// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input'
+// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
 pub fn use_input<T: Feed>(_feed: &T, _element: T::Input) { }
 
 // @has assoc_types/fn.cmp_input.html
 // @has - '//*[@class="rust fn"]' 'where T::Input: PartialEq<U::Input>'
-// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input'
+// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
 pub fn cmp_input<T: Feed, U: Feed>(a: &T::Input, b: &U::Input) -> bool
     where T::Input: PartialEq<U::Input>
 {
index 54c5939f908d422eabe291add61f9ed2251d6774..51cd4a6cbfd12d6a7f9b7c21e19e3e394ab1145e 100644 (file)
@@ -1,21 +1,21 @@
 #![crate_name = "foo"]
 
-// @has foo/fn.f.html '//*[@class="docblock attributes"]' '#[no_mangle]'
+// @has foo/fn.f.html '//*[@class="rust fn"]' '#[no_mangle]'
 #[no_mangle]
 pub extern "C" fn f() {}
 
-// @has foo/fn.g.html '//*[@class="docblock attributes"]' '#[export_name = "bar"]'
+// @has foo/fn.g.html '//*[@class="rust fn"]' '#[export_name = "bar"]'
 #[export_name = "bar"]
 pub extern "C" fn g() {}
 
-// @matches foo/enum.Foo.html '//*[@class="docblock attributes top-attr"]' \
-//      '(?m)\A#\[repr\(i64\)\]\n#\[must_use\]\Z'
+// @matches foo/enum.Foo.html '//*[@class="rust enum"]' \
+//      '#\[repr\(i64\)\]\n#\[must_use\]'
 #[repr(i64)]
 #[must_use]
 pub enum Foo {
     Bar,
 }
 
-// @has foo/struct.Repr.html '//*[@class="docblock attributes top-attr"]' '#[repr(C, align(8))]'
+// @has foo/struct.Repr.html '//*[@class="docblock type-decl"]' '#[repr(C, align(8))]'
 #[repr(C, align(8))]
 pub struct Repr;
diff --git a/src/test/rustdoc/auxiliary/primitive-doc.rs b/src/test/rustdoc/auxiliary/primitive-doc.rs
new file mode 100644 (file)
index 0000000..a5b6974
--- /dev/null
@@ -0,0 +1,6 @@
+// compile-flags: --crate-type lib --edition 2018
+
+#[doc(primitive = "usize")]
+/// This is the built-in type `usize`.
+mod usize {
+}
index b479820dfb2eceda08cbe64fcdcc65a127e630a9..ed4a5ea21374b7b19e1c1b22e149f9e4e4380aeb 100644 (file)
@@ -2,7 +2,7 @@
 
 pub struct Foo;
 
-// @has foo/struct.Bar.html '//a[@href="../foo/struct.Foo.html"]' 'Foo'
+// @has foo/struct.Bar.html '//a[@href="struct.Foo.html"]' 'Foo'
 
 /// Code-styled reference to [`Foo`].
 pub struct Bar;
index db4be82e6bfd9f7090e6f457d5669843fb517446..77432ba1539555518449c178ca4a36d911073a31 100644 (file)
@@ -8,7 +8,7 @@ pub struct Simd<T, const WIDTH: usize> {
     inner: T,
 }
 
-// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]/h3/code' 'impl Add<Simd<u8, 16_usize>> for Simd<u8, 16>'
+// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]//h3/code' 'impl Add<Simd<u8, 16_usize>> for Simd<u8, 16>'
 impl Add for Simd<u8, 16> {
     type Output = Self;
 
diff --git a/src/test/rustdoc/cross-crate-primitive-doc.rs b/src/test/rustdoc/cross-crate-primitive-doc.rs
new file mode 100644 (file)
index 0000000..05376e4
--- /dev/null
@@ -0,0 +1,9 @@
+// aux-build:primitive-doc.rs
+// compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options
+
+#![no_std]
+
+extern crate primitive_doc;
+
+// @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'usize'
+pub fn foo() -> usize { 0 }
index e48a56f906c955a0afea9c8342043047d44bcb41..ede3f455a2044c4e1d27f671f87d32497708c43c 100644 (file)
@@ -1,3 +1,5 @@
+// compile-flags: --document-private-items
+
 #![feature(decl_macro)]
 
 // @has decl_macro/macro.my_macro.html //pre 'pub macro my_macro() {'
 pub macro by_example_single {
     ($foo:expr) => {}
 }
+
+mod a {
+    mod b {
+        // @has decl_macro/a/b/macro.by_example_vis.html //pre 'pub(super) macro by_example_vis($foo:expr) {'
+        pub(in super) macro by_example_vis {
+            ($foo:expr) => {}
+        }
+        mod c {
+            // @has decl_macro/a/b/c/macro.by_example_vis_named.html //pre 'pub(in a) macro by_example_vis_named($foo:expr) {'
+            pub(in a) macro by_example_vis_named {
+                ($foo:expr) => {}
+            }
+        }
+    }
+}
index e4f0bdab16292b51a9d6b4940ab66f3d6ea10e25..7bcd2a3c149e9a7a81d0bbc947311fc4827f39ae 100644 (file)
@@ -1,7 +1,7 @@
 #![crate_name = "foo"]
 
-// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#tymethod.req"]' 'req'
-// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#method.prov"]' 'prov'
+// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#tymethod.req"]' 'req'
+// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#method.prov"]' 'prov'
 
 /// Always make sure to implement [`req`], but you don't have to implement [`prov`].
 ///
index 112d632971a5f1e57a96f3e33404aae2437d0b23..1e644bb9739877c203b19acbf7b83e6d5dd80d6a 100644 (file)
@@ -1,8 +1,8 @@
 // @has issue_33054/impls/struct.Foo.html
 // @has - '//code' 'impl Foo'
 // @has - '//code' 'impl Bar for Foo'
-// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
-// @count - '//*[@id="main"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
+// @count - '//*[@id="main"]/details/summary/*[@class="impl"]' 1
 // @has issue_33054/impls/bar/trait.Bar.html
 // @has - '//code' 'impl Bar for Foo'
 // @count - '//*[@class="struct"]' 1
diff --git a/src/test/rustdoc/empty-impls.rs b/src/test/rustdoc/empty-impls.rs
new file mode 100644 (file)
index 0000000..86dec32
--- /dev/null
@@ -0,0 +1,19 @@
+#![crate_name = "foo"]
+
+// @has foo/struct.Foo.html
+// @has - '//div[@id="synthetic-implementations-list"]/h3[@id="impl-Send"]' 'impl Send for Foo'
+pub struct Foo;
+
+pub trait EmptyTrait {}
+
+// @has - '//div[@id="trait-implementations-list"]/h3[@id="impl-EmptyTrait"]' 'impl EmptyTrait for Foo'
+impl EmptyTrait for Foo {}
+
+pub trait NotEmpty {
+    fn foo(&self);
+}
+
+// @has - '//div[@id="trait-implementations-list"]/details/summary/h3[@id="impl-NotEmpty"]' 'impl NotEmpty for Foo'
+impl NotEmpty for Foo {
+    fn foo(&self) {}
+}
index 4db63b12b6bb75f16306674fd2c65b19d1990ed0..8c36a7fa002d856badd5b14c9954854dc78b8ee9 100644 (file)
@@ -3,7 +3,7 @@
 
 extern crate cross_crate_self;
 
-// @has self/struct.S.html '//a[@href="../self/struct.S.html#method.f"]' "Self::f"
-// @has self/struct.S.html '//a[@href="../self/struct.S.html"]' "Self"
+// @has self/struct.S.html '//a[@href="struct.S.html#method.f"]' "Self::f"
+// @has self/struct.S.html '//a[@href="struct.S.html"]' "Self"
 // @has self/struct.S.html '//a[@href="../cross_crate_self/index.html"]' "crate"
 pub use cross_crate_self::S;
index e4f0c737bdd3312be648ff318704194405ee7574..8ec1a7b4f9056381784dda314f1537252061b2bd 100644 (file)
@@ -4,7 +4,7 @@
 pub struct Something;
 
 // @has anchors/struct.SomeOtherType.html
-// @has - '//a/@href' '../anchors/struct.Something.html#Anchor!'
+// @has - '//a/@href' 'struct.Something.html#Anchor!'
 
 /// I want...
 ///
index 28dc7073a3eccc26be1e0f764b39883c811a76f7..68647127fe880317b22fedae82dc95035c8d0c3a 100644 (file)
@@ -9,14 +9,14 @@ fn f() -> Self::T {
 }
 
 /// Link to [UsesDefaults::T] and [UsesDefaults::f]
-// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T'
-// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f'
+// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T'
+// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f'
 pub struct UsesDefaults;
 impl TraitWithDefault for UsesDefaults {}
 
 /// Link to [OverridesDefaults::T] and [OverridesDefaults::f]
-// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T'
-// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f'
+// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T'
+// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f'
 pub struct OverridesDefaults;
 impl TraitWithDefault for OverridesDefaults {
     type T = bool;
index 43a43a79738b3cbbfbe0fcd230d4cd8cf8fb0a65..2757418bc64e5c4990885be015c95b3813c8771a 100644 (file)
@@ -9,10 +9,10 @@
 pub fn foo() {}
 
 /// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input]
-// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html"]' 'MyStruct'
-// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from struct'
-// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.clone"]' 'MyStruct::clone'
-// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input'
+// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html"]' 'MyStruct'
+// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from struct'
+// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.clone"]' 'MyStruct::clone'
+// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input'
 pub struct MyStruct { foo: () }
 
 impl Clone for MyStruct {
@@ -30,7 +30,7 @@ impl T for MyStruct {
     type Input = usize;
 
     /// [link from method][MyStruct::method] on method
-    // @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from method'
+    // @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from method'
     fn method(i: usize) {
     }
 }
index 7760546e1fa1cf288efff0c6676ff4ba49eaca0c..39f5c298bc4a1ac30164bb4c904c3aad9a037c4f 100644 (file)
@@ -1,21 +1,21 @@
 // @has basic/index.html
-// @has - '//a/@href' '../basic/struct.ThisType.html'
-// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method'
-// @has - '//a/@href' '../basic/enum.ThisEnum.html'
-// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html#tymethod.this_associated_method'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedtype.ThisAssociatedType'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST'
-// @has - '//a/@href' '../basic/trait.ThisTrait.html'
-// @has - '//a/@href' '../basic/type.ThisAlias.html'
-// @has - '//a/@href' '../basic/union.ThisUnion.html'
-// @has - '//a/@href' '../basic/fn.this_function.html'
-// @has - '//a/@href' '../basic/constant.THIS_CONST.html'
-// @has - '//a/@href' '../basic/static.THIS_STATIC.html'
-// @has - '//a/@href' '../basic/macro.this_macro.html'
-// @has - '//a/@href' '../basic/trait.SoAmbiguous.html'
-// @has - '//a/@href' '../basic/fn.SoAmbiguous.html'
+// @has - '//a/@href' 'struct.ThisType.html'
+// @has - '//a/@href' 'struct.ThisType.html#method.this_method'
+// @has - '//a/@href' 'enum.ThisEnum.html'
+// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant'
+// @has - '//a/@href' 'trait.ThisTrait.html'
+// @has - '//a/@href' 'trait.ThisTrait.html#tymethod.this_associated_method'
+// @has - '//a/@href' 'trait.ThisTrait.html#associatedtype.ThisAssociatedType'
+// @has - '//a/@href' 'trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST'
+// @has - '//a/@href' 'trait.ThisTrait.html'
+// @has - '//a/@href' 'type.ThisAlias.html'
+// @has - '//a/@href' 'union.ThisUnion.html'
+// @has - '//a/@href' 'fn.this_function.html'
+// @has - '//a/@href' 'constant.THIS_CONST.html'
+// @has - '//a/@href' 'static.THIS_STATIC.html'
+// @has - '//a/@href' 'macro.this_macro.html'
+// @has - '//a/@href' 'trait.SoAmbiguous.html'
+// @has - '//a/@href' 'fn.SoAmbiguous.html'
 //! In this crate we would like to link to:
 //!
 //! * [`ThisType`](ThisType)
@@ -46,7 +46,7 @@ macro_rules! this_macro {
     () => {};
 }
 
-// @has basic/struct.ThisType.html '//a/@href' '../basic/macro.this_macro.html'
+// @has basic/struct.ThisType.html '//a/@href' 'macro.this_macro.html'
 /// another link to [`this_macro!()`]
 pub struct ThisType;
 
@@ -72,10 +72,10 @@ pub trait SoAmbiguous {}
 pub fn SoAmbiguous() {}
 
 
-// @has basic/struct.SomeOtherType.html '//a/@href' '../basic/struct.ThisType.html'
-// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method'
-// @has - '//a/@href' '../basic/enum.ThisEnum.html'
-// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant'
+// @has basic/struct.SomeOtherType.html '//a/@href' 'struct.ThisType.html'
+// @has - '//a/@href' 'struct.ThisType.html#method.this_method'
+// @has - '//a/@href' 'enum.ThisEnum.html'
+// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant'
 /// Shortcut links for:
 /// * [`ThisType`]
 /// * [`ThisType::this_method`]
index 837390b3c716102a0005a5a0a4eca8169f7fd1db..85c5866ca7ecfbc1b8ad0d56d01f3cc1d84e538e 100644 (file)
@@ -4,7 +4,7 @@
 
 extern crate my_rand;
 
-// @has 'additional_doc/trait.Rng.html' '//a[@href="../additional_doc/trait.Rng.html"]' 'Rng'
+// @has 'additional_doc/trait.Rng.html' '//a[@href="trait.Rng.html"]' 'Rng'
 // @has 'additional_doc/trait.Rng.html' '//a[@href="../my_rand/trait.RngCore.html"]' 'RngCore'
 /// This is an [`Rng`].
 pub use my_rand::Rng;
index 9c9d4c649455ebe3c82c1088b9d0539d3985550d..31337f20f18dc91e1336f26378bbcf2a9c59ae07 100644 (file)
@@ -6,5 +6,5 @@
 
 extern crate hidden_dep;
 
-// @has 'hidden/struct.Ready.html' '//a/@href' '../hidden/fn.ready.html'
+// @has 'hidden/struct.Ready.html' '//a/@href' 'fn.ready.html'
 pub use hidden_dep::future::{ready, Ready};
index 45f561328f279040293c625f41f678f86b1be403..db7952b5aced0699f2408995dda1d8db8f1cd48d 100644 (file)
@@ -11,6 +11,6 @@ pub mod bar {
 
 // NOTE: we re-exported both `Foo` and `Bar` here,
 // NOTE: so they are inlined and therefore we link to the current module.
-// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/bar/trait.Bar.html"]' 'Bar'
-// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/trait.Baz.html"]' 'Baz'
+// @has 'submodule_outer/trait.Foo.html' '//a[@href="bar/trait.Bar.html"]' 'Bar'
+// @has 'submodule_outer/trait.Foo.html' '//a[@href="trait.Baz.html"]' 'Baz'
 pub use ::bar_::{Foo, Baz};
index 12c3cee29c3acd93c921438bc50134f6778e91cd..d782c5cf5dc84ade7959ce68d05c429dbeb81127 100644 (file)
@@ -2,26 +2,26 @@
 // first try backticks
 /// Trait: [`trait@Name`], fn: [`fn@Name`], [`Name`][`macro@Name`]
 // @has disambiguators_removed/struct.AtDisambiguator.html
-// @has - '//a[@href="../disambiguators_removed/trait.Name.html"][code]' "Name"
-// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name"
-// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name"
+// @has - '//a[@href="trait.Name.html"][code]' "Name"
+// @has - '//a[@href="fn.Name.html"][code]' "Name"
+// @has - '//a[@href="macro.Name.html"][code]' "Name"
 pub struct AtDisambiguator;
 
 /// fn: [`Name()`], macro: [`Name!`]
 // @has disambiguators_removed/struct.SymbolDisambiguator.html
-// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name()"
-// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name!"
+// @has - '//a[@href="fn.Name.html"][code]' "Name()"
+// @has - '//a[@href="macro.Name.html"][code]' "Name!"
 pub struct SymbolDisambiguator;
 
 // Now make sure that backticks aren't added if they weren't already there
 /// [fn@Name]
 // @has disambiguators_removed/trait.Name.html
-// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "Name"
-// @!has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name"
+// @has - '//a[@href="fn.Name.html"]' "Name"
+// @!has - '//a[@href="fn.Name.html"][code]' "Name"
 
 // FIXME: this will turn !() into ! alone
 /// [Name!()]
-// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name!"
+// @has - '//a[@href="macro.Name.html"]' "Name!"
 pub trait Name {}
 
 #[allow(non_snake_case)]
@@ -29,22 +29,22 @@ pub trait Name {}
 // Try collapsed reference links
 /// [macro@Name][]
 // @has disambiguators_removed/fn.Name.html
-// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name"
+// @has - '//a[@href="macro.Name.html"]' "Name"
 
 // Try links that have the same text as a generated URL
-/// Weird URL aligned [../disambiguators_removed/macro.Name.html][trait@Name]
-// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "../disambiguators_removed/macro.Name.html"
+/// Weird URL aligned [macro.Name.html][trait@Name]
+// @has - '//a[@href="trait.Name.html"]' "macro.Name.html"
 pub fn Name() {}
 
 #[macro_export]
 // Rustdoc doesn't currently handle links that have weird interspersing of inline code blocks.
 /// [fn@Na`m`e]
 // @has disambiguators_removed/macro.Name.html
-// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "fn@Name"
+// @has - '//a[@href="fn.Name.html"]' "fn@Name"
 
 // It also doesn't handle any case where the code block isn't the whole link text:
 /// [trait@`Name`]
-// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "trait@Name"
+// @has - '//a[@href="trait.Name.html"]' "trait@Name"
 macro_rules! Name {
     () => ()
 }
index 70bf343a9a5e5a144950de51f51e3767bce088ff..2270a1fafa1cb8dd75d3a971a89aa5d45ba39849 100644 (file)
@@ -11,4 +11,4 @@ pub enum Foo {
 /// I want [Foo::X::y].
 pub fn foo() {}
 
-// @has foo/fn.foo.html '//a/@href' '../foo/enum.Foo.html#variant.X.field.y'
+// @has foo/fn.foo.html '//a/@href' 'enum.Foo.html#variant.X.field.y'
index e1934698d1fadce81e0f04bed9b727333e6c0f5c..f37ae62dde1aa45076563fe149bdbb9fc323dc06 100644 (file)
@@ -12,6 +12,6 @@ pub fn f(&self) {
 
 // @has 'extern_type/foreigntype.ExternType.html'
 // @has 'extern_type/fn.links_to_extern_type.html' \
-// 'href="../extern_type/foreigntype.ExternType.html#method.f"'
+// 'href="foreigntype.ExternType.html#method.f"'
 /// See also [ExternType::f]
 pub fn links_to_extern_type() {}
index 76618cdce4cd29355a7dc38300f0ffbab43c3db4..68a5672a8d2091e6ca3c3a7c53fbef0cf59a1e1e 100644 (file)
@@ -8,4 +8,4 @@ pub enum Foo {
     },
 }
 
-// @has foo/enum.Foo.html '//a/@href' '../foo/enum.Foo.html#variant.Bar.field.abc'
+// @has foo/enum.Foo.html '//a/@href' 'enum.Foo.html#variant.Bar.field.abc'
index feb013b22be65a7569333a883aafc00350793bc1..24b9dc30a9e496cd50123c6522e829f8540f085a 100644 (file)
@@ -6,11 +6,11 @@ pub fn foo() {
 }
 
 pub mod foo {}
-// @has mod_ambiguity/struct.A.html '//a/@href' '../mod_ambiguity/foo/index.html'
+// @has mod_ambiguity/struct.A.html '//a/@href' 'foo/index.html'
 /// Module is [`module@foo`]
 pub struct A;
 
 
-// @has mod_ambiguity/struct.B.html '//a/@href' '../mod_ambiguity/fn.foo.html'
+// @has mod_ambiguity/struct.B.html '//a/@href' 'fn.foo.html'
 /// Function is [`fn@foo`]
 pub struct B;
index ab6e3da17f4ed4db0b96801bdbc4c232a24846d1..478b40b0b516fb9c3b19bfbbb78312019f16eed1 100644 (file)
@@ -11,6 +11,6 @@ pub mod char {
 pub struct MyString;
 
 /// See also [crate::char] and [mod@char]
-// @has prim_precedence/struct.MyString2.html '//*[@href="../prim_precedence/char/index.html"]' 'crate::char'
-// @has - '//*[@href="../prim_precedence/char/index.html"]' 'mod@char'
+// @has prim_precedence/struct.MyString2.html '//*[@href="char/index.html"]' 'crate::char'
+// @has - '//*[@href="char/index.html"]' 'mod@char'
 pub struct MyString2;
index 337102d6ab3fa966d7a32fd362fda42d156234e1..2756a7998e8ea42cdb580578b8b0cc17a6e26b4e 100644 (file)
@@ -4,9 +4,9 @@
 // make sure to update `rustdoc-ui/intra-doc/private.rs` if you update this file
 
 /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
-// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html"]' 'DontDocMe'
-// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#method.f"]' 'DontDocMe::f'
-// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x'
+// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html"]' 'DontDocMe'
+// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#method.f"]' 'DontDocMe::f'
+// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x'
 pub struct DocMe;
 struct DontDocMe {
     x: usize,
index ab4626ccfc3abfd4e46d87427a03f358d7035481..fce10a130be655a70684bf0809caae5fdc20b94b 100644 (file)
@@ -9,17 +9,17 @@
 use proc_macro_macro::{DeriveB, attr_b};
 
 // @has proc_macro/struct.Foo.html
-// @has - '//a/@href' '../proc_macro/derive.DeriveA.html'
-// @has - '//a/@href' '../proc_macro/attr.attr_a.html'
-// @has - '//a/@href' '../proc_macro/trait.DeriveTrait.html'
+// @has - '//a/@href' 'derive.DeriveA.html'
+// @has - '//a/@href' 'attr.attr_a.html'
+// @has - '//a/@href' 'trait.DeriveTrait.html'
 // @has - '//a/@href' '../proc_macro_macro/derive.DeriveB.html'
 // @has - '//a/@href' '../proc_macro_macro/attr.attr_b.html'
 /// Link to [DeriveA], [attr_a], [DeriveB], [attr_b], [DeriveTrait]
 pub struct Foo;
 
 // @has proc_macro/struct.Bar.html
-// @has - '//a/@href' '../proc_macro/derive.DeriveA.html'
-// @has - '//a/@href' '../proc_macro/attr.attr_a.html'
+// @has - '//a/@href' 'derive.DeriveA.html'
+// @has - '//a/@href' 'attr.attr_a.html'
 /// Link to [deriveA](derive@DeriveA) [attr](macro@attr_a)
 pub struct Bar;
 
index dd52249abc6d0ad27cab524bd0c82325ba7fa7e5..579fa68cee8be37d1ff4e5b2a5516e77e4f0a682 100644 (file)
@@ -13,7 +13,7 @@
 
 // @has outer/index.html
 // @ has - '//a[@href="https://doc.rust-lang.org/nightly/std/env/fn.var.html"]' "std::env"
-// @ has - '//a[@href="../outer/fn.f.html"]' "g"
+// @ has - '//a[@href="fn.f.html"]' "g"
 pub use f as g;
 
 // FIXME: same as above
index d289797f1465201ee71bcb16b13eec01c2771c92..177c3016fb1515728f54073540a3a0bda841d4e8 100644 (file)
@@ -5,7 +5,7 @@ pub mod r#impl {
     impl S {
         /// See [Self::b].
         // @has raw_ident_self/impl/struct.S.html
-        // @has - '//a[@href="../../raw_ident_self/impl/struct.S.html#method.b"]' 'Self::b'
+        // @has - '//a[@href="struct.S.html#method.b"]' 'Self::b'
         pub fn a() {}
 
         pub fn b() {}
index 96f3580f3055e8e2405fbfb5627c4c59ecb42f65..64683bacd6513838fdc3a001f1fb3e61aebb08bd 100644 (file)
@@ -3,13 +3,13 @@
 #![crate_name = "foo"]
 extern crate inner;
 
-// @has foo/struct.Inner.html '//a[@href="../foo/fn.with_code.html"]' 'crate::with_code'
+// @has foo/struct.Inner.html '//a[@href="fn.with_code.html"]' 'crate::with_code'
 /// [crate::with_code]
-// @has - '//a[@href="../foo/fn.with_code.html"]' 'different text'
+// @has - '//a[@href="fn.with_code.html"]' 'different text'
 /// [different text][with_code]
-// @has - '//a[@href="../foo/fn.me_too.html"]' 'me_too'
+// @has - '//a[@href="fn.me_too.html"]' 'me_too'
 #[doc = "[me_too]"]
-// @has - '//a[@href="../foo/fn.me_three.html"]' 'reference link'
+// @has - '//a[@href="fn.me_three.html"]' 'reference link'
 /// This [reference link]
 #[doc = "has an attr in the way"]
 ///
index b2b75127b3122b3161b46ca3c48ee046580c47bc..0ba7df8a78ad75a57016a09e88f1c676a9f615c9 100644 (file)
@@ -1,8 +1,8 @@
 #![crate_name = "foo"]
 
 
-// @has foo/index.html '//a/@href' '../foo/struct.Foo.html#method.new'
-// @has foo/struct.Foo.html '//a/@href' '../foo/struct.Foo.html#method.new'
+// @has foo/index.html '//a/@href' 'struct.Foo.html#method.new'
+// @has foo/struct.Foo.html '//a/@href' 'struct.Foo.html#method.new'
 
 /// Use [`new`] to create a new instance.
 ///
@@ -15,8 +15,8 @@ pub fn new() -> Self {
     }
 }
 
-// @has foo/index.html '//a/@href' '../foo/struct.Bar.html#method.new2'
-// @has foo/struct.Bar.html '//a/@href' '../foo/struct.Bar.html#method.new2'
+// @has foo/index.html '//a/@href' 'struct.Bar.html#method.new2'
+// @has foo/struct.Bar.html '//a/@href' 'struct.Bar.html#method.new2'
 
 /// Use [`new2`] to create a new instance.
 ///
@@ -30,7 +30,7 @@ pub fn new2() -> Self {
 }
 
 pub struct MyStruct {
-    // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#structfield.struct_field'
+    // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#structfield.struct_field'
 
     /// [`struct_field`]
     ///
@@ -39,7 +39,7 @@ pub struct MyStruct {
 }
 
 pub enum MyEnum {
-    // @has foo/enum.MyEnum.html '//a/@href' '../foo/enum.MyEnum.html#variant.EnumVariant'
+    // @has foo/enum.MyEnum.html '//a/@href' 'enum.MyEnum.html#variant.EnumVariant'
 
     /// [`EnumVariant`]
     ///
@@ -48,7 +48,7 @@ pub enum MyEnum {
 }
 
 pub union MyUnion {
-    // @has foo/union.MyUnion.html '//a/@href' '../foo/union.MyUnion.html#structfield.union_field'
+    // @has foo/union.MyUnion.html '//a/@href' 'union.MyUnion.html#structfield.union_field'
 
     /// [`union_field`]
     ///
@@ -57,21 +57,21 @@ pub union MyUnion {
 }
 
 pub trait MyTrait {
-    // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedtype.AssoType'
+    // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedtype.AssoType'
 
     /// [`AssoType`]
     ///
     /// [`AssoType`]: Self::AssoType
     type AssoType;
 
-    // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedconstant.ASSO_CONST'
+    // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedconstant.ASSO_CONST'
 
     /// [`ASSO_CONST`]
     ///
     /// [`ASSO_CONST`]: Self::ASSO_CONST
     const ASSO_CONST: i32 = 1;
 
-    // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#method.asso_fn'
+    // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#method.asso_fn'
 
     /// [`asso_fn`]
     ///
@@ -80,7 +80,7 @@ fn asso_fn() {}
 }
 
 impl MyStruct {
-    // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.for_impl'
+    // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.for_impl'
 
     /// [`for_impl`]
     ///
@@ -91,21 +91,21 @@ pub fn for_impl() {
 }
 
 impl MyTrait for MyStruct {
-    // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType'
+    // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType'
 
     /// [`AssoType`]
     ///
     /// [`AssoType`]: Self::AssoType
     type AssoType = u32;
 
-    // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST'
+    // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST'
 
     /// [`ASSO_CONST`]
     ///
     /// [`ASSO_CONST`]: Self::ASSO_CONST
     const ASSO_CONST: i32 = 10;
 
-    // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.asso_fn'
+    // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.asso_fn'
 
     /// [`asso_fn`]
     ///
index ef1987a829ad20ed4ac57476c203d44d8b655205..cf60dc1dbd50ec11c6e9a5802f1e48c2885c318e 100644 (file)
@@ -5,21 +5,21 @@
 
 impl MyTrait for MyStruct {
 
-// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType'
+// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType'
 
     /// [`AssoType`]
     ///
     /// [`AssoType`]: MyStruct::AssoType
     type AssoType = u32;
 
-// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST'
+// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST'
 
     /// [`ASSO_CONST`]
     ///
     /// [`ASSO_CONST`]: MyStruct::ASSO_CONST
     const ASSO_CONST: i32 = 10;
 
-// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.trait_fn'
+// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.trait_fn'
 
     /// [`trait_fn`]
     ///
index affd2aaec2d2218de073a00af359bf30d248f8d9..7602aced56416ba9a002a87b3ac142238c28edaf 100644 (file)
@@ -2,7 +2,7 @@
 
 /// Link to [S::assoc_fn()]
 /// Link to [Default::default()]
-// @has trait_item/struct.S.html '//*[@href="../trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()'
+// @has trait_item/struct.S.html '//*[@href="struct.S.html#method.assoc_fn"]' 'S::assoc_fn()'
 // @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()'
 pub struct S;
 
index add1530a5a675b7c71a7430c67159691f34115a2..63bf7fa5768a718992c85318e13a0dc197481b75 100644 (file)
@@ -1,12 +1,12 @@
 #![crate_name = "foo"]
-// @has foo/enum.E1.html '//a/@href' '../foo/enum.E1.html#variant.A'
+// @has foo/enum.E1.html '//a/@href' 'enum.E1.html#variant.A'
 
 /// [Self::A::b]
 pub enum E1 {
     A { b: usize }
 }
 
-// @has foo/enum.E2.html '//a/@href' '../foo/enum.E2.html#variant.A'
+// @has foo/enum.E2.html '//a/@href' 'enum.E2.html#variant.A'
 
 /// [Self::A::b]
 pub enum E2 {
index 896fc1a78f13f94ff997826ae39f023e225b61b7..5de26abace6fab01d1b20f6570358578468f4c88 100644 (file)
@@ -7,5 +7,5 @@ impl super::Blah for super::What { }
 pub trait Blah { }
 
 // @count issue_21474/struct.What.html \
-//        '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
+//        '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
 pub struct What;
index 4cc40560254634bbac9ab30cfe5bc2fc7d994308..497276e6826a1acc1556ae1b22bfeddef74a47b9 100644 (file)
@@ -23,9 +23,9 @@ pub fn foo() {}
 }
 
 impl Bar for Foo {
-    // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedtype.Bar"]' 'Bar'
-    // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedconstant.Baz"]' 'Baz'
-    // @has - '//*[@href="../issue_28478/trait.Bar.html#tymethod.bar"]' 'bar'
+    // @has - '//*[@href="trait.Bar.html#associatedtype.Bar"]' 'Bar'
+    // @has - '//*[@href="trait.Bar.html#associatedconstant.Baz"]' 'Baz'
+    // @has - '//*[@href="trait.Bar.html#tymethod.bar"]' 'bar'
     fn bar() {}
-    // @has - '//*[@href="../issue_28478/trait.Bar.html#method.baz"]' 'baz'
+    // @has - '//*[@href="trait.Bar.html#method.baz"]' 'baz'
 }
index 19bab394dcf20dc9a991fe9463e979690a08c1c4..2b25da77d7e7b23df6d65ef8f3809602909dfde6 100644 (file)
@@ -5,7 +5,7 @@ pub trait MyTrait {
     fn my_string(&self) -> String;
 }
 
-// @has - "//div[@id='implementors-list']/h3[@id='impl-MyTrait']//code" "impl<T> MyTrait for T where T: Debug"
+// @has - "//div[@id='implementors-list']//h3[@id='impl-MyTrait']//code" "impl<T> MyTrait for T where T: Debug"
 impl<T> MyTrait for T where T: fmt::Debug {
     fn my_string(&self) -> String {
         format!("{:?}", self)
index 0225c0c5c2fa74e2f3d841d87fa61e235f5730af..8a5f0413826a910ff7f7ceaa72db9c2438a4ecd7 100644 (file)
@@ -4,12 +4,12 @@ pub trait Bar<T, U> {}
 
 // @has 'foo/struct.Foo1.html'
 pub struct Foo1;
-// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
 // @has - '//*[@class="impl"]' "impl Bar<Foo1, &'static Foo1> for Foo1"
 impl Bar<Foo1, &'static Foo1> for Foo1 {}
 
 // @has 'foo/struct.Foo2.html'
 pub struct Foo2;
-// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
 // @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8"
 impl Bar<&'static Foo2, Foo2> for u8 {}
index 74502be622a4f0659f8c7309a21f9703640394b2..0820512e521402e57912a0c79740e1592622b097 100644 (file)
@@ -13,8 +13,8 @@ impl<B, C> Signal2 for B where B: Signal<Item = C> {
 // @has issue_50159/struct.Switch.html
 // @has - '//code' 'impl<B> Send for Switch<B> where <B as Signal>::Item: Send'
 // @has - '//code' 'impl<B> Sync for Switch<B> where <B as Signal>::Item: Sync'
-// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 5
+// @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5
 pub struct Switch<B: Signal> {
     pub inner: <B as Signal2>::Item2,
 }
index d9accf9c5998b355c6f139af54aa738fb8e44557..d018c948162d9c076459be355be9f9da2fa0e3c3 100644 (file)
@@ -7,7 +7,7 @@ pub trait Owned<'a> {
 }
 
 // @has issue_51236/struct.Owned.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> Send for \
 // Owned<T> where <T as Owned<'static>>::Reader: Send"
 pub struct Owned<T> where T: for<'a> ::traits::Owned<'a> {
     marker: PhantomData<<T as ::traits::Owned<'static>>::Reader>,
index 3ebf154077f49c3a7b24b8bf9647f9dc3a28cd76..ddc14e68675a98cd3af319e08e56a05a3700bdb2 100644 (file)
@@ -12,9 +12,9 @@ macro_rules! array_impls {
     }
 }
 
-// @has issue_53812/trait.MyIterator.html '//*[@id="implementors-list"]//h3[1]' 'MyStruct<[T; 0]>'
-// @has - '//*[@id="implementors-list"]//h3[2]' 'MyStruct<[T; 1]>'
-// @has - '//*[@id="implementors-list"]//h3[3]' 'MyStruct<[T; 2]>'
-// @has - '//*[@id="implementors-list"]//h3[4]' 'MyStruct<[T; 3]>'
-// @has - '//*[@id="implementors-list"]//h3[5]' 'MyStruct<[T; 10]>'
+// @has issue_53812/trait.MyIterator.html '//*[@id="implementors-list"]/h3[1]' 'MyStruct<[T; 0]>'
+// @has - '//*[@id="implementors-list"]/h3[2]' 'MyStruct<[T; 1]>'
+// @has - '//*[@id="implementors-list"]/h3[3]' 'MyStruct<[T; 2]>'
+// @has - '//*[@id="implementors-list"]/h3[4]' 'MyStruct<[T; 3]>'
+// @has - '//*[@id="implementors-list"]/h3[5]' 'MyStruct<[T; 10]>'
 array_impls! { 10 3 2 1 0 }
index 263b1eb0bd65aad1cbcbcf0130804d482c187088..47da94a4ccf4c88ab39ea62e213afcb66976e14d 100644 (file)
@@ -3,10 +3,10 @@ pub trait ScopeHandle<'scope> {}
 
 
 // @has issue_54705/struct.ScopeFutureContents.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'scope, S> \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'scope, S> \
 // Send for ScopeFutureContents<'scope, S> where S: Sync"
 //
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'scope, S> \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'scope, S> \
 // Sync for ScopeFutureContents<'scope, S> where S: Sync"
 pub struct ScopeFutureContents<'scope, S>
     where S: ScopeHandle<'scope>,
index d312a5114595a466290dad98f9ce96f8707eea71..d1877f39ba761e1d4b98f787678cac20972d0ddf 100644 (file)
@@ -1,16 +1,16 @@
 #![feature(negative_impls)]
 
 // @has issue_55321/struct.A.html
-// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Send for A"
-// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Sync for A"
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' "impl !Send for A"
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' "impl !Sync for A"
 pub struct A();
 
 impl !Send for A {}
 impl !Sync for A {}
 
 // @has issue_55321/struct.B.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Send for \
 // B<T>"
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Sync for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Sync for \
 // B<T>"
 pub struct B<T: ?Sized>(A, Box<T>);
index 4aa553f7793396d769862da26af1f132492c3a9c..f156d225bd79b5ee9c3846522f6d9781fdd2c9bf 100644 (file)
@@ -2,19 +2,19 @@
 
 // @has issue_55364/subone/index.html
 // These foo/bar links in the module's documentation should refer inside `subone`
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
 pub mod subone {
     //! See either [foo] or [bar].
 
     // This should refer to subone's `bar`
     // @has issue_55364/subone/fn.foo.html
-    // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar'
+    // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
     /// See [bar]
     pub fn foo() {}
     // This should refer to subone's `foo`
     // @has issue_55364/subone/fn.bar.html
-    // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo'
+    // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
     /// See [foo]
     pub fn bar() {}
 }
@@ -23,11 +23,11 @@ pub fn bar() {}
 
 // @has issue_55364/subtwo/index.html
 // These foo/bar links in the module's documentation should not reference inside `subtwo`
-// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo'
-// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar'
+// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
 // Instead it should be referencing the top level functions
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
 // Though there should be such links later
 // @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.foo.html"]' 'foo'
 // @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.bar.html"]' 'bar'
@@ -37,13 +37,13 @@ pub mod subtwo {
     // Despite the module's docs referring to the top level foo/bar,
     // this should refer to subtwo's `bar`
     // @has issue_55364/subtwo/fn.foo.html
-    // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar'
+    // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
     /// See [bar]
     pub fn foo() {}
     // Despite the module's docs referring to the top level foo/bar,
     // this should refer to subtwo's `foo`
     // @has issue_55364/subtwo/fn.bar.html
-    // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo'
+    // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
     /// See [foo]
     pub fn bar() {}
 }
@@ -59,8 +59,8 @@ pub fn bar() {}
 
 // @has issue_55364/subthree/index.html
 // This module should also refer to the top level foo/bar
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
 pub mod subthree {
     //! See either [foo][super::foo] or [bar][super::bar]
 }
@@ -68,8 +68,8 @@ pub mod subthree {
 // Next we go *deeper* - In order to ensure it's not just "this or parent"
 // we test `crate::` and a `super::super::...` chain
 // @has issue_55364/subfour/subfive/subsix/subseven/subeight/index.html
-// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subone/fn.foo.html"]' 'other foo'
-// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subtwo/fn.bar.html"]' 'other bar'
+// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
+// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
 pub mod subfour {
     pub mod subfive {
         pub mod subsix {
index 5b67817fa4caaf6d4dfac69a4521f10d38429aa4..b932a3d34749cc364e48a7ead86e918f819b6da0 100644 (file)
@@ -17,7 +17,7 @@ impl<'a, T> MyTrait for Inner<'a, T> {
 }
 
 // @has issue_56822/struct.Parser.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'a> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'a> Send for \
 // Parser<'a>"
 pub struct Parser<'a> {
     field: <Wrapper<Inner<'a, u8>> as MyTrait>::Output
index 6acc86277385de125041e9e09f4f45c7dbadadd4..79b8b70c5452572c5e468e56f0b1b66f10206026 100644 (file)
@@ -26,9 +26,9 @@ unsafe impl<I> Send for DynTrait<I>
 {}
 
 // @has issue_60726/struct.IntoIter.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Send for \
 // IntoIter<T>"
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Sync for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Sync for \
 // IntoIter<T>"
 pub struct IntoIter<T>{
     hello:DynTrait<FooInterface<T>>,
index 6ed3bfbe3e54bdcd64f54c9d89bdb4ebe39f43bc..64044cfe94720f5531b77147f04560653104b042 100644 (file)
@@ -10,7 +10,7 @@ pub fn empty() -> Self {
 }
 
 impl Default for Body {
-    // @has foo/struct.Body.html '//a/@href' '../foo/struct.Body.html#method.empty'
+    // @has foo/struct.Body.html '//a/@href' 'struct.Body.html#method.empty'
 
     /// Returns [`Body::empty()`](Body::empty).
     fn default() -> Body {
diff --git a/src/test/rustdoc/item-hide-threshold.rs b/src/test/rustdoc/item-hide-threshold.rs
new file mode 100644 (file)
index 0000000..8986f72
--- /dev/null
@@ -0,0 +1,156 @@
+#![allow(unused)]
+
+// @has 'item_hide_threshold/struct.PubStruct.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+pub struct PubStruct {
+    pub a: usize,
+    pub b: usize,
+}
+
+// @has 'item_hide_threshold/struct.BigPubStruct.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields'
+pub struct BigPubStruct {
+    pub a: usize,
+    pub b: usize,
+    pub c: usize,
+    pub d: usize,
+    pub e: usize,
+    pub f: usize,
+    pub g: usize,
+    pub h: usize,
+    pub i: usize,
+    pub j: usize,
+    pub k: usize,
+    pub l: usize,
+    pub m: usize,
+}
+
+// @has 'item_hide_threshold/union.BigUnion.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields'
+pub union BigUnion {
+    pub a: usize,
+    pub b: usize,
+    pub c: usize,
+    pub d: usize,
+    pub e: usize,
+    pub f: usize,
+    pub g: usize,
+    pub h: usize,
+    pub i: usize,
+    pub j: usize,
+    pub k: usize,
+    pub l: usize,
+    pub m: usize,
+}
+
+// @has 'item_hide_threshold/union.Union.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+pub union Union {
+    pub a: usize,
+    pub b: usize,
+    pub c: usize,
+}
+
+// @has 'item_hide_threshold/struct.PrivStruct.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+// @has - '//div[@class="docblock type-decl"]' 'fields omitted'
+pub struct PrivStruct {
+    a: usize,
+    b: usize,
+}
+
+// @has 'item_hide_threshold/enum.Enum.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields'
+pub enum Enum {
+    A, B, C,
+    D {
+        a: u8,
+        b: u8
+    }
+}
+
+// @has 'item_hide_threshold/enum.LargeEnum.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show variants'
+pub enum LargeEnum {
+    A, B, C, D, E, F(u8), G, H, I, J, K, L, M
+}
+
+// @has 'item_hide_threshold/trait.Trait.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+pub trait Trait {
+    type A;
+    #[must_use]
+    fn foo();
+    fn bar();
+}
+
+// @has 'item_hide_threshold/trait.GinormousTrait.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show associated items'
+pub trait GinormousTrait {
+    type A;
+    type B;
+    type C;
+    type D;
+    type E;
+    type F;
+    type G;
+    type H;
+    type I;
+    type J;
+    type K;
+    type L;
+    type M;
+    const N: usize = 1;
+    #[must_use]
+    fn foo();
+    fn bar();
+}
+
+// @has 'item_hide_threshold/trait.HugeTrait.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show associated constants and methods'
+pub trait HugeTrait {
+    type A;
+    const M: usize = 1;
+    const N: usize = 1;
+    const O: usize = 1;
+    const P: usize = 1;
+    const Q: usize = 1;
+    const R: usize = 1;
+    const S: usize = 1;
+    const T: usize = 1;
+    const U: usize = 1;
+    const V: usize = 1;
+    const W: usize = 1;
+    const X: usize = 1;
+    #[must_use]
+    fn foo();
+    fn bar();
+}
+
+// @has 'item_hide_threshold/trait.BigTrait.html'
+// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
+// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show methods'
+pub trait BigTrait {
+    type A;
+    #[must_use]
+    fn foo();
+    fn bar();
+    fn baz();
+    fn quux();
+    fn frob();
+    fn greeble();
+    fn blap();
+    fn whoop();
+    fn pow();
+    fn bang();
+    fn oomph();
+    fn argh();
+    fn wap();
+    fn ouch();
+}
index f9eb2b722d6e6307543e005fb561fca672bf7b08..75a2531a308f7d95d31d703bee35a99682e2efb3 100644 (file)
@@ -1,7 +1,7 @@
 #![crate_name = "foo"]
 
-// @has foo/index.html '//a[@href="../foo/foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST'
-// @has foo/index.html '//a[@href="../foo/struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST'
+// @has foo/index.html '//a[@href="foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST'
+// @has foo/index.html '//a[@href="struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST'
 
 //! We have here [`foo::FIRSTCONST`] and [`Bar::CONST`].
 
diff --git a/src/test/rustdoc/prim-title.rs b/src/test/rustdoc/prim-title.rs
deleted file mode 100644 (file)
index fa3fd51..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#![crate_name = "foo"]
-
-// @has foo/primitive.u8.html '//head/title' 'u8 - Rust'
-// @!has - '//head/title' 'foo'
-#[doc(primitive = "u8")]
-/// `u8` docs
-mod u8 {}
index 82196e413e94b856a267a4e1a78541a2835e94e5..f6d1f2cf91b5fca1915212708a47ee68c3d6473b 100644 (file)
@@ -61,12 +61,12 @@ pub fn some_derive(_item: TokenStream) -> TokenStream {
 // @has some_macros/foo/index.html
 mod foo {
     // @has - '//code' 'pub use some_proc_macro;'
-    // @has - '//a/@href' '../../some_macros/macro.some_proc_macro.html'
+    // @has - '//a/@href' '../macro.some_proc_macro.html'
     pub use some_proc_macro;
     // @has - '//code' 'pub use some_proc_attr;'
-    // @has - '//a/@href' '../../some_macros/attr.some_proc_attr.html'
+    // @has - '//a/@href' '../attr.some_proc_attr.html'
     pub use some_proc_attr;
     // @has - '//code' 'pub use some_derive;'
-    // @has - '//a/@href' '../../some_macros/derive.SomeDerive.html'
+    // @has - '//a/@href' '../derive.SomeDerive.html'
     pub use some_derive;
 }
index 3ecf434c39e45a88ffe719fdb947b6d51560694f..ad190361267606a0e514d7ee23f93eb5ce12de94 100644 (file)
@@ -8,13 +8,13 @@ pub mod internal {
     ///
     /// [name]: mod
     /// [other name]: crate::internal::mod
-    // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name'
-    // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name'
+    // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'name'
+    // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'other name'
     pub struct B;
 }
 
 /// See [name].
 ///
 /// [name]: internal::mod
-// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name'
+// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="internal/struct.mod.html"]' 'name'
 pub struct A;
diff --git a/src/test/rustdoc/reexport-stability-tags-deprecated-and-portability.rs b/src/test/rustdoc/reexport-stability-tags-deprecated-and-portability.rs
new file mode 100644 (file)
index 0000000..a79d059
--- /dev/null
@@ -0,0 +1,48 @@
+#![crate_name = "foo"]
+#![feature(doc_cfg)]
+
+pub mod tag {
+    #[deprecated(since = "0.1.8", note = "Use bar() instead")]
+    pub trait Deprecated {}
+
+    #[doc(cfg(feature = "sync"))]
+    pub trait Portability {}
+
+    #[deprecated(since = "0.1.8", note = "Use bar() instead")]
+    #[doc(cfg(feature = "sync"))]
+    pub trait Both {}
+
+    pub trait None {}
+}
+
+// @has foo/mod1/index.html
+pub mod mod1 {
+    // @has - '//code' 'pub use tag::Deprecated;'
+    // @has - '//span' 'Deprecated'
+    // @!has - '//span' 'sync'
+    pub use tag::Deprecated;
+}
+
+// @has foo/mod2/index.html
+pub mod mod2 {
+    // @has - '//code' 'pub use tag::Portability;'
+    // @!has - '//span' 'Deprecated'
+    // @has - '//span' 'sync'
+    pub use tag::Portability;
+}
+
+// @has foo/mod3/index.html
+pub mod mod3 {
+    // @has - '//code' 'pub use tag::Both;'
+    // @has - '//span' 'Deprecated'
+    // @has - '//span' 'sync'
+    pub use tag::Both;
+}
+
+// @has foo/mod4/index.html
+pub mod mod4 {
+    // @has - '//code' 'pub use tag::None;'
+    // @!has - '//span' 'Deprecated'
+    // @!has - '//span' 'sync'
+    pub use tag::None;
+}
diff --git a/src/test/rustdoc/reexport-stability-tags-unstable-and-portability.rs b/src/test/rustdoc/reexport-stability-tags-unstable-and-portability.rs
new file mode 100644 (file)
index 0000000..ff8a910
--- /dev/null
@@ -0,0 +1,61 @@
+#![crate_name = "foo"]
+#![feature(doc_cfg)]
+#![feature(staged_api)]
+#![stable(feature = "rust1", since = "1.0.0")]
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod tag {
+    #[unstable(feature = "humans", issue = "none")]
+    pub trait Unstable {}
+
+    #[stable(feature = "rust1", since = "1.0.0")]
+    #[doc(cfg(feature = "sync"))]
+    pub trait Portability {}
+
+    #[unstable(feature = "humans", issue = "none")]
+    #[doc(cfg(feature = "sync"))]
+    pub trait Both {}
+
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub trait None {}
+}
+
+// @has foo/mod1/index.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod mod1 {
+    // @has - '//code' 'pub use tag::Unstable;'
+    // @has - '//span' 'Experimental'
+    // @!has - '//span' 'sync'
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub use tag::Unstable;
+}
+
+// @has foo/mod2/index.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod mod2 {
+    // @has - '//code' 'pub use tag::Portability;'
+    // @!has - '//span' 'Experimental'
+    // @has - '//span' 'sync'
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub use tag::Portability;
+}
+
+// @has foo/mod3/index.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod mod3 {
+    // @has - '//code' 'pub use tag::Both;'
+    // @has - '//span' 'Experimental'
+    // @has - '//span' 'sync'
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub use tag::Both;
+}
+
+// @has foo/mod4/index.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod mod4 {
+    // @has - '//code' 'pub use tag::None;'
+    // @!has - '//span' 'Experimental'
+    // @!has - '//span' 'sync'
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub use tag::None;
+}
index 974b863bb160446cd59f7d6ea3f34984d1f70a8a..998683bdde7f27236af2fedf949f96fa81822ca5 100644 (file)
@@ -1,9 +1,9 @@
 #![crate_name = "foo"]
 
 
-// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/struct.Foo.html#structfield.bar"]' 'Foo::bar'
-// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/union.Bar.html#structfield.foo"]' 'Bar::foo'
-// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/enum.Uniooon.html#variant.X"]' 'Uniooon::X'
+// @has foo/index.html '//*[@class="docblock"]/p/a[@href="struct.Foo.html#structfield.bar"]' 'Foo::bar'
+// @has foo/index.html '//*[@class="docblock"]/p/a[@href="union.Bar.html#structfield.foo"]' 'Bar::foo'
+// @has foo/index.html '//*[@class="docblock"]/p/a[@href="enum.Uniooon.html#variant.X"]' 'Uniooon::X'
 
 //! Test with [Foo::bar], [Bar::foo], [Uniooon::X]
 
index 38de5316b6cf55276451f87e2b852081ace7f1d5..0dd3a3f7a86c5884e5586d6364b727f6702b6698 100644 (file)
@@ -1,8 +1,8 @@
 // @has basic/struct.Foo.html
 // @has - '//code' 'impl<T> Send for Foo<T> where T: Send'
 // @has - '//code' 'impl<T> Sync for Foo<T> where T: Sync'
-// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 5
+// @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5
 pub struct Foo<T> {
     field: T,
 }
index 80a717718c22b607a6dc920d72cad34aa5eb4339..d951a20e2dec04820e90b610884f10b07e3b2a89 100644 (file)
@@ -20,7 +20,7 @@ pub struct Foo<T> {
 }
 
 // @has complex/struct.NotOuter.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'a, T, K: \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'a, T, K: \
 // ?Sized> Send for Outer<'a, T, K> where K: for<'b> Fn((&'b bool, &'a u8)) \
 // -> &'b i8, T: MyTrait<'a>, <T as MyTrait<'a>>::MyItem: Copy, 'a: 'static"
 
index 6d0a68f9b0734eeaaf7c0f465e012d1593df9a5c..05c88f10822ca1563d345f48a8b4d94eba81fbc8 100644 (file)
@@ -9,10 +9,10 @@ unsafe impl<'a, T> Send for Inner<'a, T>
 {}
 
 // @has lifetimes/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Send \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Send \
 // for Foo<'c, K> where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static"
 //
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Sync \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Sync \
 // for Foo<'c, K> where K: Sync"
 pub struct Foo<'c, K: 'c> {
     inner_field: Inner<'c, K>,
index d20b4744af15b413a7a7ea71f2e10d39610d388f..88ddd57349a29565fd7fc4241450e044f1c3d382 100644 (file)
@@ -1,12 +1,12 @@
 // @has manual/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl<T> Sync for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' 'impl<T> Sync for \
 // Foo<T> where T: Sync'
 //
-// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' \
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' \
 // 'impl<T> Send for Foo<T>'
 //
-// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 4
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 4
 pub struct Foo<T> {
     field: T,
 }
index 30713849da221fd6bcf278778cbff88aa19d2658..53801542c95205c13dc3bfb5304964329a93ec37 100644 (file)
@@ -3,10 +3,10 @@ pub struct Inner<T: Copy> {
 }
 
 // @has negative/struct.Outer.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> !Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> !Send for \
 // Outer<T>"
 //
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> \
 // !Sync for Outer<T>"
 pub struct Outer<T: Copy> {
     inner_field: Inner<T>,
index e710ce1c2ed95eb1c49a21da5a81d28ecabc9998..d4d93a87ffc9b891c22981e069fc98138e029c05 100644 (file)
@@ -9,10 +9,10 @@ unsafe impl<T> Send for Inner<T>
 }
 
 // @has nested/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl<T> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' 'impl<T> Send for \
 // Foo<T> where T: Copy'
 //
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' \
 // 'impl<T> Sync for Foo<T> where T: Sync'
 pub struct Foo<T> {
     inner_field: Inner<T>,
index cf173111ec1e27ebdab26114d4315721d1ccff01..3a23dc2cf9576c295ce5ee7ab886b621821feef7 100644 (file)
@@ -9,7 +9,7 @@ unsafe impl<T> Send for Inner<T>
 }
 
 // @has no_redundancy/struct.Outer.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> Send for \
 // Outer<T> where T: Copy + Send"
 pub struct Outer<T> {
     inner_field: Inner<T>,
index 5346521f8d2e365cbffabda500186b9e781605fc..060491e3cf10f7c5a2487306d217e9471f8cfc63 100644 (file)
@@ -23,10 +23,10 @@ unsafe impl<'a, T> Sync for Inner<'a, T>
 }
 
 // @has project/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Send \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Send \
 // for Foo<'c, K> where K: MyTrait<MyItem = bool>, 'c: 'static"
 //
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Sync \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Sync \
 // for Foo<'c, K> where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, 'c: 'static,"
 pub struct Foo<'c, K: 'c> {
     inner_field: Inner<'c, K>,
index 905aa20918beff2e6ee91c59626c68641586af87..ecdbdf41b2025f6f5c2b1bdeb7071c1bea90805c 100644 (file)
@@ -23,7 +23,7 @@ impl<T> Pattern for Wrapper<T> {
 
 
 // @has self_referential/struct.WriteAndThen.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<P1> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<P1> Send for \
 // WriteAndThen<P1>  where  <P1 as Pattern>::Value: Send"
 pub struct WriteAndThen<P1>(pub P1::Value,pub <Constrain<P1, Wrapper<P1::Value>> as Pattern>::Value)
     where P1: Pattern;
index 59493744b623d7155f82ad268e1939cb9eda97af..a10e694c1b2817c79974d87b4d3fdad374aac792 100644 (file)
@@ -3,7 +3,7 @@ pub trait OwnedTrait<'a> {
 }
 
 // @has static_region/struct.Owned.html
-// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<T> Send for \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<T> Send for \
 // Owned<T> where <T as OwnedTrait<'static>>::Reader: Send"
 pub struct Owned<T> where T: OwnedTrait<'static> {
     marker: <T as OwnedTrait<'static>>::Reader,
diff --git a/src/test/rustdoc/tab_title.rs b/src/test/rustdoc/tab_title.rs
new file mode 100644 (file)
index 0000000..7dce609
--- /dev/null
@@ -0,0 +1,44 @@
+#![crate_name = "foo"]
+#![feature(doc_keyword)]
+
+// tests for the html <title> element
+
+// @has foo/index.html '//head/title' 'foo - Rust'
+
+// @has foo/fn.widget_count.html '//head/title' 'widget_count in foo - Rust'
+/// blah
+pub fn widget_count() {}
+
+// @has foo/struct.Widget.html '//head/title' 'Widget in foo - Rust'
+pub struct Widget;
+
+// @has foo/constant.ANSWER.html '//head/title' 'ANSWER in foo - Rust'
+pub const ANSWER: u8 = 42;
+
+// @has foo/blah/index.html '//head/title' 'foo::blah - Rust'
+pub mod blah {
+    // @has foo/blah/struct.Widget.html '//head/title' 'Widget in foo::blah - Rust'
+    pub struct Widget;
+
+    // @has foo/blah/trait.Awesome.html '//head/title' 'Awesome in foo::blah - Rust'
+    pub trait Awesome {}
+
+    // @has foo/blah/fn.make_widget.html '//head/title' 'make_widget in foo::blah - Rust'
+    pub fn make_widget() {}
+
+    // @has foo/macro.cool_macro.html '//head/title' 'cool_macro in foo - Rust'
+    #[macro_export]
+    macro_rules! cool_macro {
+        ($t:tt) => { $t }
+    }
+}
+
+// @has foo/keyword.continue.html '//head/title' 'continue - Rust'
+#[doc(keyword = "continue")]
+mod continue_keyword {}
+
+// @has foo/primitive.u8.html '//head/title' 'u8 - Rust'
+// @!has - '//head/title' 'foo'
+#[doc(primitive = "u8")]
+/// `u8` docs
+mod u8 {}
index 2a103509ae1933e9f4cb303e38070cee731d2bc8..2bb24a82193feef3f87a96678743c3e6f701f49f 100644 (file)
@@ -2,7 +2,7 @@
 
 
 pub trait Foo {
-    // @has foo/trait.Foo.html '//h3[@id="tymethod.foo"]//span[@class="docblock attributes"]' '#[must_use]'
+    // @has foo/trait.Foo.html '//h3[@id="tymethod.foo"]//div[@class="code-attribute"]' '#[must_use]'
     #[must_use]
     fn foo();
 }
@@ -11,11 +11,11 @@ pub trait Foo {
 pub struct Bar;
 
 impl Bar {
-    // @has foo/struct.Bar.html '//h4[@id="method.bar"]//span[@class="docblock attributes"]' '#[must_use]'
+    // @has foo/struct.Bar.html '//h4[@id="method.bar"]//div[@class="code-attribute"]' '#[must_use]'
     #[must_use]
     pub fn bar() {}
 
-    // @has foo/struct.Bar.html '//h4[@id="method.bar2"]//span[@class="docblock attributes"]' '#[must_use]'
+    // @has foo/struct.Bar.html '//h4[@id="method.bar2"]//div[@class="code-attribute"]' '#[must_use]'
     #[must_use]
     pub fn bar2() {}
 }
index 6c09be1144a83a3c8f96eb52753b13121ecba8ca..c6a9313e821c9562fc55c2b952829b52d0d37cbc 100644 (file)
@@ -40,25 +40,25 @@ fn defaulted_override(&self) {}
 impl MyTrait for MyStruct {
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="type"]/@href' #associatedtype.Assoc
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3
-    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#associatedtype.Assoc
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' trait.MyTrait.html#associatedtype.Assoc
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc
     type Assoc = bool;
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="constant"]/@href' #associatedconstant.VALUE
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3
-    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#associatedconstant.VALUE
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE
     const VALUE: u32 = 20;
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="fnname"]/@href' #tymethod.trait_function
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="anchor"]/@href' #method.trait_function-2
-    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#tymethod.trait_function
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
     fn trait_function(&self) {}
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="fnname"]/@href' #method.defaulted_override
     // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="anchor"]/@href' #method.defaulted_override-3
-    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#method.defaulted_override
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override
     fn defaulted_override(&self) {}
-    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#method.defaulted
+    // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted
     // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted
 }
 
index bac28b44012cc292763030594afca4c7f8610251..e311dadff0e637f1393573cbbc00a4600df289cc 100644 (file)
@@ -1,4 +1,4 @@
-// @has trait_self_link/trait.Foo.html //a/@href ../trait_self_link/trait.Foo.html
+// @has trait_self_link/trait.Foo.html //a/@href trait.Foo.html
 pub trait Foo {}
 
 pub struct Bar;
index c1c9bf62819af5b6fcb258df381f2a3de3927b3d..b50f6ab5deb1569624f34c9bfddcb90e72be94be 100644 (file)
@@ -1,6 +1,6 @@
 fn test() {
     let v: isize;
-    //~^ HELP make this binding mutable
+    //~^ HELP consider making this binding mutable
     //~| SUGGESTION mut v
     v = 1; //~ NOTE first assignment
     println!("v={}", v);
index df0f4c4d80608867d3e6e14d85046f4e58d81cc4..bba5d8dffe4bd8a2a26ec38245fb7f95ad33acc8 100644 (file)
@@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v`
   --> $DIR/assign-imm-local-twice.rs:7:5
    |
 LL |     let v: isize;
-   |         - help: make this binding mutable: `mut v`
+   |         - help: consider making this binding mutable: `mut v`
 ...
 LL |     v = 1;
    |     ----- first assignment to `v`
diff --git a/src/test/ui/associated-type-bounds/issue-79949.rs b/src/test/ui/associated-type-bounds/issue-79949.rs
new file mode 100644 (file)
index 0000000..9f924f1
--- /dev/null
@@ -0,0 +1,26 @@
+// check-pass
+
+#![allow(incomplete_features)]
+#![feature(associated_type_bounds)]
+#![feature(generic_associated_types)]
+
+trait MP {
+    type T<'a>;
+}
+struct S(String);
+impl MP for S {
+    type T<'a> = &'a str;
+}
+
+trait SR: MP {
+    fn sr<IM>(&self) -> i32
+    where
+        for<'a> IM: T<T: U<<Self as MP>::T<'a>>>;
+}
+
+trait T {
+    type T;
+}
+trait U<X> {}
+
+fn main() {}
diff --git a/src/test/ui/associated-type-bounds/issue-81193.rs b/src/test/ui/associated-type-bounds/issue-81193.rs
new file mode 100644 (file)
index 0000000..d2aa54a
--- /dev/null
@@ -0,0 +1,15 @@
+// check-pass
+
+#![feature(associated_type_bounds)]
+
+trait A<'a, 'b> {}
+
+trait B<'a, 'b, 'c> {}
+
+fn err<'u, 'a, F>()
+where
+    for<'b> F: Iterator<Item: for<'c> B<'a, 'b, 'c> + for<'c> A<'a, 'c>>,
+{
+}
+
+fn main() {}
diff --git a/src/test/ui/associated-type-bounds/issue-83017.rs b/src/test/ui/associated-type-bounds/issue-83017.rs
new file mode 100644 (file)
index 0000000..8f0a9ea
--- /dev/null
@@ -0,0 +1,39 @@
+#![feature(associated_type_bounds)]
+
+trait TraitA<'a> {
+    type AsA;
+}
+
+trait TraitB<'a, 'b> {
+    type AsB;
+}
+
+trait TraitC<'a, 'b, 'c> {}
+
+struct X;
+
+impl<'a, 'b, 'c> TraitC<'a, 'b, 'c> for X {}
+
+struct Y;
+
+impl<'a, 'b> TraitB<'a, 'b> for Y {
+    type AsB = X;
+}
+
+struct Z;
+
+impl<'a> TraitA<'a> for Z {
+    type AsA = Y;
+}
+
+fn foo<T>()
+where
+    for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>,
+{
+}
+
+fn main() {
+    foo::<Z>();
+    //~^ ERROR: the trait bound `for<'a, 'b> <Z as TraitA<'a>>::AsA: TraitB<'a, 'b>` is not satisfied
+    //~| ERROR: the trait bound `for<'a, 'b, 'c> <<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB: TraitC<'a, 'b, 'c>` is not satisfied
+}
diff --git a/src/test/ui/associated-type-bounds/issue-83017.stderr b/src/test/ui/associated-type-bounds/issue-83017.stderr
new file mode 100644 (file)
index 0000000..4eb71fd
--- /dev/null
@@ -0,0 +1,27 @@
+error[E0277]: the trait bound `for<'a, 'b> <Z as TraitA<'a>>::AsA: TraitB<'a, 'b>` is not satisfied
+  --> $DIR/issue-83017.rs:36:5
+   |
+LL | fn foo<T>()
+   |    --- required by a bound in this
+LL | where
+LL |     for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>,
+   |                                ------------------------------------------------------- required by this bound in `foo`
+...
+LL |     foo::<Z>();
+   |     ^^^^^^^^ the trait `for<'a, 'b> TraitB<'a, 'b>` is not implemented for `<Z as TraitA<'a>>::AsA`
+
+error[E0277]: the trait bound `for<'a, 'b, 'c> <<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB: TraitC<'a, 'b, 'c>` is not satisfied
+  --> $DIR/issue-83017.rs:36:5
+   |
+LL | fn foo<T>()
+   |    --- required by a bound in this
+LL | where
+LL |     for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>,
+   |                                                            -------------------------- required by this bound in `foo`
+...
+LL |     foo::<Z>();
+   |     ^^^^^^^^ the trait `for<'a, 'b, 'c> TraitC<'a, 'b, 'c>` is not implemented for `<<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
index f0403b76620c1ba076a7c07303783c7b420d141b..e2062e82725c0507fad934ec9f6720dfab28ea71 100644 (file)
@@ -2,6 +2,10 @@
 trait T {
     async fn foo() {} //~ ERROR functions in traits cannot be declared `async`
     async fn bar(&self) {} //~ ERROR functions in traits cannot be declared `async`
+    async fn baz() { //~ ERROR functions in traits cannot be declared `async`
+        // Nested item must not ICE.
+        fn a() {}
+    }
 }
 
 fn main() {}
index 6080b2815eeb8cd3577aaa74515ff4c3632f066a..1eb8969a80d20047db21c886240c0dc11aebc73d 100644 (file)
@@ -20,6 +20,22 @@ LL |     async fn bar(&self) {}
    = note: `async` trait functions are not currently supported
    = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
 
-error: aborting due to 2 previous errors
+error[E0706]: functions in traits cannot be declared `async`
+  --> $DIR/async-trait-fn.rs:5:5
+   |
+LL |       async fn baz() {
+   |       ^----
+   |       |
+   |  _____`async` because of this
+   | |
+LL | |         // Nested item must not ICE.
+LL | |         fn a() {}
+LL | |     }
+   | |_____^
+   |
+   = note: `async` trait functions are not currently supported
+   = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0706`.
index 5eb4b548717371107b022ab0fb562d3b7bbb698b..f2dec87baf08b446f558e43945e66c5de6055514 100644 (file)
@@ -13,7 +13,7 @@ LL | pub async fn g(x: usize) {
    |                -
    |                |
    |                first assignment to `x`
-   |                help: make this binding mutable: `mut x`
+   |                help: consider making this binding mutable: `mut x`
 LL |     x += 1;
    |     ^^^^^^ cannot assign twice to immutable variable
 
diff --git a/src/test/ui/async-await/large_moves.rs b/src/test/ui/async-await/large_moves.rs
new file mode 100644 (file)
index 0000000..4fac046
--- /dev/null
@@ -0,0 +1,24 @@
+#![deny(large_assignments)]
+#![feature(large_assignments)]
+#![move_size_limit = "1000"]
+// build-fail
+// only-x86_64
+
+// edition:2018
+
+fn main() {
+    let x = async { //~ ERROR large_assignments
+        let y = [0; 9999];
+        dbg!(y);
+        thing(&y).await;
+        dbg!(y);
+    };
+    let z = (x, 42); //~ ERROR large_assignments
+    //~^ ERROR large_assignments
+    let a = z.0; //~ ERROR large_assignments
+    let b = z.1;
+}
+
+async fn thing(y: &[u8]) {
+    dbg!(y);
+}
diff --git a/src/test/ui/async-await/large_moves.stderr b/src/test/ui/async-await/large_moves.stderr
new file mode 100644 (file)
index 0000000..8c47ec0
--- /dev/null
@@ -0,0 +1,38 @@
+error: moving 10024 bytes
+  --> $DIR/large_moves.rs:10:13
+   |
+LL |       let x = async {
+   |  _____________^
+LL | |         let y = [0; 9999];
+LL | |         dbg!(y);
+LL | |         thing(&y).await;
+LL | |         dbg!(y);
+LL | |     };
+   | |_____^ value moved from here
+   |
+note: the lint level is defined here
+  --> $DIR/large_moves.rs:1:9
+   |
+LL | #![deny(large_assignments)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: moving 10024 bytes
+  --> $DIR/large_moves.rs:16:14
+   |
+LL |     let z = (x, 42);
+   |              ^ value moved from here
+
+error: moving 10024 bytes
+  --> $DIR/large_moves.rs:16:13
+   |
+LL |     let z = (x, 42);
+   |             ^^^^^^^ value moved from here
+
+error: moving 10024 bytes
+  --> $DIR/large_moves.rs:18:13
+   |
+LL |     let a = z.0;
+   |             ^^^ value moved from here
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/attr-main-2.rs b/src/test/ui/attr-main-2.rs
deleted file mode 100644 (file)
index 3a51f83..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-// run-pass
-
-#![feature(main)]
-
-pub fn main() {
-    panic!()
-}
-
-#[main]
-fn foo() {
-}
diff --git a/src/test/ui/attr-main.rs b/src/test/ui/attr-main.rs
deleted file mode 100644 (file)
index 9c4caaa..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-// run-pass
-// pretty-expanded FIXME #23616
-
-#![feature(main)]
-
-#[main]
-fn foo() {
-}
diff --git a/src/test/ui/attr.rs b/src/test/ui/attr.rs
deleted file mode 100644 (file)
index 9c4caaa..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-// run-pass
-// pretty-expanded FIXME #23616
-
-#![feature(main)]
-
-#[main]
-fn foo() {
-}
diff --git a/src/test/ui/bad/bad-const-type.rs b/src/test/ui/bad/bad-const-type.rs
deleted file mode 100644 (file)
index 934ee35..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-static i: String = 10;
-//~^ ERROR mismatched types
-//~| expected struct `String`, found integer
-fn main() { println!("{}", i); }
diff --git a/src/test/ui/bad/bad-const-type.stderr b/src/test/ui/bad/bad-const-type.stderr
deleted file mode 100644 (file)
index a9c84b4..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/bad-const-type.rs:1:20
-   |
-LL | static i: String = 10;
-   |                    ^^
-   |                    |
-   |                    expected struct `String`, found integer
-   |                    help: try using a conversion method: `10.to_string()`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/bad/bad-crate-name.rs b/src/test/ui/bad/bad-crate-name.rs
deleted file mode 100644 (file)
index 837d5c3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-extern crate krate-name-here;
-//~^ ERROR crate name using dashes are not valid in `extern crate` statements
-//~| ERROR can't find crate for `krate_name_here`
-
-fn main() {}
diff --git a/src/test/ui/bad/bad-crate-name.stderr b/src/test/ui/bad/bad-crate-name.stderr
deleted file mode 100644 (file)
index e015010..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-error: crate name using dashes are not valid in `extern crate` statements
-  --> $DIR/bad-crate-name.rs:1:14
-   |
-LL | extern crate krate-name-here;
-   |              ^^^^^^^^^^^^^^^ dash-separated idents are not valid
-   |
-help: if the original crate name uses dashes you need to use underscores in the code
-   |
-LL | extern crate krate_name_here;
-   |                   ^    ^
-
-error[E0463]: can't find crate for `krate_name_here`
-  --> $DIR/bad-crate-name.rs:1:1
-   |
-LL | extern crate krate-name-here;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0463`.
diff --git a/src/test/ui/bad/bad-env-capture.rs b/src/test/ui/bad/bad-env-capture.rs
deleted file mode 100644 (file)
index 83fd254..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-// error-pattern: can't capture dynamic environment in a fn item
-fn foo() {
-    let x: isize;
-    fn bar() { log(debug, x); }
-}
-fn main() { foo(); }
diff --git a/src/test/ui/bad/bad-env-capture.stderr b/src/test/ui/bad/bad-env-capture.stderr
deleted file mode 100644 (file)
index f78a38a..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-error[E0434]: can't capture dynamic environment in a fn item
-  --> $DIR/bad-env-capture.rs:4:27
-   |
-LL |     fn bar() { log(debug, x); }
-   |                           ^
-   |
-   = help: use the `|| { ... }` closure form instead
-
-error[E0425]: cannot find function `log` in this scope
-  --> $DIR/bad-env-capture.rs:4:16
-   |
-LL |     fn bar() { log(debug, x); }
-   |                ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
-  --> $DIR/bad-env-capture.rs:4:20
-   |
-LL |     fn bar() { log(debug, x); }
-   |                    ^^^^^ not found in this scope
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0425, E0434.
-For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/bad/bad-env-capture2.rs b/src/test/ui/bad/bad-env-capture2.rs
deleted file mode 100644 (file)
index b04569c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-// error-pattern: can't capture dynamic environment in a fn item
-fn foo(x: isize) {
-    fn bar() { log(debug, x); }
-}
-fn main() { foo(2); }
diff --git a/src/test/ui/bad/bad-env-capture2.stderr b/src/test/ui/bad/bad-env-capture2.stderr
deleted file mode 100644 (file)
index 57c807f..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-error[E0434]: can't capture dynamic environment in a fn item
-  --> $DIR/bad-env-capture2.rs:3:27
-   |
-LL |     fn bar() { log(debug, x); }
-   |                           ^
-   |
-   = help: use the `|| { ... }` closure form instead
-
-error[E0425]: cannot find function `log` in this scope
-  --> $DIR/bad-env-capture2.rs:3:16
-   |
-LL |     fn bar() { log(debug, x); }
-   |                ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
-  --> $DIR/bad-env-capture2.rs:3:20
-   |
-LL |     fn bar() { log(debug, x); }
-   |                    ^^^^^ not found in this scope
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0425, E0434.
-For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/bad/bad-env-capture3.rs b/src/test/ui/bad/bad-env-capture3.rs
deleted file mode 100644 (file)
index 62f12fd..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-// error-pattern: can't capture dynamic environment in a fn item
-fn foo(x: isize) {
-    fn mth() {
-        fn bar() { log(debug, x); }
-    }
-}
-
-fn main() { foo(2); }
diff --git a/src/test/ui/bad/bad-env-capture3.stderr b/src/test/ui/bad/bad-env-capture3.stderr
deleted file mode 100644 (file)
index d6eb4f8..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-error[E0434]: can't capture dynamic environment in a fn item
-  --> $DIR/bad-env-capture3.rs:4:31
-   |
-LL |         fn bar() { log(debug, x); }
-   |                               ^
-   |
-   = help: use the `|| { ... }` closure form instead
-
-error[E0425]: cannot find function `log` in this scope
-  --> $DIR/bad-env-capture3.rs:4:20
-   |
-LL |         fn bar() { log(debug, x); }
-   |                    ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
-  --> $DIR/bad-env-capture3.rs:4:24
-   |
-LL |         fn bar() { log(debug, x); }
-   |                        ^^^^^ not found in this scope
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0425, E0434.
-For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/bad/bad-expr-lhs.rs b/src/test/ui/bad/bad-expr-lhs.rs
deleted file mode 100644 (file)
index 39536f1..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-fn main() {
-    1 = 2; //~ ERROR invalid left-hand side of assignment
-    1 += 2; //~ ERROR invalid left-hand side of assignment
-    (1, 2) = (3, 4); //~ ERROR destructuring assignments are unstable
-    //~| ERROR invalid left-hand side of assignment
-    //~| ERROR invalid left-hand side of assignment
-
-    let (a, b) = (1, 2);
-    (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable
-
-    None = Some(3); //~ ERROR invalid left-hand side of assignment
-}
diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr
deleted file mode 100644 (file)
index d4b2193..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-error[E0658]: destructuring assignments are unstable
-  --> $DIR/bad-expr-lhs.rs:4:12
-   |
-LL |     (1, 2) = (3, 4);
-   |     ------ ^
-   |     |
-   |     cannot assign to this expression
-   |
-   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
-   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
-
-error[E0658]: destructuring assignments are unstable
-  --> $DIR/bad-expr-lhs.rs:9:12
-   |
-LL |     (a, b) = (3, 4);
-   |     ------ ^
-   |     |
-   |     cannot assign to this expression
-   |
-   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
-   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
-
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:2:7
-   |
-LL |     1 = 2;
-   |     - ^
-   |     |
-   |     cannot assign to this expression
-
-error[E0067]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:3:7
-   |
-LL |     1 += 2;
-   |     - ^^
-   |     |
-   |     cannot assign to this expression
-
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:4:12
-   |
-LL |     (1, 2) = (3, 4);
-   |      -     ^
-   |      |
-   |      cannot assign to this expression
-
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:4:12
-   |
-LL |     (1, 2) = (3, 4);
-   |         -  ^
-   |         |
-   |         cannot assign to this expression
-
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:11:10
-   |
-LL |     None = Some(3);
-   |     ---- ^
-   |     |
-   |     cannot assign to this expression
-
-error: aborting due to 7 previous errors
-
-Some errors have detailed explanations: E0067, E0070, E0658.
-For more information about an error, try `rustc --explain E0067`.
diff --git a/src/test/ui/bad/bad-expr-path.rs b/src/test/ui/bad/bad-expr-path.rs
deleted file mode 100644 (file)
index 31fc9cf..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-mod m1 {}
-
-fn main(arguments: Vec<String>) { //~ ERROR `main` function has wrong type
-    log(debug, m1::arguments);
-    //~^ ERROR cannot find function `log` in this scope
-    //~| ERROR cannot find value `debug` in this scope
-    //~| ERROR cannot find value `arguments` in module `m1`
-}
diff --git a/src/test/ui/bad/bad-expr-path.stderr b/src/test/ui/bad/bad-expr-path.stderr
deleted file mode 100644 (file)
index 77c48c9..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-error[E0425]: cannot find function `log` in this scope
-  --> $DIR/bad-expr-path.rs:4:5
-   |
-LL |     log(debug, m1::arguments);
-   |     ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
-  --> $DIR/bad-expr-path.rs:4:9
-   |
-LL |     log(debug, m1::arguments);
-   |         ^^^^^ not found in this scope
-
-error[E0425]: cannot find value `arguments` in module `m1`
-  --> $DIR/bad-expr-path.rs:4:20
-   |
-LL |     log(debug, m1::arguments);
-   |                    ^^^^^^^^^ not found in `m1`
-
-error[E0580]: `main` function has wrong type
-  --> $DIR/bad-expr-path.rs:3:1
-   |
-LL | fn main(arguments: Vec<String>) {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
-   |
-   = note: expected fn pointer `fn()`
-              found fn pointer `fn(Vec<String>)`
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0425, E0580.
-For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/bad/bad-expr-path2.rs b/src/test/ui/bad/bad-expr-path2.rs
deleted file mode 100644 (file)
index eb88edb..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-mod m1 {
-    pub mod arguments {}
-}
-
-fn main(arguments: Vec<String>) { //~ ERROR `main` function has wrong type
-    log(debug, m1::arguments);
-    //~^ ERROR cannot find function `log` in this scope
-    //~| ERROR cannot find value `debug` in this scope
-    //~| ERROR expected value, found module `m1::arguments`
-}
diff --git a/src/test/ui/bad/bad-expr-path2.stderr b/src/test/ui/bad/bad-expr-path2.stderr
deleted file mode 100644 (file)
index d06e102..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-error[E0425]: cannot find function `log` in this scope
-  --> $DIR/bad-expr-path2.rs:6:5
-   |
-LL |     log(debug, m1::arguments);
-   |     ^^^ not found in this scope
-
-error[E0425]: cannot find value `debug` in this scope
-  --> $DIR/bad-expr-path2.rs:6:9
-   |
-LL |     log(debug, m1::arguments);
-   |         ^^^^^ not found in this scope
-
-error[E0423]: expected value, found module `m1::arguments`
-  --> $DIR/bad-expr-path2.rs:6:16
-   |
-LL |     log(debug, m1::arguments);
-   |                ^^^^^^^^^^^^^ not a value
-
-error[E0580]: `main` function has wrong type
-  --> $DIR/bad-expr-path2.rs:5:1
-   |
-LL | fn main(arguments: Vec<String>) {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
-   |
-   = note: expected fn pointer `fn()`
-              found fn pointer `fn(Vec<String>)`
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0423, E0425, E0580.
-For more information about an error, try `rustc --explain E0423`.
diff --git a/src/test/ui/bad/bad-extern-link-attrs.rs b/src/test/ui/bad/bad-extern-link-attrs.rs
deleted file mode 100644 (file)
index 43fe8c1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#[link()] //~ ERROR: specified without `name =
-#[link(name = "")] //~ ERROR: with empty name
-#[link(name = "foo")]
-#[link(name = "foo", kind = "bar")] //~ ERROR: unknown kind
-extern "C" {}
-
-fn main() {}
diff --git a/src/test/ui/bad/bad-extern-link-attrs.stderr b/src/test/ui/bad/bad-extern-link-attrs.stderr
deleted file mode 100644 (file)
index 525c605..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-error[E0459]: `#[link(...)]` specified without `name = "foo"`
-  --> $DIR/bad-extern-link-attrs.rs:1:1
-   |
-LL | #[link()]
-   | ^^^^^^^^^ missing `name` argument
-
-error[E0454]: `#[link(name = "")]` given with empty name
-  --> $DIR/bad-extern-link-attrs.rs:2:1
-   |
-LL | #[link(name = "")]
-   | ^^^^^^^^^^^^^^^^^^ empty name given
-
-error[E0458]: unknown kind: `bar`
-  --> $DIR/bad-extern-link-attrs.rs:4:22
-   |
-LL | #[link(name = "foo", kind = "bar")]
-   | ---------------------^^^^^^^^^^^^--
-   |                      |
-   |                      unknown kind
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0454, E0458, E0459.
-For more information about an error, try `rustc --explain E0454`.
diff --git a/src/test/ui/bad/bad-intrinsic-monomorphization.rs b/src/test/ui/bad/bad-intrinsic-monomorphization.rs
deleted file mode 100644 (file)
index f36a5f1..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-// build-fail
-
-#![feature(repr_simd, platform_intrinsics, core_intrinsics)]
-#![allow(warnings)]
-#![crate_type = "rlib"]
-
-// Bad monomorphizations could previously cause LLVM asserts even though the
-// error was caught in the compiler.
-
-extern "platform-intrinsic" {
-    fn simd_add<T>(x: T, y: T) -> T;
-}
-
-use std::intrinsics;
-
-#[derive(Copy, Clone)]
-pub struct Foo(i64);
-
-pub fn test_cttz(v: Foo) -> Foo {
-    intrinsics::cttz(v)
-    //~^ ERROR `cttz` intrinsic: expected basic integer type, found `Foo`
-}
-
-pub unsafe fn test_fadd_fast(a: Foo, b: Foo) -> Foo {
-    intrinsics::fadd_fast(a, b)
-    //~^ ERROR `fadd_fast` intrinsic: expected basic float type, found `Foo`
-}
-
-pub unsafe fn test_simd_add(a: Foo, b: Foo) -> Foo {
-    simd_add(a, b)
-    //~^ ERROR `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
-}
diff --git a/src/test/ui/bad/bad-intrinsic-monomorphization.stderr b/src/test/ui/bad/bad-intrinsic-monomorphization.stderr
deleted file mode 100644 (file)
index c070f01..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-error[E0511]: invalid monomorphization of `cttz` intrinsic: expected basic integer type, found `Foo`
-  --> $DIR/bad-intrinsic-monomorphization.rs:20:5
-   |
-LL |     intrinsics::cttz(v)
-   |     ^^^^^^^^^^^^^^^^^^^
-
-error[E0511]: invalid monomorphization of `fadd_fast` intrinsic: expected basic float type, found `Foo`
-  --> $DIR/bad-intrinsic-monomorphization.rs:25:5
-   |
-LL |     intrinsics::fadd_fast(a, b)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0511]: invalid monomorphization of `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
-  --> $DIR/bad-intrinsic-monomorphization.rs:30:5
-   |
-LL |     simd_add(a, b)
-   |     ^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0511`.
diff --git a/src/test/ui/bad/bad-lint-cap.rs b/src/test/ui/bad/bad-lint-cap.rs
deleted file mode 100644 (file)
index e65c831..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-// compile-flags: --cap-lints test
-// error-pattern: unknown lint level: `test`
-
-fn main() {}
diff --git a/src/test/ui/bad/bad-lint-cap.stderr b/src/test/ui/bad/bad-lint-cap.stderr
deleted file mode 100644 (file)
index f284dbf..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-error: unknown lint level: `test`
-
diff --git a/src/test/ui/bad/bad-lint-cap2.rs b/src/test/ui/bad/bad-lint-cap2.rs
deleted file mode 100644 (file)
index 8bc8aca..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-// compile-flags: --cap-lints deny
-
-#![warn(unused)]
-#![deny(warnings)]
-
-use std::option; //~ ERROR
-
-fn main() {}
diff --git a/src/test/ui/bad/bad-lint-cap2.stderr b/src/test/ui/bad/bad-lint-cap2.stderr
deleted file mode 100644 (file)
index 3f3affe..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-error: unused import: `std::option`
-  --> $DIR/bad-lint-cap2.rs:6:5
-   |
-LL | use std::option;
-   |     ^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/bad-lint-cap2.rs:4:9
-   |
-LL | #![deny(warnings)]
-   |         ^^^^^^^^
-   = note: `#[deny(unused_imports)]` implied by `#[deny(warnings)]`
-
-error: aborting due to previous error
-
diff --git a/src/test/ui/bad/bad-lint-cap3.rs b/src/test/ui/bad/bad-lint-cap3.rs
deleted file mode 100644 (file)
index c381058..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-// check-pass
-// compile-flags: --cap-lints warn
-
-#![warn(unused)]
-#![deny(warnings)]
-
-use std::option; //~ WARN
-
-fn main() {}
diff --git a/src/test/ui/bad/bad-lint-cap3.stderr b/src/test/ui/bad/bad-lint-cap3.stderr
deleted file mode 100644 (file)
index 0fb6532..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-warning: unused import: `std::option`
-  --> $DIR/bad-lint-cap3.rs:7:5
-   |
-LL | use std::option;
-   |     ^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/bad-lint-cap3.rs:5:9
-   |
-LL | #![deny(warnings)]
-   |         ^^^^^^^^
-   = note: `#[warn(unused_imports)]` implied by `#[warn(warnings)]`
-
-warning: 1 warning emitted
-
diff --git a/src/test/ui/bad/bad-main.rs b/src/test/ui/bad/bad-main.rs
deleted file mode 100644 (file)
index 7511599..0000000
+++ /dev/null
@@ -1 +0,0 @@
-fn main(x: isize) { } //~ ERROR: `main` function has wrong type [E0580]
diff --git a/src/test/ui/bad/bad-main.stderr b/src/test/ui/bad/bad-main.stderr
deleted file mode 100644 (file)
index 675b66d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0580]: `main` function has wrong type
-  --> $DIR/bad-main.rs:1:1
-   |
-LL | fn main(x: isize) { }
-   | ^^^^^^^^^^^^^^^^^ incorrect number of function parameters
-   |
-   = note: expected fn pointer `fn()`
-              found fn pointer `fn(isize)`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0580`.
diff --git a/src/test/ui/bad/bad-method-typaram-kind.rs b/src/test/ui/bad/bad-method-typaram-kind.rs
deleted file mode 100644 (file)
index b088eae..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-fn foo<T:'static>() {
-    1.bar::<T>(); //~ ERROR `T` cannot be sent between threads safely
-}
-
-trait Bar {
-    fn bar<T:Send>(&self);
-}
-
-impl Bar for usize {
-    fn bar<T:Send>(&self) {
-    }
-}
-
-fn main() {}
diff --git a/src/test/ui/bad/bad-method-typaram-kind.stderr b/src/test/ui/bad/bad-method-typaram-kind.stderr
deleted file mode 100644 (file)
index fd3999a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-error[E0277]: `T` cannot be sent between threads safely
-  --> $DIR/bad-method-typaram-kind.rs:2:7
-   |
-LL |     1.bar::<T>();
-   |       ^^^ `T` cannot be sent between threads safely
-   |
-help: consider further restricting this bound
-   |
-LL | fn foo<T:'static + std::marker::Send>() {
-   |                  ^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/bad/bad-mid-path-type-params.rs b/src/test/ui/bad/bad-mid-path-type-params.rs
deleted file mode 100644 (file)
index c42ce60..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-struct S<T> {
-    contents: T,
-}
-
-impl<T> S<T> {
-    fn new<U>(x: T, _: U) -> S<T> {
-        S {
-            contents: x,
-        }
-    }
-}
-
-trait Trait<T> {
-    fn new<U>(x: T, y: U) -> Self;
-}
-
-struct S2 {
-    contents: isize,
-}
-
-impl Trait<isize> for S2 {
-    fn new<U>(x: isize, _: U) -> S2 {
-        S2 {
-            contents: x,
-        }
-    }
-}
-
-fn foo<'a>() {
-    let _ = S::new::<isize,f64>(1, 1.0);
-    //~^ ERROR this associated function takes 1 type argument but 2 type arguments were supplied
-
-    let _ = S::<'a,isize>::new::<f64>(1, 1.0);
-    //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
-
-    let _: S2 = Trait::new::<isize,f64>(1, 1.0);
-    //~^ ERROR this associated function takes 1 type argument but 2 type arguments were supplied
-
-    let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
-    //~^ ERROR this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
-    //~| ERROR this associated function takes 1 type argument but 2 type arguments were supplied
-}
-
-fn main() {}
diff --git a/src/test/ui/bad/bad-mid-path-type-params.stderr b/src/test/ui/bad/bad-mid-path-type-params.stderr
deleted file mode 100644 (file)
index dd96856..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
-  --> $DIR/bad-mid-path-type-params.rs:30:16
-   |
-LL |     let _ = S::new::<isize,f64>(1, 1.0);
-   |                ^^^        ---- help: remove this type argument
-   |                |
-   |                expected 1 type argument
-   |
-note: associated function defined here, with 1 type parameter: `U`
-  --> $DIR/bad-mid-path-type-params.rs:6:8
-   |
-LL |     fn new<U>(x: T, _: U) -> S<T> {
-   |        ^^^ -
-
-error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
-  --> $DIR/bad-mid-path-type-params.rs:33:13
-   |
-LL |     let _ = S::<'a,isize>::new::<f64>(1, 1.0);
-   |             ^   --- help: remove this lifetime argument
-   |             |
-   |             expected 0 lifetime arguments
-   |
-note: struct defined here, with 0 lifetime parameters
-  --> $DIR/bad-mid-path-type-params.rs:1:8
-   |
-LL | struct S<T> {
-   |        ^
-
-error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
-  --> $DIR/bad-mid-path-type-params.rs:36:24
-   |
-LL |     let _: S2 = Trait::new::<isize,f64>(1, 1.0);
-   |                        ^^^        ---- help: remove this type argument
-   |                        |
-   |                        expected 1 type argument
-   |
-note: associated function defined here, with 1 type parameter: `U`
-  --> $DIR/bad-mid-path-type-params.rs:14:8
-   |
-LL |     fn new<U>(x: T, y: U) -> Self;
-   |        ^^^ -
-
-error[E0107]: this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
-  --> $DIR/bad-mid-path-type-params.rs:39:17
-   |
-LL |     let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
-   |                 ^^^^^   --- help: remove this lifetime argument
-   |                 |
-   |                 expected 0 lifetime arguments
-   |
-note: trait defined here, with 0 lifetime parameters
-  --> $DIR/bad-mid-path-type-params.rs:13:7
-   |
-LL | trait Trait<T> {
-   |       ^^^^^
-
-error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
-  --> $DIR/bad-mid-path-type-params.rs:39:36
-   |
-LL |     let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
-   |                                    ^^^      ---- help: remove this type argument
-   |                                    |
-   |                                    expected 1 type argument
-   |
-note: associated function defined here, with 1 type parameter: `U`
-  --> $DIR/bad-mid-path-type-params.rs:14:8
-   |
-LL |     fn new<U>(x: T, y: U) -> Self;
-   |        ^^^ -
-
-error: aborting due to 5 previous errors
-
-For more information about this error, try `rustc --explain E0107`.
diff --git a/src/test/ui/bad/bad-module.rs b/src/test/ui/bad/bad-module.rs
deleted file mode 100644 (file)
index b23e97c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-fn main() {
-    let foo = thing::len(Vec::new());
-    //~^ ERROR failed to resolve: use of undeclared crate or module `thing`
-
-    let foo = foo::bar::baz();
-    //~^ ERROR failed to resolve: use of undeclared crate or module `foo`
-}
diff --git a/src/test/ui/bad/bad-module.stderr b/src/test/ui/bad/bad-module.stderr
deleted file mode 100644 (file)
index 581a661..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-error[E0433]: failed to resolve: use of undeclared crate or module `thing`
-  --> $DIR/bad-module.rs:2:15
-   |
-LL |     let foo = thing::len(Vec::new());
-   |               ^^^^^ use of undeclared crate or module `thing`
-
-error[E0433]: failed to resolve: use of undeclared crate or module `foo`
-  --> $DIR/bad-module.rs:5:15
-   |
-LL |     let foo = foo::bar::baz();
-   |               ^^^ use of undeclared crate or module `foo`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0433`.
diff --git a/src/test/ui/bad/bad-sized.rs b/src/test/ui/bad/bad-sized.rs
deleted file mode 100644 (file)
index a152196..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-trait Trait {}
-
-pub fn main() {
-    let x: Vec<dyn Trait + Sized> = Vec::new();
-    //~^ ERROR only auto traits can be used as additional traits in a trait object
-    //~| ERROR the size for values of type
-    //~| ERROR the size for values of type
-    //~| ERROR the size for values of type
-}
diff --git a/src/test/ui/bad/bad-sized.stderr b/src/test/ui/bad/bad-sized.stderr
deleted file mode 100644 (file)
index 768893d..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-error[E0225]: only auto traits can be used as additional traits in a trait object
-  --> $DIR/bad-sized.rs:4:28
-   |
-LL |     let x: Vec<dyn Trait + Sized> = Vec::new();
-   |                    -----   ^^^^^ additional non-auto trait
-   |                    |
-   |                    first non-auto trait
-   |
-   = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Trait + Sized {}`
-   = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
-
-error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
-  --> $DIR/bad-sized.rs:4:12
-   |
-LL |     let x: Vec<dyn Trait + Sized> = Vec::new();
-   |            ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
-   | 
-  ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-   |
-LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
-   |                - required by this bound in `Vec`
-   |
-   = help: the trait `Sized` is not implemented for `dyn Trait`
-
-error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
-  --> $DIR/bad-sized.rs:4:37
-   |
-LL |     let x: Vec<dyn Trait + Sized> = Vec::new();
-   |                                     ^^^^^^^^ doesn't have a size known at compile-time
-   |
-   = help: the trait `Sized` is not implemented for `dyn Trait`
-   = note: required by `Vec::<T>::new`
-
-error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
-  --> $DIR/bad-sized.rs:4:37
-   |
-LL |     let x: Vec<dyn Trait + Sized> = Vec::new();
-   |                                     ^^^ doesn't have a size known at compile-time
-   | 
-  ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-   |
-LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
-   |                - required by this bound in `Vec`
-   |
-   = help: the trait `Sized` is not implemented for `dyn Trait`
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0225, E0277.
-For more information about an error, try `rustc --explain E0225`.
diff --git a/src/test/ui/bad/bad-type-env-capture.rs b/src/test/ui/bad/bad-type-env-capture.rs
deleted file mode 100644 (file)
index 53dfb13..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-fn foo<T>() {
-    fn bar(b: T) { } //~ ERROR can't use generic parameters from outer
-}
-fn main() { }
diff --git a/src/test/ui/bad/bad-type-env-capture.stderr b/src/test/ui/bad/bad-type-env-capture.stderr
deleted file mode 100644 (file)
index 6f24c0d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-error[E0401]: can't use generic parameters from outer function
-  --> $DIR/bad-type-env-capture.rs:2:15
-   |
-LL | fn foo<T>() {
-   |        - type parameter from outer function
-LL |     fn bar(b: T) { }
-   |        ---    ^ use of generic parameter from outer function
-   |        |
-   |        help: try using a local generic parameter instead: `bar<T>`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0401`.
index 3dccca784151e6b0ce2b2ee77b12c2c3825af03c..74cf5a55b70ab214e31672310555b0274cea22da 100644 (file)
@@ -29,7 +29,7 @@ LL |         let x = 3;
    |             -
    |             |
    |             first assignment to `x`
-   |             help: make this binding mutable: `mut x`
+   |             help: consider making this binding mutable: `mut x`
 LL |         unsafe {
 LL |             llvm_asm!("nop" : "=r"(x));
    |                                    ^ cannot assign twice to immutable variable
@@ -41,7 +41,7 @@ LL |         let x = 3;
    |             -
    |             |
    |             first assignment to `x`
-   |             help: make this binding mutable: `mut x`
+   |             help: consider making this binding mutable: `mut x`
 LL |         unsafe {
 LL |             llvm_asm!("nop" : "+r"(x));
    |                                    ^ cannot assign twice to immutable variable
index 5661ca52cbabc973520604d40fbf1eda0ec27c4a..dd22d7e2e2ed2986cfa1d023a750d91abae1936d 100644 (file)
@@ -5,7 +5,7 @@ LL |         x => {
    |         -
    |         |
    |         first assignment to `x`
-   |         help: make this binding mutable: `mut x`
+   |         help: consider making this binding mutable: `mut x`
 LL |             x += 1;
    |             ^^^^^^ cannot assign twice to immutable variable
 
@@ -16,7 +16,7 @@ LL |         E::Foo(x) => {
    |                -
    |                |
    |                first assignment to `x`
-   |                help: make this binding mutable: `mut x`
+   |                help: consider making this binding mutable: `mut x`
 LL |             x += 1;
    |             ^^^^^^ cannot assign twice to immutable variable
 
@@ -27,7 +27,7 @@ LL |         S { bar: x } => {
    |                  -
    |                  |
    |                  first assignment to `x`
-   |                  help: make this binding mutable: `mut x`
+   |                  help: consider making this binding mutable: `mut x`
 LL |             x += 1;
    |             ^^^^^^ cannot assign twice to immutable variable
 
@@ -38,7 +38,7 @@ LL |         (x,) => {
    |          -
    |          |
    |          first assignment to `x`
-   |          help: make this binding mutable: `mut x`
+   |          help: consider making this binding mutable: `mut x`
 LL |             x += 1;
    |             ^^^^^^ cannot assign twice to immutable variable
 
@@ -49,7 +49,7 @@ LL |         [x,_,_] => {
    |          -
    |          |
    |          first assignment to `x`
-   |          help: make this binding mutable: `mut x`
+   |          help: consider making this binding mutable: `mut x`
 LL |             x += 1;
    |             ^^^^^^ cannot assign twice to immutable variable
 
index 7255ca327e753328a56579e421fc06fe8f9cd0cb..bddb0633a0b86ea716b8e244b9d11b0fc46b86f4 100644 (file)
@@ -2,7 +2,7 @@ error[E0384]: cannot assign to immutable argument `_x`
   --> $DIR/immutable-arg.rs:2:5
    |
 LL | fn foo(_x: u32) {
-   |        -- help: make this binding mutable: `mut _x`
+   |        -- help: consider making this binding mutable: `mut _x`
 LL |     _x = 4;
    |     ^^^^^^ cannot assign to immutable argument
 
index cbd45cbb6199081dbbe6e329bfbc5a3bb8092cc4..ded46e56e34514c8d9010dff9e0c203370091ab1 100644 (file)
@@ -1,6 +1,6 @@
 fn test_drop_replace() {
     let b: Box<isize>;
-    //~^ HELP make this binding mutable
+    //~^ HELP consider making this binding mutable
     //~| SUGGESTION mut b
     b = Box::new(1);    //~ NOTE first assignment
     b = Box::new(2);    //~ ERROR cannot assign twice to immutable variable `b`
@@ -9,13 +9,13 @@ fn test_drop_replace() {
 
 fn test_call() {
     let b = Box::new(1);    //~ NOTE first assignment
-                            //~| HELP make this binding mutable
+                            //~| HELP consider making this binding mutable
                             //~| SUGGESTION mut b
     b = Box::new(2);        //~ ERROR cannot assign twice to immutable variable `b`
                             //~| NOTE cannot assign twice to immutable
 }
 
-fn test_args(b: Box<i32>) {  //~ HELP make this binding mutable
+fn test_args(b: Box<i32>) {  //~ HELP consider making this binding mutable
                                 //~| SUGGESTION mut b
     b = Box::new(2);            //~ ERROR cannot assign to immutable argument `b`
                                 //~| NOTE cannot assign to immutable argument
index 83b634051bb00a9778b0603e8b1f366f38d31019..47aa30908270d91b7ac9107dcf7318ea259b5e6a 100644 (file)
@@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `b`
   --> $DIR/issue-45199.rs:6:5
    |
 LL |     let b: Box<isize>;
-   |         - help: make this binding mutable: `mut b`
+   |         - help: consider making this binding mutable: `mut b`
 ...
 LL |     b = Box::new(1);
    |     - first assignment to `b`
@@ -16,7 +16,7 @@ LL |     let b = Box::new(1);
    |         -
    |         |
    |         first assignment to `b`
-   |         help: make this binding mutable: `mut b`
+   |         help: consider making this binding mutable: `mut b`
 ...
 LL |     b = Box::new(2);
    |     ^ cannot assign twice to immutable variable
@@ -25,7 +25,7 @@ error[E0384]: cannot assign to immutable argument `b`
   --> $DIR/issue-45199.rs:20:5
    |
 LL | fn test_args(b: Box<i32>) {
-   |              - help: make this binding mutable: `mut b`
+   |              - help: consider making this binding mutable: `mut b`
 LL |
 LL |     b = Box::new(2);
    |     ^ cannot assign to immutable argument
diff --git a/src/test/ui/borrowck/issue-83309-ice-immut-in-for-loop.rs b/src/test/ui/borrowck/issue-83309-ice-immut-in-for-loop.rs
new file mode 100644 (file)
index 0000000..6da88be
--- /dev/null
@@ -0,0 +1,32 @@
+// rust-lang/rust#83309: The compiler tries to suggest potential
+// methods that return `&mut` items. However, when it doesn't
+// find such methods, it still tries to add suggestions
+// which then fails an assertion later because there was
+// no suggestions to make.
+
+
+fn main() {
+    for v in Query.iter_mut() {
+        //~^ NOTE this iterator yields `&` references
+        *v -= 1;
+        //~^ ERROR cannot assign to `*v` which is behind a `&` reference
+        //~| NOTE `v` is a `&` reference, so the data it refers to cannot be written
+    }
+}
+
+pub struct Query;
+pub struct QueryIter<'a>(&'a i32);
+
+impl Query {
+    pub fn iter_mut<'a>(&'a mut self) -> QueryIter<'a> {
+        todo!();
+    }
+}
+
+impl<'a> Iterator for QueryIter<'a> {
+    type Item = &'a i32;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        todo!();
+    }
+}
diff --git a/src/test/ui/borrowck/issue-83309-ice-immut-in-for-loop.stderr b/src/test/ui/borrowck/issue-83309-ice-immut-in-for-loop.stderr
new file mode 100644 (file)
index 0000000..143b74c
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0594]: cannot assign to `*v` which is behind a `&` reference
+  --> $DIR/issue-83309-ice-immut-in-for-loop.rs:11:9
+   |
+LL |     for v in Query.iter_mut() {
+   |              ---------------- this iterator yields `&` references
+LL |
+LL |         *v -= 1;
+   |         ^^^^^^^ `v` is a `&` reference, so the data it refers to cannot be written
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0594`.
index ac153cb57423fd8f731cd36ac265187d78f91175..aed099a53eaf4b2f3295bf9bb75bcaf800767697 100644 (file)
@@ -1,11 +1,25 @@
 // gate-test-const_raw_ptr_to_usize_cast
+// revisions: with_feature without_feature
+
+#![cfg_attr(with_feature, feature(const_raw_ptr_to_usize_cast))]
 
 fn main() {
-    const X: u32 = unsafe {
-        main as u32 //~ ERROR casting pointers to integers in constants is unstable
+    const X: usize = unsafe {
+        main as usize //[without_feature]~ ERROR casting pointers to integers in constants is unstable
     };
     const Y: u32 = 0;
-    const Z: u32 = unsafe {
-        &Y as *const u32 as u32 //~ ERROR is unstable
+    const Z: usize = unsafe {
+        &Y as *const u32 as usize //[without_feature]~ ERROR is unstable
+    };
+    // Cast in `const` without `unsafe` block
+    const SAFE: usize = {
+        &Y as *const u32 as usize //[without_feature]~ ERROR is unstable
+        //[with_feature]~^ ERROR cast of pointer to int is unsafe and requires unsafe
     };
 }
+
+// Cast in `const fn` without `unsafe` block
+const fn test() -> usize {
+    &0 as *const i32 as usize //[without_feature]~ ERROR is unstable
+    //[with_feature]~^ ERROR cast of pointer to int is unsafe and requires unsafe
+}
diff --git a/src/test/ui/cast/cast-ptr-to-int-const.stderr b/src/test/ui/cast/cast-ptr-to-int-const.stderr
deleted file mode 100644 (file)
index f523b14..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-error[E0658]: casting pointers to integers in constants is unstable
-  --> $DIR/cast-ptr-to-int-const.rs:5:9
-   |
-LL |         main as u32
-   |         ^^^^^^^^^^^
-   |
-   = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
-   = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
-
-error[E0658]: casting pointers to integers in constants is unstable
-  --> $DIR/cast-ptr-to-int-const.rs:9:9
-   |
-LL |         &Y as *const u32 as u32
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
-   = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/cast/cast-ptr-to-int-const.with_feature.stderr b/src/test/ui/cast/cast-ptr-to-int-const.with_feature.stderr
new file mode 100644 (file)
index 0000000..8282bc3
--- /dev/null
@@ -0,0 +1,19 @@
+error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block
+  --> $DIR/cast-ptr-to-int-const.rs:16:9
+   |
+LL |         &Y as *const u32 as usize
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int
+   |
+   = note: casting pointers to integers in constants
+
+error[E0133]: cast of pointer to int is unsafe and requires unsafe function or block
+  --> $DIR/cast-ptr-to-int-const.rs:23:5
+   |
+LL |     &0 as *const i32 as usize
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ cast of pointer to int
+   |
+   = note: casting pointers to integers in constants
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/cast/cast-ptr-to-int-const.without_feature.stderr b/src/test/ui/cast/cast-ptr-to-int-const.without_feature.stderr
new file mode 100644 (file)
index 0000000..c87fa1a
--- /dev/null
@@ -0,0 +1,39 @@
+error[E0658]: casting pointers to integers in constants is unstable
+  --> $DIR/cast-ptr-to-int-const.rs:8:9
+   |
+LL |         main as usize
+   |         ^^^^^^^^^^^^^
+   |
+   = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
+   = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
+
+error[E0658]: casting pointers to integers in constants is unstable
+  --> $DIR/cast-ptr-to-int-const.rs:12:9
+   |
+LL |         &Y as *const u32 as usize
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
+   = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
+
+error[E0658]: casting pointers to integers in constants is unstable
+  --> $DIR/cast-ptr-to-int-const.rs:16:9
+   |
+LL |         &Y as *const u32 as usize
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
+   = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
+
+error[E0658]: casting pointers to integers in constant functions is unstable
+  --> $DIR/cast-ptr-to-int-const.rs:23:5
+   |
+LL |     &0 as *const i32 as usize
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #51910 <https://github.com/rust-lang/rust/issues/51910> for more information
+   = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
index 5f5e621d762dfc67c703a04d75de1a4ece607f82..9fa2255e1b34824455fb24a7ecf9c75b52926c5c 100644 (file)
@@ -1,5 +1,8 @@
 // run-pass
 
+// Remove this file when `std::raw` is removed.
+// The replacement pointer metadata APIs are tested in library/core/tests/ptr.rs
+#![allow(deprecated)]
 #![feature(raw)]
 
 use std::mem;
@@ -37,5 +40,4 @@ fn main() {
 
     assert_eq!(b, d);
     assert_eq!(c, d as usize);
-
 }
diff --git a/src/test/ui/cast/issue-84213.fixed b/src/test/ui/cast/issue-84213.fixed
new file mode 100644 (file)
index 0000000..e1a6055
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+
+struct Something {
+    pub field: u32,
+}
+
+fn main() {
+    let mut something = Something { field: 1337 };
+
+    let _pointer_to_something = &something as *const Something;
+    //~^ ERROR: non-primitive cast
+
+    let _mut_pointer_to_something = &mut something as *mut Something;
+    //~^ ERROR: non-primitive cast
+}
diff --git a/src/test/ui/cast/issue-84213.rs b/src/test/ui/cast/issue-84213.rs
new file mode 100644 (file)
index 0000000..3df264b
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+
+struct Something {
+    pub field: u32,
+}
+
+fn main() {
+    let mut something = Something { field: 1337 };
+
+    let _pointer_to_something = something as *const Something;
+    //~^ ERROR: non-primitive cast
+
+    let _mut_pointer_to_something = something as *mut Something;
+    //~^ ERROR: non-primitive cast
+}
diff --git a/src/test/ui/cast/issue-84213.stderr b/src/test/ui/cast/issue-84213.stderr
new file mode 100644 (file)
index 0000000..a76aac5
--- /dev/null
@@ -0,0 +1,25 @@
+error[E0605]: non-primitive cast: `Something` as `*const Something`
+  --> $DIR/issue-84213.rs:10:33
+   |
+LL |     let _pointer_to_something = something as *const Something;
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: borrow the value for the cast to be valid
+   |
+LL |     let _pointer_to_something = &something as *const Something;
+   |                                 ^
+
+error[E0605]: non-primitive cast: `Something` as `*mut Something`
+  --> $DIR/issue-84213.rs:13:37
+   |
+LL |     let _mut_pointer_to_something = something as *mut Something;
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: borrow the value for the cast to be valid
+   |
+LL |     let _mut_pointer_to_something = &mut something as *mut Something;
+   |                                     ^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0605`.
diff --git a/src/test/ui/closures/issue-84128.rs b/src/test/ui/closures/issue-84128.rs
new file mode 100644 (file)
index 0000000..f81d7cf
--- /dev/null
@@ -0,0 +1,16 @@
+// test for issue 84128
+// missing suggestion for similar ADT type with diffetent generic paramenter
+// on closure ReturnNoExpression
+
+struct Foo<T>(T);
+
+fn main() {
+    || {
+        if false {
+            return Foo(0);
+        }
+
+        Foo(())
+        //~^ ERROR mismatched types [E0308]
+    };
+}
diff --git a/src/test/ui/closures/issue-84128.stderr b/src/test/ui/closures/issue-84128.stderr
new file mode 100644 (file)
index 0000000..70d9273
--- /dev/null
@@ -0,0 +1,15 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-84128.rs:13:13
+   |
+LL |         Foo(())
+   |             ^^ expected integer, found `()`
+   |
+note: return type inferred to be `{integer}` here
+  --> $DIR/issue-84128.rs:10:20
+   |
+LL |             return Foo(0);
+   |                    ^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
index de2ffc2e5dc1d0e1e704c139262e638096c120d3..aff51ee9e2f541514975efbd1175be5e2b24b67a 100644 (file)
@@ -5,10 +5,9 @@ LL | impl Bar for Baz { }
    |      ^^^ type aliases cannot be used as traits
    |
 help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
-  --> $DIR/two_files_data.rs:5:1
    |
-LL | type Bar = dyn Foo;
-   | ^^^^^^^^^^^^^^^^^^^
+LL | trait Bar = dyn Foo;
+   |
 
 error: aborting due to previous error
 
index fa69115b2daaf477ae1b10eb19548bb28b5b8743..3301e7a18d11ab1e2ab597829ffa221daa05e109 100644 (file)
@@ -1,5 +1,3 @@
-#![feature(non_ascii_idents)]
-
 fn main() {
     let _ = ("a̐éö̲", 0u7); //~ ERROR invalid width
     let _ = ("아あ", 1i42); //~ ERROR invalid width
index c01942712d4f382dc7d6bae1bf3f9cb0d55d9be8..a776a4a1e7e1392d4ab08e69e6946876ca7d8120 100644 (file)
@@ -1,5 +1,5 @@
 error: invalid width `7` for integer literal
-  --> $DIR/unicode_2.rs:4:25
+  --> $DIR/unicode_2.rs:2:25
    |
 LL |     let _ = ("a̐éö̲", 0u7);
    |                     ^^^
@@ -7,7 +7,7 @@ LL |     let _ = ("a̐éö̲", 0u7);
    = help: valid widths are 8, 16, 32, 64 and 128
 
 error: invalid width `42` for integer literal
-  --> $DIR/unicode_2.rs:5:20
+  --> $DIR/unicode_2.rs:3:20
    |
 LL |     let _ = ("아あ", 1i42);
    |                      ^^^^
@@ -15,7 +15,7 @@ LL |     let _ = ("아あ", 1i42);
    = help: valid widths are 8, 16, 32, 64 and 128
 
 error[E0425]: cannot find value `a̐é` in this scope
-  --> $DIR/unicode_2.rs:6:13
+  --> $DIR/unicode_2.rs:4:13
    |
 LL |     let _ = a̐é;
    |             ^^ not found in this scope
index b3f8d8a643fb5b7e32d57d856aa63ae433192a2c..6223ad880d695661d50d4338b2dccb7751a47960 100644 (file)
@@ -5,7 +5,7 @@ LL |     let x = 42;
    |         -
    |         |
    |         first assignment to `x`
-   |         help: make this binding mutable: `mut x`
+   |         help: consider making this binding mutable: `mut x`
 LL |     x = 43;
    |     ^^^^^^ cannot assign twice to immutable variable
 
index 769b6e952dc9caa04fda579957c5b43b4a1d5a96..6514409698e3e6fda6c46dfd28083691563207f6 100644 (file)
@@ -1,4 +1,4 @@
-#![feature(const_generics)]
+#![cfg_attr(full, feature(const_generics))]
 #![feature(const_generics_defaults)]
 #![allow(incomplete_features)]
 
diff --git a/src/test/ui/const-generics/defaults/complex-generic-default-expr.full.stderr b/src/test/ui/const-generics/defaults/complex-generic-default-expr.full.stderr
new file mode 100644 (file)
index 0000000..e0e2b6c
--- /dev/null
@@ -0,0 +1,18 @@
+error: constant expression depends on a generic parameter
+  --> $DIR/complex-generic-default-expr.rs:6:34
+   |
+LL | struct Foo<const N: usize, const M: usize = { N + 1 }>;
+   |                                  ^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+  --> $DIR/complex-generic-default-expr.rs:10:21
+   |
+LL | struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
+   |                     ^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/const-generics/defaults/complex-generic-default-expr.min.stderr b/src/test/ui/const-generics/defaults/complex-generic-default-expr.min.stderr
new file mode 100644 (file)
index 0000000..58abd8d
--- /dev/null
@@ -0,0 +1,20 @@
+error: generic parameters may not be used in const operations
+  --> $DIR/complex-generic-default-expr.rs:6:47
+   |
+LL | struct Foo<const N: usize, const M: usize = { N + 1 }>;
+   |                                               ^ cannot perform const operation using `N`
+   |
+   = help: const parameters may only be used as standalone arguments, i.e. `N`
+   = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
+
+error: generic parameters may not be used in const operations
+  --> $DIR/complex-generic-default-expr.rs:10:62
+   |
+LL | struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
+   |                                                              ^ cannot perform const operation using `T`
+   |
+   = note: type parameters may not be used in const expressions
+   = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/const-generics/defaults/complex-generic-default-expr.rs b/src/test/ui/const-generics/defaults/complex-generic-default-expr.rs
new file mode 100644 (file)
index 0000000..a7b712f
--- /dev/null
@@ -0,0 +1,14 @@
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+
+struct Foo<const N: usize, const M: usize = { N + 1 }>;
+//[full]~^ ERROR constant expression depends on a generic parameter
+//[min]~^^ ERROR generic parameters may not be used in const operations
+
+struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
+//[full]~^ ERROR constant expression depends on a generic parameter
+//[min]~^^ ERROR generic parameters may not be used in const operations
+
+fn main() {}
index 150c70770ae51e69d1627326c5e898707aa7c911..4fa21b8b1fb787b557a7bd52128425a6a8f3a6ba 100644 (file)
@@ -1,6 +1,6 @@
 // run-pass
-
-#![feature(const_generics)]
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
 #![feature(const_generics_defaults)]
 #![allow(incomplete_features)]
 
diff --git a/src/test/ui/const-generics/defaults/const-param-as-default-value.rs b/src/test/ui/const-generics/defaults/const-param-as-default-value.rs
new file mode 100644 (file)
index 0000000..59ac261
--- /dev/null
@@ -0,0 +1,23 @@
+// run-pass
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+struct Foo<const N: usize, const M: usize = N>([u8; N], [u8; M]);
+
+fn foo<const N: usize>() -> Foo<N> {
+    let x = [0; N];
+    Foo(x, x)
+}
+
+// To check that we actually apply the correct substs for const param defaults.
+fn concrete_foo() -> Foo<13> {
+    Foo(Default::default(), Default::default())
+}
+
+
+fn main() {
+    let val = foo::<13>();
+    assert_eq!(val.0, val.1);
+
+    let val = concrete_foo();
+    assert_eq!(val.0, val.1);
+}
diff --git a/src/test/ui/const-generics/defaults/const-param-in-ty-defaults.rs b/src/test/ui/const-generics/defaults/const-param-in-ty-defaults.rs
new file mode 100644 (file)
index 0000000..3f534ca
--- /dev/null
@@ -0,0 +1,14 @@
+// run-pass
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+struct Foo<const N: usize, T = [u8; N]>(T);
+
+impl<const N: usize> Foo<N> {
+    fn new() -> Self {
+        Foo([0; N])
+    }
+}
+
+fn main() {
+    assert_eq!(Foo::new().0, [0; 10]);
+}
diff --git a/src/test/ui/const-generics/defaults/default-on-impl.full.stderr b/src/test/ui/const-generics/defaults/default-on-impl.full.stderr
new file mode 100644 (file)
index 0000000..c417a26
--- /dev/null
@@ -0,0 +1,8 @@
+error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/default-on-impl.rs:8:12
+   |
+LL | impl<const N: usize = 1> Foo<N> {}
+   |            ^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/const-generics/defaults/default-on-impl.min.stderr b/src/test/ui/const-generics/defaults/default-on-impl.min.stderr
new file mode 100644 (file)
index 0000000..c417a26
--- /dev/null
@@ -0,0 +1,8 @@
+error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/default-on-impl.rs:8:12
+   |
+LL | impl<const N: usize = 1> Foo<N> {}
+   |            ^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/const-generics/defaults/default-on-impl.rs b/src/test/ui/const-generics/defaults/default-on-impl.rs
new file mode 100644 (file)
index 0000000..735549d
--- /dev/null
@@ -0,0 +1,11 @@
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+
+struct Foo<const N: usize>;
+
+impl<const N: usize = 1> Foo<N> {}
+//~^ ERROR defaults for const parameters are only allowed
+
+fn main() {}
diff --git a/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs b/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs
new file mode 100644 (file)
index 0000000..4bb56c6
--- /dev/null
@@ -0,0 +1,5 @@
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+struct Foo<const N: u8 = { 255 + 1 }>;
+//~^ ERROR evaluation of constant value failed
+fn main() {}
diff --git a/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr b/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr
new file mode 100644 (file)
index 0000000..8464ea9
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0080]: evaluation of constant value failed
+  --> $DIR/default-param-wf-concrete.rs:3:28
+   |
+LL | struct Foo<const N: u8 = { 255 + 1 }>;
+   |                            ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
index b39e69ab10b668c93e539d609cc6d1831c4ded16..32acf567cf2b98d19012d742adf726381737629e 100644 (file)
@@ -1,5 +1,7 @@
 // aux-build:const_defaulty.rs
 // check-pass
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
 #![feature(const_generics_defaults)]
 #![allow(incomplete_features)]
 
index c4a666a829d8cb0132ff626f9aaaf54edbe02d66..29d835e36c6eb74d9830d0cb2bcc25494235b0a3 100644 (file)
@@ -1,5 +1,5 @@
 error: lifetime parameters must be declared prior to const parameters
-  --> $DIR/intermixed-lifetime.rs:6:28
+  --> $DIR/intermixed-lifetime.rs:7:28
    |
 LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
    |           -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T = u32>`
index 69a490978d1df0c004dd5d4393d4f761457cb6dd..985e7b655ece9482038d40a1b564aee406129f59 100644 (file)
@@ -1,26 +1,14 @@
 error: lifetime parameters must be declared prior to const parameters
-  --> $DIR/intermixed-lifetime.rs:6:28
+  --> $DIR/intermixed-lifetime.rs:7:28
    |
 LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
-   |           -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
+   |           -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const N: usize, T = u32>`
 
-error: type parameters must be declared prior to const parameters
-  --> $DIR/intermixed-lifetime.rs:6:32
-   |
-LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
-   |           ---------------------^------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
-
-error: lifetime parameters must be declared prior to const parameters
+error: lifetime parameters must be declared prior to type parameters
   --> $DIR/intermixed-lifetime.rs:10:37
    |
 LL | struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
-   |           --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
-
-error: type parameters must be declared prior to const parameters
-  --> $DIR/intermixed-lifetime.rs:10:28
-   |
-LL | struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
-   |           -----------------^----------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
+   |           --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const N: usize, T = u32>`
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
index 9e83bf92a59b93b68e66b170e11965eddd7ebd37..307e3aaf1fbf38f532b155af97bdcf47b15c4942 100644 (file)
@@ -1,15 +1,13 @@
-// revisions: full min
 // Checks that lifetimes cannot be interspersed between consts and types.
+// revisions: full min
 #![cfg_attr(full, feature(const_generics))]
-#![cfg_attr(full, allow(incomplete_features))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
 
 struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
 //~^ Error lifetime parameters must be declared prior to const parameters
-//[min]~^^ Error type parameters must be declared prior to const parameters
 
 struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
-//[full]~^ Error lifetime parameters must be declared prior to type parameters
-//[min]~^^ Error type parameters must be declared prior to const parameters
-//[min]~| Error lifetime parameters must be declared prior to const parameters
+//~^ Error lifetime parameters must be declared prior to type parameters
 
 fn main() {}
diff --git a/src/test/ui/const-generics/defaults/mismatch.full.stderr b/src/test/ui/const-generics/defaults/mismatch.full.stderr
new file mode 100644 (file)
index 0000000..be4f364
--- /dev/null
@@ -0,0 +1,52 @@
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:12:28
+   |
+LL |     let e: Example::<13> = ();
+   |            -------------   ^^ expected struct `Example`, found `()`
+   |            |
+   |            expected due to this
+
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:14:34
+   |
+LL |     let e: Example2::<u32, 13> = ();
+   |            -------------------   ^^ expected struct `Example2`, found `()`
+   |            |
+   |            expected due to this
+   |
+   = note: expected struct `Example2`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:16:34
+   |
+LL |     let e: Example3::<13, u32> = ();
+   |            -------------------   ^^ expected struct `Example3`, found `()`
+   |            |
+   |            expected due to this
+   |
+   = note: expected struct `Example3`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:18:28
+   |
+LL |     let e: Example3::<7> = ();
+   |            -------------   ^^ expected struct `Example3`, found `()`
+   |            |
+   |            expected due to this
+   |
+   = note: expected struct `Example3<7_usize>`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:22:28
+   |
+LL |     let e: Example4::<7> = ();
+   |            -------------   ^^ expected struct `Example4`, found `()`
+   |            |
+   |            expected due to this
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/const-generics/defaults/mismatch.min.stderr b/src/test/ui/const-generics/defaults/mismatch.min.stderr
new file mode 100644 (file)
index 0000000..be4f364
--- /dev/null
@@ -0,0 +1,52 @@
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:12:28
+   |
+LL |     let e: Example::<13> = ();
+   |            -------------   ^^ expected struct `Example`, found `()`
+   |            |
+   |            expected due to this
+
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:14:34
+   |
+LL |     let e: Example2::<u32, 13> = ();
+   |            -------------------   ^^ expected struct `Example2`, found `()`
+   |            |
+   |            expected due to this
+   |
+   = note: expected struct `Example2`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:16:34
+   |
+LL |     let e: Example3::<13, u32> = ();
+   |            -------------------   ^^ expected struct `Example3`, found `()`
+   |            |
+   |            expected due to this
+   |
+   = note: expected struct `Example3`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:18:28
+   |
+LL |     let e: Example3::<7> = ();
+   |            -------------   ^^ expected struct `Example3`, found `()`
+   |            |
+   |            expected due to this
+   |
+   = note: expected struct `Example3<7_usize>`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/mismatch.rs:22:28
+   |
+LL |     let e: Example4::<7> = ();
+   |            -------------   ^^ expected struct `Example4`, found `()`
+   |            |
+   |            expected due to this
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
index d85b756f538dcaca7d7632abb4c6e24bb0b2fb2b..68a640c0a08b31bb6fd8cdf3c5427cc42b017f26 100644 (file)
@@ -1,4 +1,5 @@
-#![feature(const_generics)]
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
 #![feature(const_generics_defaults)]
 #![allow(incomplete_features)]
 
diff --git a/src/test/ui/const-generics/defaults/mismatch.stderr b/src/test/ui/const-generics/defaults/mismatch.stderr
deleted file mode 100644 (file)
index ff72c71..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/mismatch.rs:11:28
-   |
-LL |     let e: Example::<13> = ();
-   |            -------------   ^^ expected struct `Example`, found `()`
-   |            |
-   |            expected due to this
-
-error[E0308]: mismatched types
-  --> $DIR/mismatch.rs:13:34
-   |
-LL |     let e: Example2::<u32, 13> = ();
-   |            -------------------   ^^ expected struct `Example2`, found `()`
-   |            |
-   |            expected due to this
-   |
-   = note: expected struct `Example2`
-           found unit type `()`
-
-error[E0308]: mismatched types
-  --> $DIR/mismatch.rs:15:34
-   |
-LL |     let e: Example3::<13, u32> = ();
-   |            -------------------   ^^ expected struct `Example3`, found `()`
-   |            |
-   |            expected due to this
-   |
-   = note: expected struct `Example3`
-           found unit type `()`
-
-error[E0308]: mismatched types
-  --> $DIR/mismatch.rs:17:28
-   |
-LL |     let e: Example3::<7> = ();
-   |            -------------   ^^ expected struct `Example3`, found `()`
-   |            |
-   |            expected due to this
-   |
-   = note: expected struct `Example3<7_usize>`
-           found unit type `()`
-
-error[E0308]: mismatched types
-  --> $DIR/mismatch.rs:21:28
-   |
-LL |     let e: Example4::<7> = ();
-   |            -------------   ^^ expected struct `Example4`, found `()`
-   |            |
-   |            expected due to this
-
-error: aborting due to 5 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
index a25d4baca1a978bb5895f6079f9ddc53cc78cc32..7a57950dfc924d9cb19e851e008cd79dd3d84d1e 100644 (file)
@@ -10,4 +10,4 @@ trait Foo<const KIND: bool = true> {}
 
 fn foo<const SIZE: usize = 5>() {}
 
-struct Range<const FROM: usize = 0, const LEN: usize = 0, const TO: usize = {FROM + LEN}>;
+struct Range<const FROM: usize = 0, const LEN: usize = 0, const TO: usize = FROM>;
index f7a1d2ca4b2ef6b3184a74a9c3dce7e883368190..f549993c413d4c080850f4103fe9e69738101396 100644 (file)
@@ -17,4 +17,4 @@ trait Foo<const KIND : bool = true> { }
 fn foo<const SIZE : usize = 5>() { }
 
 struct Range<const FROM : usize = 0, const LEN : usize = 0, const TO : usize =
-             { FROM + LEN }>;
+             FROM>;
index 18ecf46729977916659df74d89668a2298d2bad9..c64c2974c8f8f94fb81585ec9fd3f40093ea5db2 100644 (file)
@@ -6,7 +6,7 @@
 #![allow(incomplete_features)]
 
 #[repr(C)]
-pub struct Loaf<T: Sized, const N: usize = 1usize> {
+pub struct Loaf<T: Sized, const N: usize = 1> {
     head: [T; N],
     slice: [T],
 }
diff --git a/src/test/ui/const-generics/defaults/simple-defaults.min.stderr b/src/test/ui/const-generics/defaults/simple-defaults.min.stderr
deleted file mode 100644 (file)
index 0746c64..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-error: type parameters must be declared prior to const parameters
-  --> $DIR/simple-defaults.rs:8:40
-   |
-LL | struct FixedOutput<'a, const N: usize, T=u32> {
-   |                   ---------------------^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
-
-error: aborting due to previous error
-
index cb66c7769bb23b263444407daa9cbcd405bb60de..c003cb2c5a6ee556d9723169e95bd14acc980453 100644 (file)
@@ -1,12 +1,12 @@
-// [full] run-pass
-// revisions: min full
-// Checks some basic test cases for defaults.
+// run-pass
+// Checks that type param defaults are allowed after const params.
+// revisions: full min
 #![cfg_attr(full, feature(const_generics))]
-#![cfg_attr(full, allow(incomplete_features))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
 #![allow(dead_code)]
 
 struct FixedOutput<'a, const N: usize, T=u32> {
-    //[min]~^ ERROR type parameters must be declared prior to const parameters
     out: &'a [T; N],
 }
 
diff --git a/src/test/ui/const-generics/defaults/type-default-const-param-name.rs b/src/test/ui/const-generics/defaults/type-default-const-param-name.rs
new file mode 100644 (file)
index 0000000..e68075e
--- /dev/null
@@ -0,0 +1,19 @@
+// check-pass
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
+
+struct N;
+
+struct Foo<const N: usize = 1, T = N>(T);
+
+impl Foo {
+    fn new() -> Self {
+        Foo(N)
+    }
+}
+
+fn main() {
+    let Foo::<1, N>(N) = Foo::new();
+}
index accc73134d8997f984832b730d4b62ce98871923..eb0bcb282155691fd7081e15c62ee227e40caf23 100644 (file)
@@ -1,19 +1,8 @@
 error: generic parameters with a default must be trailing
-  --> $DIR/wrong-order.rs:4:10
+  --> $DIR/wrong-order.rs:6:10
    |
 LL | struct A<T = u32, const N: usize> {
    |          ^
-   |
-   = note: using type defaults and const parameters in the same parameter list is currently not permitted
-
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/wrong-order.rs:2:27
-   |
-LL | #![cfg_attr(full, feature(const_generics))]
-   |                           ^^^^^^^^^^^^^^
-   |
-   = note: `#[warn(incomplete_features)]` on by default
-   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
 
-error: aborting due to previous error; 1 warning emitted
+error: aborting due to previous error
 
index c8f1d471b244b3497ae28a6f3704e1745d2b8df9..eb0bcb282155691fd7081e15c62ee227e40caf23 100644 (file)
@@ -1,10 +1,8 @@
 error: generic parameters with a default must be trailing
-  --> $DIR/wrong-order.rs:4:10
+  --> $DIR/wrong-order.rs:6:10
    |
 LL | struct A<T = u32, const N: usize> {
    |          ^
-   |
-   = note: using type defaults and const parameters in the same parameter list is currently not permitted
 
 error: aborting due to previous error
 
index 5c2d9b8ad475104d5e488a331f4f05b1b8ecc49b..88e9e96ba43f961ef071a49a0f5ff147d62bd1ae 100644 (file)
@@ -1,5 +1,7 @@
 // revisions: full min
-#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(full, feature(const_generics))]
+#![feature(const_generics_defaults)]
+#![allow(incomplete_features)]
 
 struct A<T = u32, const N: usize> {
     //~^ ERROR generic parameters with a default must be trailing
diff --git a/src/test/ui/const-generics/issues/issue-70273-assoc-fn.rs b/src/test/ui/const-generics/issues/issue-70273-assoc-fn.rs
new file mode 100644 (file)
index 0000000..189a325
--- /dev/null
@@ -0,0 +1,17 @@
+// check-pass
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![cfg_attr(full, allow(incomplete_features))]
+
+trait T<const A: usize> {
+    fn f();
+}
+struct S;
+
+impl T<0usize> for S {
+    fn f() {}
+}
+
+fn main() {
+    let _err = <S as T<0usize>>::f();
+}
diff --git a/src/test/ui/const-generics/issues/issue-84408.rs b/src/test/ui/const-generics/issues/issue-84408.rs
new file mode 100644 (file)
index 0000000..e1ba850
--- /dev/null
@@ -0,0 +1,38 @@
+// Regression test for #84408.
+// check-pass
+
+#![feature(const_generics, const_evaluatable_checked)]
+#![allow(incomplete_features)]
+
+trait Melon<const X: usize> {
+    fn new(arr: [i32; X]) -> Self;
+    fn change<T: Melon<X>>(self) -> T;
+}
+
+struct Foo([i32; 5]);
+struct Bar<const A: usize, const B: usize>([i32; A + B])
+where
+    [(); A + B]: ;
+
+impl Melon<5> for Foo {
+    fn new(arr: [i32; 5]) -> Self {
+        Foo(arr)
+    }
+    fn change<T: Melon<5>>(self) -> T {
+        T::new(self.0)
+    }
+}
+
+impl<const A: usize, const B: usize> Melon<{ A + B }> for Bar<A, B>
+where
+    [(); A + B]: ,
+{
+    fn new(arr: [i32; A + B]) -> Self {
+        Bar(arr)
+    }
+    fn change<T: Melon<{ A + B }>>(self) -> T {
+        T::new(self.0)
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs
deleted file mode 100644 (file)
index 189a325..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-// check-pass
-// revisions: full min
-#![cfg_attr(full, feature(const_generics))]
-#![cfg_attr(full, allow(incomplete_features))]
-
-trait T<const A: usize> {
-    fn f();
-}
-struct S;
-
-impl T<0usize> for S {
-    fn f() {}
-}
-
-fn main() {
-    let _err = <S as T<0usize>>::f();
-}
index c2b7b206653a60d97a73f92aa5e4ba549608f2ea..e8fd9e7769b791b70195e575b9348912b4bc86fb 100644 (file)
@@ -1,22 +1,17 @@
 error: generic parameters with a default must be trailing
-  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12
+  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:12
    |
 LL | struct Bar<T = [u8; N], const N: usize>(T);
    |            ^
    |
    = note: using type defaults and const parameters in the same parameter list is currently not permitted
 
-error: constant values inside of type parameter defaults must not depend on generic parameters
-  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44
-   |
-LL | struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
-   |                                            ^ the anonymous constant must not depend on the parameter `T`
-
-error: constant values inside of type parameter defaults must not depend on generic parameters
-  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21
+error[E0128]: generic parameters with a default cannot use forward declared identifiers
+  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21
    |
 LL | struct Bar<T = [u8; N], const N: usize>(T);
-   |                     ^ the anonymous constant must not depend on the parameter `N`
+   |                     ^ defaulted generic parameters cannot be forward declared
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0128`.
index 4a462c328bf6457e434d3c613f9b2d40db74e041..5fa6423306c5a5e7cac46e25c983fa404d4d6363 100644 (file)
@@ -1,5 +1,5 @@
 error: generic parameters with a default must be trailing
-  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12
+  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:12
    |
 LL | struct Bar<T = [u8; N], const N: usize>(T);
    |            ^
@@ -7,7 +7,7 @@ LL | struct Bar<T = [u8; N], const N: usize>(T);
    = note: using type defaults and const parameters in the same parameter list is currently not permitted
 
 error: generic parameters may not be used in const operations
-  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44
+  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:5:44
    |
 LL | struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
    |                                            ^ cannot perform const operation using `T`
@@ -15,11 +15,12 @@ LL | struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
    = note: type parameters may not be used in const expressions
    = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
 
-error: constant values inside of type parameter defaults must not depend on generic parameters
-  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21
+error[E0128]: generic parameters with a default cannot use forward declared identifiers
+  --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21
    |
 LL | struct Bar<T = [u8; N], const N: usize>(T);
-   |                     ^ the anonymous constant must not depend on the parameter `N`
+   |                     ^ defaulted generic parameters cannot be forward declared
 
 error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0128`.
index c7be8bdaf9c3dc68fe5be5b322c56c353ba0f06b..76c1b84aef557864846ab7f9695be49113ac3561 100644 (file)
@@ -1,15 +1,12 @@
 // revisions: full min
-
 #![cfg_attr(full, feature(const_generics))]
 #![cfg_attr(full, allow(incomplete_features))]
 
 struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
-//[full]~^ ERROR constant values inside of type parameter defaults
-//[min]~^^ ERROR generic parameters may not be used in const operations
+//[min]~^ ERROR generic parameters may not be used in const operations
 
-// FIXME(const_generics_defaults): We still don't know how to deal with type defaults.
 struct Bar<T = [u8; N], const N: usize>(T);
-//~^ ERROR constant values inside of type parameter defaults
+//~^ ERROR generic parameters with a default cannot use forward declared identifiers
 //~| ERROR generic parameters with a default
 
 fn main() {}
index 9c02d232e134b1a9ea5afeff0fd2f526b5f32c36..66b76627c02e631320c99fee30a2c158cd1fe841 100644 (file)
@@ -1,6 +1,6 @@
 // run-pass
 // Test a ZST enum whose dicriminant is ~0i128. This caused an ICE when casting to a i32.
-#![feature(test)]
+#![feature(bench_black_box)]
 use std::hint::black_box;
 
 #[derive(Copy, Clone)]
index 9fd9e8f36472d548961fde5565af967ddbf9102b..f2d9758b8dc08cee390edb4d8483e8c0824508ff 100644 (file)
@@ -1,7 +1,7 @@
 // issue-49296: Unsafe shenigans in constants can result in missing errors
 
-#![feature(const_fn)]
 #![feature(const_fn_union)]
+#![feature(const_fn_trait_bound)]
 
 const unsafe fn transmute<T: Copy, U: Copy>(t: T) -> U {
     #[repr(C)]
index 1006d854688a4ca860179e81bb17f897d1907bc6..cbc220a1ba29b789c18af4729aea223efdb3baac 100644 (file)
@@ -1,5 +1,5 @@
 // Test that const fn is illegal in a trait declaration, whether or
-// not a default is provided.
+// not a default is provided, and even with the feature gate.
 
 #![feature(const_fn)]
 
index 7b924aa46aa3c868958df94b8671b5317a5ba1ea..1928c51885efff2d97205da720e57fad152097b1 100644 (file)
@@ -3,7 +3,8 @@
 
 // A very basic test of const fn functionality.
 
-#![feature(const_fn, const_indexing)]
+#![feature(const_indexing)]
+#![feature(const_fn_trait_bound)]
 
 const fn add(x: u32, y: u32) -> u32 {
     x + y
index d016d236dbf81a5e24127f33099d13585c1078cf..a47f6af02965b63c7eeaca87683439beeca17fb7 100644 (file)
@@ -1,6 +1,6 @@
 // run-pass
 #![feature(const_discriminant)]
-#![feature(test)]
+#![feature(bench_black_box)]
 #![allow(dead_code)]
 
 use std::mem::{discriminant, Discriminant};
diff --git a/src/test/ui/consts/const_fn_trait_bound.gated.stderr b/src/test/ui/consts/const_fn_trait_bound.gated.stderr
new file mode 100644 (file)
index 0000000..ded05cb
--- /dev/null
@@ -0,0 +1,8 @@
+error: fatal error triggered by #[rustc_error]
+  --> $DIR/const_fn_trait_bound.rs:17:1
+   |
+LL | fn main() {}
+   | ^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/consts/const_fn_trait_bound.rs b/src/test/ui/consts/const_fn_trait_bound.rs
new file mode 100644 (file)
index 0000000..b1ef820
--- /dev/null
@@ -0,0 +1,17 @@
+// gate-test-const_fn_trait_bound
+
+// revisions: stock gated
+
+#![feature(rustc_attrs)]
+#![cfg_attr(gated, feature(const_fn_trait_bound))]
+
+const fn test1<T: std::ops::Add>() {}
+//[stock]~^ trait bounds
+const fn test2(_x: &dyn Send) {}
+//[stock]~^ trait bounds
+const fn test3() -> &'static dyn Send { loop {} }
+//[stock]~^ trait bounds
+
+
+#[rustc_error]
+fn main() {} //[gated]~ fatal error triggered by #[rustc_error]
diff --git a/src/test/ui/consts/const_fn_trait_bound.stock.stderr b/src/test/ui/consts/const_fn_trait_bound.stock.stderr
new file mode 100644 (file)
index 0000000..2ad45f3
--- /dev/null
@@ -0,0 +1,30 @@
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
+  --> $DIR/const_fn_trait_bound.rs:8:16
+   |
+LL | const fn test1<T: std::ops::Add>() {}
+   |                ^
+   |
+   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
+
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
+  --> $DIR/const_fn_trait_bound.rs:10:16
+   |
+LL | const fn test2(_x: &dyn Send) {}
+   |                ^^
+   |
+   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
+
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
+  --> $DIR/const_fn_trait_bound.rs:12:21
+   |
+LL | const fn test3() -> &'static dyn Send { loop {} }
+   |                     ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/consts/const_fn_unsize.gated.stderr b/src/test/ui/consts/const_fn_unsize.gated.stderr
new file mode 100644 (file)
index 0000000..8a65c27
--- /dev/null
@@ -0,0 +1,8 @@
+error: fatal error triggered by #[rustc_error]
+  --> $DIR/const_fn_unsize.rs:16:1
+   |
+LL | fn main() {}
+   | ^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/consts/const_fn_unsize.rs b/src/test/ui/consts/const_fn_unsize.rs
new file mode 100644 (file)
index 0000000..0cab3b0
--- /dev/null
@@ -0,0 +1,16 @@
+// gate-test-const_fn_unsize
+
+// revisions: stock gated
+
+#![feature(rustc_attrs)]
+#![cfg_attr(gated, feature(const_fn_unsize))]
+
+use std::ptr::NonNull;
+
+const fn test() {
+    let _x = NonNull::<[i32; 0]>::dangling() as NonNull<[i32]>;
+    //[stock]~^ unsizing cast
+}
+
+#[rustc_error]
+fn main() {} //[gated]~ fatal error triggered by #[rustc_error]
diff --git a/src/test/ui/consts/const_fn_unsize.stock.stderr b/src/test/ui/consts/const_fn_unsize.stock.stderr
new file mode 100644 (file)
index 0000000..cc746d4
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
+  --> $DIR/const_fn_unsize.rs:11:14
+   |
+LL |     let _x = NonNull::<[i32; 0]>::dangling() as NonNull<[i32]>;
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+   = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
index ee5434b147b661c5163189dc1f820650859e5742..e6ce7e12520f509504e1bc298418a705a31d364b 100644 (file)
@@ -130,23 +130,23 @@ LL |     const fn get_mut_sq(&mut self) -> &mut T { &mut self.0 }
    = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:84:16
    |
 LL | const fn foo11<T: std::fmt::Display>(t: T) -> T { t }
    |                ^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:86:18
    |
 LL | const fn foo11_2<T: Send>(t: T) -> T { t }
    |                  ^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
 error[E0013]: constant functions cannot refer to statics
   --> $DIR/min_const_fn.rs:90:27
@@ -209,41 +209,41 @@ LL | const fn inc(x: &mut i32) { *x += 1 }
    = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:110:6
    |
 LL | impl<T: std::fmt::Debug> Foo<T> {
    |      ^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:115:6
    |
 LL | impl<T: std::fmt::Debug + Sized> Foo<T> {
    |      ^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:120:6
    |
 LL | impl<T: Sync + Sized> Foo<T> {
    |      ^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:126:34
    |
 LL | const fn no_apit2(_x: AlanTuring<impl std::fmt::Debug>) {}
    |                                  ^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
 error[E0493]: destructors cannot be evaluated at compile-time
   --> $DIR/min_const_fn.rs:126:19
@@ -253,14 +253,14 @@ LL | const fn no_apit2(_x: AlanTuring<impl std::fmt::Debug>) {}
    |                   |
    |                   constant functions cannot evaluate destructors
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:129:22
    |
 LL | const fn no_apit(_x: impl std::fmt::Debug) {}
    |                      ^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
 error[E0493]: destructors cannot be evaluated at compile-time
   --> $DIR/min_const_fn.rs:129:18
@@ -270,50 +270,50 @@ LL | const fn no_apit(_x: impl std::fmt::Debug) {}
    |                  |
    |                  constant functions cannot evaluate destructors
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:132:23
    |
 LL | const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {}
    |                       ^^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn.rs:134:32
    |
 LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
   --> $DIR/min_const_fn.rs:134:63
    |
 LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
    |                                                               ^^^
    |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+   = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
 
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
   --> $DIR/min_const_fn.rs:134:63
    |
 LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
    |                                                               ^^^
    |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+   = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
 
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
   --> $DIR/min_const_fn.rs:141:42
    |
 LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 }
    |                                          ^^^
    |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+   = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
 
 error[E0658]: function pointers cannot appear in constant functions
   --> $DIR/min_const_fn.rs:144:21
@@ -344,5 +344,5 @@ LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
 
 error: aborting due to 39 previous errors
 
-Some errors have detailed explanations: E0013, E0493, E0658, E0723.
+Some errors have detailed explanations: E0013, E0493, E0658.
 For more information about an error, try `rustc --explain E0013`.
index 1394db591cadd050523acbb56db21c298538c69e..cf635d656996e0baf728c9159b6f14529c0b47f4 100644 (file)
@@ -1,21 +1,21 @@
-error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
+error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
   --> $DIR/min_const_fn_dyn.rs:9:5
    |
 LL |     x.0.field;
    |     ^^^^^^^^^
    |
    = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
 
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
   --> $DIR/min_const_fn_dyn.rs:12:66
    |
 LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
    |                                                                  ^^
    |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+   = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0723`.
+For more information about this error, try `rustc --explain E0658`.
index dc08ccd02b646533d17ebdda9e86b4761b64cb9d..79691cddfb40311e4e0bd6a29cdd704ec8a28e37 100644 (file)
@@ -1,12 +1,12 @@
-error[E0723]: unsizing casts to types besides slices are not allowed in const fn
+error[E0658]: unsizing casts to types besides slices are not allowed in const fn
   --> $DIR/unsizing-cast-non-null.rs:6:5
    |
 LL |     NonNull::<[T; 0]>::dangling()
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
+   = note: see issue #64992 <https://github.com/rust-lang/rust/issues/64992> for more information
+   = help: add `#![feature(const_fn_unsize)]` to the crate attributes to enable
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0723`.
+For more information about this error, try `rustc --explain E0658`.
index 43951c6854b1aa820cd0f2f857b693eb6d318edc..2fbbbbffac484a0c6d58ce2e5ccd6ec2fb62bb75 100644 (file)
@@ -6,7 +6,7 @@
 #![stable(feature = "core", since = "1.6.0")]
 #![feature(rustc_const_unstable)]
 #![feature(staged_api)]
-#![feature(const_fn)]
+#![feature(const_fn_trait_bound)]
 
 enum Opt<T> {
     Some(T),
diff --git a/src/test/ui/crate-loading/missing-std.rs b/src/test/ui/crate-loading/missing-std.rs
new file mode 100644 (file)
index 0000000..442a7c0
--- /dev/null
@@ -0,0 +1,11 @@
+// compile-flags: --target x86_64-unknown-uefi
+// rustc-env:CARGO=/usr/bin/cargo
+// rustc-env:RUSTUP_HOME=/home/bors/.rustup
+#![no_core]
+extern crate core;
+//~^ ERROR can't find crate for `core`
+//~| NOTE can't find crate
+//~| NOTE target may not be installed
+//~| HELP consider building the standard library from source with `cargo build -Zbuild-std`
+//~| HELP consider downloading the target with `rustup target add x86_64-unknown-uefi`
+fn main() {}
diff --git a/src/test/ui/crate-loading/missing-std.stderr b/src/test/ui/crate-loading/missing-std.stderr
new file mode 100644 (file)
index 0000000..25808ef
--- /dev/null
@@ -0,0 +1,13 @@
+error[E0463]: can't find crate for `core`
+  --> $DIR/missing-std.rs:5:1
+   |
+LL | extern crate core;
+   | ^^^^^^^^^^^^^^^^^^ can't find crate
+   |
+   = note: the `x86_64-unknown-uefi` target may not be installed
+   = help: consider downloading the target with `rustup target add x86_64-unknown-uefi`
+   = help: consider building the standard library from source with `cargo build -Zbuild-std`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0463`.
diff --git a/src/test/ui/derives/derive-macro-const-default.rs b/src/test/ui/derives/derive-macro-const-default.rs
new file mode 100644 (file)
index 0000000..a844f2d
--- /dev/null
@@ -0,0 +1,14 @@
+// check-pass
+#![allow(incomplete_features)]
+#![feature(const_generics_defaults)]
+
+#[derive(Clone, PartialEq, Debug)]
+struct Example<T, const N: usize = 1usize>([T; N]);
+
+fn main() {
+    let a = Example([(); 16]);
+    let b = a.clone();
+    if a != b {
+        let _c = format!("{:?}", a);
+    }
+}
diff --git a/src/test/ui/destructuring-assignment/bad-expr-lhs.rs b/src/test/ui/destructuring-assignment/bad-expr-lhs.rs
new file mode 100644 (file)
index 0000000..39536f1
--- /dev/null
@@ -0,0 +1,12 @@
+fn main() {
+    1 = 2; //~ ERROR invalid left-hand side of assignment
+    1 += 2; //~ ERROR invalid left-hand side of assignment
+    (1, 2) = (3, 4); //~ ERROR destructuring assignments are unstable
+    //~| ERROR invalid left-hand side of assignment
+    //~| ERROR invalid left-hand side of assignment
+
+    let (a, b) = (1, 2);
+    (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable
+
+    None = Some(3); //~ ERROR invalid left-hand side of assignment
+}
diff --git a/src/test/ui/destructuring-assignment/bad-expr-lhs.stderr b/src/test/ui/destructuring-assignment/bad-expr-lhs.stderr
new file mode 100644 (file)
index 0000000..d4b2193
--- /dev/null
@@ -0,0 +1,66 @@
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/bad-expr-lhs.rs:4:12
+   |
+LL |     (1, 2) = (3, 4);
+   |     ------ ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/bad-expr-lhs.rs:9:12
+   |
+LL |     (a, b) = (3, 4);
+   |     ------ ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/bad-expr-lhs.rs:2:7
+   |
+LL |     1 = 2;
+   |     - ^
+   |     |
+   |     cannot assign to this expression
+
+error[E0067]: invalid left-hand side of assignment
+  --> $DIR/bad-expr-lhs.rs:3:7
+   |
+LL |     1 += 2;
+   |     - ^^
+   |     |
+   |     cannot assign to this expression
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/bad-expr-lhs.rs:4:12
+   |
+LL |     (1, 2) = (3, 4);
+   |      -     ^
+   |      |
+   |      cannot assign to this expression
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/bad-expr-lhs.rs:4:12
+   |
+LL |     (1, 2) = (3, 4);
+   |         -  ^
+   |         |
+   |         cannot assign to this expression
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/bad-expr-lhs.rs:11:10
+   |
+LL |     None = Some(3);
+   |     ---- ^
+   |     |
+   |     cannot assign to this expression
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0067, E0070, E0658.
+For more information about an error, try `rustc --explain E0067`.
diff --git a/src/test/ui/did_you_mean/pub-macro-rules.rs b/src/test/ui/did_you_mean/pub-macro-rules.rs
new file mode 100644 (file)
index 0000000..c539370
--- /dev/null
@@ -0,0 +1,16 @@
+#[macro_use] mod bleh {
+    pub macro_rules! foo { //~ ERROR can't qualify macro_rules invocation
+        ($n:ident) => (
+            fn $n () -> i32 {
+                1
+            }
+        )
+    }
+
+}
+
+foo!(meh);
+
+fn main() {
+    println!("{}", meh());
+}
diff --git a/src/test/ui/did_you_mean/pub-macro-rules.stderr b/src/test/ui/did_you_mean/pub-macro-rules.stderr
new file mode 100644 (file)
index 0000000..0bde578
--- /dev/null
@@ -0,0 +1,8 @@
+error: can't qualify macro_rules invocation with `pub`
+  --> $DIR/pub-macro-rules.rs:2:5
+   |
+LL |     pub macro_rules! foo {
+   |     ^^^ help: try exporting the macro: `#[macro_export]`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/error-codes/E0137.rs b/src/test/ui/error-codes/E0137.rs
deleted file mode 100644 (file)
index b8299c7..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#![feature(main)]
-
-#[main]
-fn foo() {}
-
-#[main]
-fn f() {}
-//~^ ERROR E0137
diff --git a/src/test/ui/error-codes/E0137.stderr b/src/test/ui/error-codes/E0137.stderr
deleted file mode 100644 (file)
index f4d5e10..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0137]: multiple functions with a `#[main]` attribute
-  --> $DIR/E0137.rs:7:1
-   |
-LL | fn foo() {}
-   | ----------- first `#[main]` function
-...
-LL | fn f() {}
-   | ^^^^^^^^^ additional `#[main]` function
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0137`.
index 1f54af4d154e4524e942d8704ee35dfaabe803c0..dea0bb259f5062e95be1a454e8c64fb652422e09 100644 (file)
@@ -9,8 +9,8 @@ LL | fn f(p: Path) { }
    = help: unsized fn params are gated as an unstable feature
 help: function arguments must have a statically known size, borrowed types always have a known size
    |
-LL | fn f(&p: Path) { }
-   |      ^
+LL | fn f(p: &Path) { }
+   |         ^
 
 error[E0277]: the trait bound `i32: Foo` is not satisfied
   --> $DIR/E0277.rs:15:15
index 691c367aeb8597d9074f657c5cd702178e5ea014..b97aa214f843ab9582e99cab9f32f28374f391d6 100644 (file)
@@ -3,10 +3,8 @@
 const fn foo() -> usize { 0 } // ok
 
 trait Foo {
-    const fn foo() -> u32; //~ ERROR const fn is unstable
-                           //~| ERROR functions in traits cannot be declared const
-    const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
-                                //~| ERROR functions in traits cannot be declared const
+    const fn foo() -> u32; //~ ERROR functions in traits cannot be declared const
+    const fn bar() -> u32 { 0 } //~ ERROR functions in traits cannot be declared const
 }
 
 impl Foo for u32 {
index 843e56301375aaadd67f0166d922ac94a862ef93..1e7fd669b1d48941f74107fb32a653f7e87a550e 100644 (file)
@@ -5,36 +5,17 @@ LL |     const fn foo() -> u32;
    |     ^^^^^ functions in traits cannot be const
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/feature-gate-const_fn.rs:8:5
+  --> $DIR/feature-gate-const_fn.rs:7:5
    |
 LL |     const fn bar() -> u32 { 0 }
    |     ^^^^^ functions in traits cannot be const
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/feature-gate-const_fn.rs:13:5
+  --> $DIR/feature-gate-const_fn.rs:11:5
    |
 LL |     const fn foo() -> u32 { 0 }
    |     ^^^^^ functions in traits cannot be const
 
-error[E0658]: const fn is unstable
-  --> $DIR/feature-gate-const_fn.rs:6:5
-   |
-LL |     const fn foo() -> u32;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
-
-error[E0658]: const fn is unstable
-  --> $DIR/feature-gate-const_fn.rs:8:5
-   |
-LL |     const fn bar() -> u32 { 0 }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0379, E0658.
-For more information about an error, try `rustc --explain E0379`.
+For more information about this error, try `rustc --explain E0379`.
diff --git a/src/test/ui/feature-gates/feature-gate-large-assignments.rs b/src/test/ui/feature-gates/feature-gate-large-assignments.rs
new file mode 100644 (file)
index 0000000..7e9e574
--- /dev/null
@@ -0,0 +1,5 @@
+// check that `move_size_limit is feature-gated
+
+#![move_size_limit = "42"] //~ ERROR the `#[move_size_limit]` attribute is an experimental feature
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-large-assignments.stderr b/src/test/ui/feature-gates/feature-gate-large-assignments.stderr
new file mode 100644 (file)
index 0000000..8ddc304
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: the `#[move_size_limit]` attribute is an experimental feature
+  --> $DIR/feature-gate-large-assignments.rs:3:1
+   |
+LL | #![move_size_limit = "42"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #83518 <https://github.com/rust-lang/rust/issues/83518> for more information
+   = help: add `#![feature(large_assignments)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-main.rs b/src/test/ui/feature-gates/feature-gate-main.rs
deleted file mode 100644 (file)
index 9c304a1..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#[main]
-fn foo() {} //~ ERROR: declaration of a non-standard `#[main]` function may change over time
diff --git a/src/test/ui/feature-gates/feature-gate-main.stderr b/src/test/ui/feature-gates/feature-gate-main.stderr
deleted file mode 100644 (file)
index f0ef3b3..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0658]: declaration of a non-standard `#[main]` function may change over time, for now a top-level `fn main()` is required
-  --> $DIR/feature-gate-main.rs:2:1
-   |
-LL | fn foo() {}
-   | ^^^^^^^^^^^
-   |
-   = note: see issue #29634 <https://github.com/rust-lang/rust/issues/29634> for more information
-   = help: add `#![feature(main)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
index 5a01e053ed038e13b3a48c2ac1a1e2e43a684c28..8f9b433009d3c41a50fef2a41538fa3e28a81299 100644 (file)
@@ -3,10 +3,8 @@
 const fn foo() -> usize { 0 } // stabilized
 
 trait Foo {
-    const fn foo() -> u32; //~ ERROR const fn is unstable
-                           //~| ERROR functions in traits cannot be declared const
-    const fn bar() -> u32 { 0 } //~ ERROR const fn is unstable
-                                //~| ERROR functions in traits cannot be declared const
+    const fn foo() -> u32; //~ ERROR functions in traits cannot be declared const
+    const fn bar() -> u32 { 0 } //~ ERROR functions in traits cannot be declared const
 }
 
 impl Foo for u32 {
index 56a882e30fc4af88c075b8e05752bcd459830aa1..d7a58591364ed22fe9850a627aec79e3c330fb09 100644 (file)
@@ -5,36 +5,17 @@ LL |     const fn foo() -> u32;
    |     ^^^^^ functions in traits cannot be const
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/feature-gate-min_const_fn.rs:8:5
+  --> $DIR/feature-gate-min_const_fn.rs:7:5
    |
 LL |     const fn bar() -> u32 { 0 }
    |     ^^^^^ functions in traits cannot be const
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/feature-gate-min_const_fn.rs:13:5
+  --> $DIR/feature-gate-min_const_fn.rs:11:5
    |
 LL |     const fn foo() -> u32 { 0 }
    |     ^^^^^ functions in traits cannot be const
 
-error[E0658]: const fn is unstable
-  --> $DIR/feature-gate-min_const_fn.rs:6:5
-   |
-LL |     const fn foo() -> u32;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
-
-error[E0658]: const fn is unstable
-  --> $DIR/feature-gate-min_const_fn.rs:8:5
-   |
-LL |     const fn bar() -> u32 { 0 }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
-   = help: add `#![feature(const_fn)]` to the crate attributes to enable
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0379, E0658.
-For more information about an error, try `rustc --explain E0379`.
+For more information about this error, try `rustc --explain E0379`.
diff --git a/src/test/ui/feature-gates/feature-gate-no_coverage.rs b/src/test/ui/feature-gates/feature-gate-no_coverage.rs
new file mode 100644 (file)
index 0000000..c6b79f9
--- /dev/null
@@ -0,0 +1,8 @@
+#![crate_type = "lib"]
+
+#[no_coverage]
+#[feature(no_coverage)] // does not have to be enabled before `#[no_coverage]`
+fn no_coverage_is_enabled_on_this_function() {}
+
+#[no_coverage] //~ ERROR the `#[no_coverage]` attribute is an experimental feature
+fn requires_feature_no_coverage() {}
diff --git a/src/test/ui/feature-gates/feature-gate-no_coverage.stderr b/src/test/ui/feature-gates/feature-gate-no_coverage.stderr
new file mode 100644 (file)
index 0000000..04627be
--- /dev/null
@@ -0,0 +1,13 @@
+error[E0658]: the `#[no_coverage]` attribute is an experimental feature
+  --> $DIR/feature-gate-no_coverage.rs:7:1
+   |
+LL | #[no_coverage]
+   | ^^^^^^^^^^^^^^
+   |
+   = note: see issue #84605 <https://github.com/rust-lang/rust/issues/84605> for more information
+   = help: add `#![feature(no_coverage)]` to the crate attributes to enable
+   = help: or, alternatively, add `#[feature(no_coverage)]` to the function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-non_ascii_idents.rs b/src/test/ui/feature-gates/feature-gate-non_ascii_idents.rs
deleted file mode 100644 (file)
index 524ad3c..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-extern crate core as bäz; //~ ERROR non-ascii idents
-
-use föö::bar; //~ ERROR non-ascii idents
-
-mod föö { //~ ERROR non-ascii idents
-    pub fn bar() {}
-}
-
-fn bär( //~ ERROR non-ascii idents
-    bäz: isize //~ ERROR non-ascii idents
-    ) {
-    let _ö: isize; //~ ERROR non-ascii idents
-
-    match (1, 2) {
-        (_ä, _) => {} //~ ERROR non-ascii idents
-    }
-}
-
-struct Föö { //~ ERROR non-ascii idents
-    föö: isize //~ ERROR non-ascii idents
-}
-
-enum Bär { //~ ERROR non-ascii idents
-    Bäz { //~ ERROR non-ascii idents
-        qüx: isize //~ ERROR non-ascii idents
-    }
-}
-
-extern "C" {
-    fn qüx();  //~ ERROR non-ascii idents
-    //~^ ERROR items in `extern` blocks
-}
-
-fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-non_ascii_idents.stderr b/src/test/ui/feature-gates/feature-gate-non_ascii_idents.stderr
deleted file mode 100644 (file)
index c712ace..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-error: items in `extern` blocks cannot use non-ascii identifiers
-  --> $DIR/feature-gate-non_ascii_idents.rs:30:8
-   |
-LL | extern "C" {
-   | ---------- in this `extern` block
-LL |     fn qüx();
-   |        ^^^
-   |
-   = note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:1:22
-   |
-LL | extern crate core as bäz;
-   |                      ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:3:5
-   |
-LL | use föö::bar;
-   |     ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:5:5
-   |
-LL | mod föö {
-   |     ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:9:4
-   |
-LL | fn bär(
-   |    ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:10:5
-   |
-LL |     bäz: isize
-   |     ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:12:9
-   |
-LL |     let _ö: isize;
-   |         ^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:15:10
-   |
-LL |         (_ä, _) => {}
-   |          ^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:19:8
-   |
-LL | struct Föö {
-   |        ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:20:5
-   |
-LL |     föö: isize
-   |     ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:23:6
-   |
-LL | enum Bär {
-   |      ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:24:5
-   |
-LL |     Bäz {
-   |     ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:25:9
-   |
-LL |         qüx: isize
-   |         ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/feature-gate-non_ascii_idents.rs:30:8
-   |
-LL |     fn qüx();
-   |        ^^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error: aborting due to 14 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs
deleted file mode 100644 (file)
index 5504ec3..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-pub macro_rules! m1 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
-
-#[cfg(FALSE)]
-pub macro_rules! m2 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
-
-pub(crate) macro_rules! m3 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
-
-pub(in self) macro_rules! m4 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
-
-fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr
deleted file mode 100644 (file)
index bfaec39..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-error[E0658]: `pub` on `macro_rules` items is unstable
-  --> $DIR/feature-gate-pub_macro_rules.rs:1:1
-   |
-LL | pub macro_rules! m1 { () => {} }
-   | ^^^
-   |
-   = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
-   = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
-
-error[E0658]: `pub` on `macro_rules` items is unstable
-  --> $DIR/feature-gate-pub_macro_rules.rs:4:1
-   |
-LL | pub macro_rules! m2 { () => {} }
-   | ^^^
-   |
-   = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
-   = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
-
-error[E0658]: `pub` on `macro_rules` items is unstable
-  --> $DIR/feature-gate-pub_macro_rules.rs:6:1
-   |
-LL | pub(crate) macro_rules! m3 { () => {} }
-   | ^^^^^^^^^^
-   |
-   = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
-   = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
-
-error[E0658]: `pub` on `macro_rules` items is unstable
-  --> $DIR/feature-gate-pub_macro_rules.rs:8:1
-   |
-LL | pub(in self) macro_rules! m4 { () => {} }
-   | ^^^^^^^^^^^^
-   |
-   = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
-   = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
index 71acbb174ac667b82223581fbd48ddf0eccd620e..3631a03938a65972ceecc59c4b1a97258171ab43 100644 (file)
@@ -8,8 +8,8 @@ LL | fn foo(x: dyn Foo) {
    = help: unsized fn params are gated as an unstable feature
 help: function arguments must have a statically known size, borrowed types always have a known size
    |
-LL | fn foo(&x: dyn Foo) {
-   |        ^
+LL | fn foo(x: &dyn Foo) {
+   |           ^
 
 error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time
   --> $DIR/feature-gate-unsized_fn_params.rs:24:5
index 522542208724bd10071e9bdcff591130998040a5..0919c2f3a1e0e663f4137e2690f041321775dae7 100644 (file)
@@ -8,8 +8,8 @@ LL | fn f(f: dyn FnOnce()) {}
    = help: unsized fn params are gated as an unstable feature
 help: function arguments must have a statically known size, borrowed types always have a known size
    |
-LL | fn f(&f: dyn FnOnce()) {}
-   |      ^
+LL | fn f(f: &dyn FnOnce()) {}
+   |         ^
 
 error: aborting due to previous error
 
index 07167fa695e41e12de9862762ea06c9f653f3929..d1fbd38ce07d9d91c7b272904b3747a14b3eaaa0 100644 (file)
@@ -11,8 +11,8 @@
 
 #![macro_export]
 //~^ ERROR: `macro_export` attribute cannot be used at crate level
-#![main]
-//~^ ERROR: `main` attribute cannot be used at crate level
+#![rustc_main] //~ ERROR: the `#[rustc_main]` attribute is used internally to specify
+//~^ ERROR: `rustc_main` attribute cannot be used at crate level
 #![start]
 //~^ ERROR: `start` attribute cannot be used at crate level
 #![repr()]
@@ -106,24 +106,6 @@ mod inner { #![export_name="2200"] }
     //~| NOTE not a function or static
 }
 
-#[main]
-//~^ ERROR: `main` attribute can only be used on functions
-mod main {
-    mod inner { #![main] }
-    //~^ ERROR: `main` attribute can only be used on functions
-
-    // for `fn f()` case, see feature-gate-main.rs
-
-    #[main] struct S;
-    //~^ ERROR: `main` attribute can only be used on functions
-
-    #[main] type T = S;
-    //~^ ERROR: `main` attribute can only be used on functions
-
-    #[main] impl S { }
-    //~^ ERROR: `main` attribute can only be used on functions
-}
-
 #[start]
 //~^ ERROR: `start` attribute can only be used on functions
 mod start {
index 33a5021cde43034181d6da69903946ae3c35a578..ae2c428cc75ce13cb408a26f83fb61e483234a88 100644 (file)
@@ -1,3 +1,11 @@
+error[E0658]: the `#[rustc_main]` attribute is used internally to specify test entry point function
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1
+   |
+LL | #![rustc_main]
+   | ^^^^^^^^^^^^^^
+   |
+   = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
+
 error: attribute must be of the form `#[inline]` or `#[inline(always|never)]`
   --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:5
    |
@@ -8,62 +16,32 @@ LL |     #[inline = "2100"] fn f() { }
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
 
-error: `main` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:109:1
-   |
-LL | #[main]
-   | ^^^^^^^
-
-error: `main` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:112:17
-   |
-LL |     mod inner { #![main] }
-   |                 ^^^^^^^^
-
-error: `main` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:5
-   |
-LL |     #[main] struct S;
-   |     ^^^^^^^
-
-error: `main` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:5
-   |
-LL |     #[main] type T = S;
-   |     ^^^^^^^
-
-error: `main` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:5
-   |
-LL |     #[main] impl S { }
-   |     ^^^^^^^
-
 error: `start` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:109:1
    |
 LL | #[start]
    | ^^^^^^^^
 
 error: `start` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:130:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:112:17
    |
 LL |     mod inner { #![start] }
    |                 ^^^^^^^^^
 
 error: `start` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:135:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:5
    |
 LL |     #[start] struct S;
    |     ^^^^^^^^
 
 error: `start` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:138:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:5
    |
 LL |     #[start] type T = S;
    |     ^^^^^^^^
 
 error: `start` attribute can only be used on functions
-  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:141:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:5
    |
 LL |     #[start] impl S { }
    |     ^^^^^^^^
@@ -137,11 +115,11 @@ error: `macro_export` attribute cannot be used at crate level
 LL | #![macro_export]
    | ^^^^^^^^^^^^^^^^
 
-error: `main` attribute cannot be used at crate level
+error: `rustc_main` attribute cannot be used at crate level
   --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1
    |
-LL | #![main]
-   | ^^^^^^^^
+LL | #![rustc_main]
+   | ^^^^^^^^^^^^^^
 
 error: `start` attribute cannot be used at crate level
   --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1
@@ -245,6 +223,7 @@ error: attribute should be applied to a function or static
 LL |     #[export_name = "2200"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static
 
-error: aborting due to 36 previous errors
+error: aborting due to 32 previous errors
 
-For more information about this error, try `rustc --explain E0518`.
+Some errors have detailed explanations: E0518, E0658.
+For more information about an error, try `rustc --explain E0518`.
diff --git a/src/test/ui/feature-gates/thread-local-const-init.rs b/src/test/ui/feature-gates/thread-local-const-init.rs
new file mode 100644 (file)
index 0000000..6584ffa
--- /dev/null
@@ -0,0 +1,4 @@
+thread_local!(static X: u32 = const { 0 });
+//~^ ERROR: use of unstable library feature 'thread_local_const_init'
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/thread-local-const-init.stderr b/src/test/ui/feature-gates/thread-local-const-init.stderr
new file mode 100644 (file)
index 0000000..a35b18a
--- /dev/null
@@ -0,0 +1,13 @@
+error[E0658]: use of unstable library feature 'thread_local_const_init'
+  --> $DIR/thread-local-const-init.rs:1:1
+   |
+LL | thread_local!(static X: u32 = const { 0 });
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #84223 <https://github.com/rust-lang/rust/issues/84223> for more information
+   = help: add `#![feature(thread_local_const_init)]` to the crate attributes to enable
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/fn/bad-main.rs b/src/test/ui/fn/bad-main.rs
new file mode 100644 (file)
index 0000000..7511599
--- /dev/null
@@ -0,0 +1 @@
+fn main(x: isize) { } //~ ERROR: `main` function has wrong type [E0580]
diff --git a/src/test/ui/fn/bad-main.stderr b/src/test/ui/fn/bad-main.stderr
new file mode 100644 (file)
index 0000000..675b66d
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0580]: `main` function has wrong type
+  --> $DIR/bad-main.rs:1:1
+   |
+LL | fn main(x: isize) { }
+   | ^^^^^^^^^^^^^^^^^ incorrect number of function parameters
+   |
+   = note: expected fn pointer `fn()`
+              found fn pointer `fn(isize)`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0580`.
index 2dbd1840dec55da18cf6676424d4f8251cab693c..6527eb4750455f79dcf08c30b6fa378e78a632fa 100644 (file)
@@ -1,5 +1,3 @@
-// check-pass
-
 #![feature(generic_associated_types)]
   //~^ WARNING: the feature `generic_associated_types` is incomplete
 #![feature(associated_type_defaults)]
@@ -22,6 +20,7 @@ impl<T> Foo for Fooer<T> {
 }
 
 fn f(_arg : Box<dyn for<'a> Foo<A<'a> = &'a ()>>) {}
+//~^ the trait `Foo` cannot be made into an object
 
 
 fn main() {
index f3769827f04b251808d8ed2e4a2ecacc2eeba42c..49dfce8b4bd380199930e3caf9bf6ad1b8e7bf65 100644 (file)
@@ -1,5 +1,5 @@
 warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/gat-in-trait-path.rs:3:12
+  --> $DIR/gat-in-trait-path.rs:1:12
    |
 LL | #![feature(generic_associated_types)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,5 +7,21 @@ LL | #![feature(generic_associated_types)]
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
 
-warning: 1 warning emitted
+error[E0038]: the trait `Foo` cannot be made into an object
+  --> $DIR/gat-in-trait-path.rs:22:13
+   |
+LL | fn f(_arg : Box<dyn for<'a> Foo<A<'a> = &'a ()>>) {}
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object
+   |
+   = help: consider moving `A` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/gat-in-trait-path.rs:6:10
+   |
+LL | trait Foo {
+   |       --- this trait cannot be made into an object...
+LL |     type A<'a> where Self: 'a;
+   |          ^ ...because it contains the generic associated type `A`
+
+error: aborting due to previous error; 1 warning emitted
 
+For more information about this error, try `rustc --explain E0038`.
index ff38b3e93eb1aec3b617b64cd22f27e8cead4c09..6ee865072aebf8886b851674ef46720438919fc1 100644 (file)
@@ -1,5 +1,3 @@
-// check-pass
-
 #![feature(generic_associated_types)]
   //~^ WARNING: the feature `generic_associated_types` is incomplete
 
@@ -8,5 +6,6 @@ trait X {
 }
 
 fn _func1<'a>(_x: Box<dyn X<Y<'a>=&'a ()>>) {}
+//~^ ERROR the trait `X` cannot be made into an object
 
 fn main() {}
index 0fbf704df76a188ad1e28995b1c4ae2095ebd5b6..65998afa7f914428f7cb8bdfd06d340ef95cc98f 100644 (file)
@@ -1,5 +1,5 @@
 warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-67510-pass.rs:3:12
+  --> $DIR/issue-67510-pass.rs:1:12
    |
 LL | #![feature(generic_associated_types)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,5 +7,21 @@ LL | #![feature(generic_associated_types)]
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
 
-warning: 1 warning emitted
+error[E0038]: the trait `X` cannot be made into an object
+  --> $DIR/issue-67510-pass.rs:8:19
+   |
+LL | fn _func1<'a>(_x: Box<dyn X<Y<'a>=&'a ()>>) {}
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object
+   |
+   = help: consider moving `Y` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/issue-67510-pass.rs:5:10
+   |
+LL | trait X {
+   |       - this trait cannot be made into an object...
+LL |     type Y<'a>;
+   |          ^ ...because it contains the generic associated type `Y`
+
+error: aborting due to previous error; 1 warning emitted
 
+For more information about this error, try `rustc --explain E0038`.
diff --git a/src/test/ui/generic-associated-types/issue-70303.rs b/src/test/ui/generic-associated-types/issue-70303.rs
new file mode 100644 (file)
index 0000000..a1cb229
--- /dev/null
@@ -0,0 +1,60 @@
+// check-pass
+
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait Document {
+    type Cursor<'a>: DocCursor<'a>;
+
+    fn cursor(&self) -> Self::Cursor<'_>;
+}
+
+struct DocumentImpl {}
+
+impl Document for DocumentImpl {
+    type Cursor<'a> = DocCursorImpl<'a>;
+
+    fn cursor(&self) -> Self::Cursor<'_> {
+        DocCursorImpl {
+            document: &self,
+        }
+    }
+}
+
+
+trait DocCursor<'a> {}
+
+struct DocCursorImpl<'a> {
+    document: &'a DocumentImpl,
+}
+
+impl<'a> DocCursor<'a> for DocCursorImpl<'a> {}
+
+struct Lexer<'d, Cursor>
+where
+    Cursor: DocCursor<'d>,
+{
+    cursor: Cursor,
+    _phantom: std::marker::PhantomData<&'d ()>,
+}
+
+
+impl<'d, Cursor> Lexer<'d, Cursor>
+where
+    Cursor: DocCursor<'d>,
+{
+    pub fn from<Doc>(document: &'d Doc) -> Lexer<'d, Cursor>
+    where
+        Doc: Document<Cursor<'d> = Cursor>,
+    {
+        Lexer {
+            cursor: document.cursor(),
+            _phantom: std::marker::PhantomData,
+        }
+    }
+}
+
+pub fn main() {
+    let doc = DocumentImpl {};
+    let lexer: Lexer<'_, DocCursorImpl<'_>> = Lexer::from(&doc);
+}
diff --git a/src/test/ui/generic-associated-types/issue-70304.rs b/src/test/ui/generic-associated-types/issue-70304.rs
new file mode 100644 (file)
index 0000000..225f61d
--- /dev/null
@@ -0,0 +1,63 @@
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait Document {
+    type Cursor<'a>: DocCursor<'a>;
+
+    fn cursor(&self) -> Self::Cursor<'_>;
+}
+
+struct DocumentImpl {}
+
+impl Document for DocumentImpl {
+    type Cursor<'a> = DocCursorImpl<'a>;
+
+    fn cursor(&self) -> Self::Cursor<'_> {
+        DocCursorImpl {
+            document: &self,
+        }
+    }
+}
+
+
+trait DocCursor<'a> {}
+
+struct DocCursorImpl<'a> {
+    document: &'a DocumentImpl,
+}
+
+impl<'a> DocCursor<'a> for DocCursorImpl<'a> {}
+
+struct Lexer<'d, Cursor>
+where
+    Cursor: DocCursor<'d>,
+{
+    cursor: Cursor,
+    _phantom: std::marker::PhantomData<&'d ()>,
+}
+
+
+impl<'d, Cursor> Lexer<'d, Cursor>
+where
+    Cursor: DocCursor<'d>,
+{
+    pub fn from<Doc>(document: &'d Doc) -> Lexer<'d, Cursor>
+    where
+        Doc: Document<Cursor<'d> = Cursor>,
+    {
+        Lexer {
+            cursor: document.cursor(),
+            _phantom: std::marker::PhantomData,
+        }
+    }
+}
+
+fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
+                                       //~^ ERROR: missing lifetime specifier
+    DocumentImpl {}
+}
+
+pub fn main() {
+    let doc = create_doc();
+    let lexer: Lexer<'_, DocCursorImpl<'_>> = Lexer::from(&doc);
+}
diff --git a/src/test/ui/generic-associated-types/issue-70304.stderr b/src/test/ui/generic-associated-types/issue-70304.stderr
new file mode 100644 (file)
index 0000000..dfa8601
--- /dev/null
@@ -0,0 +1,15 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/issue-70304.rs:55:41
+   |
+LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
+   |                                         ^^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime
+   |
+LL | fn create_doc() -> impl Document<Cursor<'static> = DocCursorImpl<'_>> {
+   |                                         ^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/generic-associated-types/issue-71176.rs b/src/test/ui/generic-associated-types/issue-71176.rs
new file mode 100644 (file)
index 0000000..470476b
--- /dev/null
@@ -0,0 +1,21 @@
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait Provider {
+    type A<'a>;
+      //~^ ERROR: missing generics for associated type
+}
+
+impl Provider for () {
+    type A<'a> = ();
+}
+
+struct Holder<B> {
+  inner: Box<dyn Provider<A = B>>,
+}
+
+fn main() {
+    Holder {
+        inner: Box::new(()),
+    };
+}
diff --git a/src/test/ui/generic-associated-types/issue-71176.stderr b/src/test/ui/generic-associated-types/issue-71176.stderr
new file mode 100644 (file)
index 0000000..dd19dd4
--- /dev/null
@@ -0,0 +1,19 @@
+error[E0107]: missing generics for associated type `Provider::A`
+  --> $DIR/issue-71176.rs:5:10
+   |
+LL |     type A<'a>;
+   |          ^ expected 1 lifetime argument
+   |
+note: associated type defined here, with 1 lifetime parameter: `'a`
+  --> $DIR/issue-71176.rs:5:10
+   |
+LL |     type A<'a>;
+   |          ^ --
+help: use angle brackets to add missing lifetime argument
+   |
+LL |     type A<'a><'a>;
+   |           ^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
index 5e73a8829862233ca6242d7087979ae55e8a79ec..9643c82db773257f08e6172a6fd2726303517780 100644 (file)
@@ -36,4 +36,6 @@ fn get_sub<'a>(&'a mut self) -> Self::SubType<'a> {
 
 fn main() {
     let sub: Box<dyn SuperTrait<SubType = SubStruct>> = Box::new(SuperStruct::new(0));
+    //~^ ERROR the trait `SuperTrait` cannot be made into an object
+    //~^^ ERROR the trait `SuperTrait` cannot be made into an object
 }
index 17661e0d90a4aecf2b550d95f27498ba38f4ea3e..d31560f12f0bb81af8669081e159e5a37f576bc6 100644 (file)
@@ -23,6 +23,39 @@ help: use angle brackets to add missing lifetime argument
 LL |     type SubType<'a><'a>: SubTrait;
    |                 ^^^^
 
-error: aborting due to previous error; 1 warning emitted
+error[E0038]: the trait `SuperTrait` cannot be made into an object
+  --> $DIR/issue-76535.rs:38:14
+   |
+LL |     let sub: Box<dyn SuperTrait<SubType = SubStruct>> = Box::new(SuperStruct::new(0));
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object
+   |
+   = help: consider moving `SubType` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/issue-76535.rs:7:10
+   |
+LL | pub trait SuperTrait {
+   |           ---------- this trait cannot be made into an object...
+LL |     type SubType<'a>: SubTrait;
+   |          ^^^^^^^ ...because it contains the generic associated type `SubType`
+
+error[E0038]: the trait `SuperTrait` cannot be made into an object
+  --> $DIR/issue-76535.rs:38:57
+   |
+LL |     let sub: Box<dyn SuperTrait<SubType = SubStruct>> = Box::new(SuperStruct::new(0));
+   |                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object
+   |
+   = help: consider moving `SubType` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/issue-76535.rs:7:10
+   |
+LL | pub trait SuperTrait {
+   |           ---------- this trait cannot be made into an object...
+LL |     type SubType<'a>: SubTrait;
+   |          ^^^^^^^ ...because it contains the generic associated type `SubType`
+   = note: required because of the requirements on the impl of `CoerceUnsized<Box<dyn SuperTrait<SubType = SubStruct<'_>>>>` for `Box<SuperStruct>`
+   = note: required by cast to type `Box<dyn SuperTrait<SubType = SubStruct<'_>>>`
+
+error: aborting due to 3 previous errors; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0107`.
+Some errors have detailed explanations: E0038, E0107.
+For more information about an error, try `rustc --explain E0038`.
diff --git a/src/test/ui/generic-associated-types/issue-78671.rs b/src/test/ui/generic-associated-types/issue-78671.rs
new file mode 100644 (file)
index 0000000..4e47d3c
--- /dev/null
@@ -0,0 +1,15 @@
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait CollectionFamily {
+    type Member<T>;
+         //~^ ERROR: missing generics for associated type
+}
+fn floatify() {
+    Box::new(Family) as &dyn CollectionFamily<Member=usize>
+    //~^ the trait `CollectionFamily` cannot be made into an object
+}
+
+struct Family;
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/issue-78671.stderr b/src/test/ui/generic-associated-types/issue-78671.stderr
new file mode 100644 (file)
index 0000000..c9febfb
--- /dev/null
@@ -0,0 +1,35 @@
+error[E0107]: missing generics for associated type `CollectionFamily::Member`
+  --> $DIR/issue-78671.rs:5:10
+   |
+LL |     type Member<T>;
+   |          ^^^^^^ expected 1 type argument
+   |
+note: associated type defined here, with 1 type parameter: `T`
+  --> $DIR/issue-78671.rs:5:10
+   |
+LL |     type Member<T>;
+   |          ^^^^^^ -
+help: use angle brackets to add missing type argument
+   |
+LL |     type Member<T><T>;
+   |                ^^^
+
+error[E0038]: the trait `CollectionFamily` cannot be made into an object
+  --> $DIR/issue-78671.rs:9:25
+   |
+LL |     Box::new(Family) as &dyn CollectionFamily<Member=usize>
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` cannot be made into an object
+   |
+   = help: consider moving `Member` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/issue-78671.rs:5:10
+   |
+LL | trait CollectionFamily {
+   |       ---------------- this trait cannot be made into an object...
+LL |     type Member<T>;
+   |          ^^^^^^ ...because it contains the generic associated type `Member`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0038, E0107.
+For more information about an error, try `rustc --explain E0038`.
index aeb33ca54641cc399daa00c0ef7209194e4aba85..b2ba3c24abbe1d7cb90a545bb452173283cd8e2c 100644 (file)
@@ -42,5 +42,6 @@ fn get<'a>(&self, _: &K) -> Option<Box<V>> {
 fn main() {
     let m = Box::new(std::collections::BTreeMap::<u8, u8>::new())
         as Box<dyn MapLike<u8, u8, VRefCont = dyn RefCont<'_, u8>>>;
-    //~^^ ERROR type mismatch resolving
+    //~^^ the trait `MapLike` cannot be made into an object
+    //~^^ the trait `MapLike` cannot be made into an object
 }
index a119bff03e2906e68bf67f30895e0ea4e9b110a1..4973ae19729acd6771b482f8086b271db27d003d 100644 (file)
@@ -14,17 +14,39 @@ help: use angle brackets to add missing lifetime argument
 LL |     type VRefCont<'a><'a>: RefCont<'a, V>;
    |                  ^^^^
 
-error[E0271]: type mismatch resolving `<BTreeMap<u8, u8> as MapLike<u8, u8>>::VRefCont<'static> == (dyn RefCont<'_, u8> + 'static)`
+error[E0038]: the trait `MapLike` cannot be made into an object
+  --> $DIR/issue-79422.rs:44:12
+   |
+LL |         as Box<dyn MapLike<u8, u8, VRefCont = dyn RefCont<'_, u8>>>;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` cannot be made into an object
+   |
+   = help: consider moving `VRefCont` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/issue-79422.rs:21:10
+   |
+LL | trait MapLike<K, V> {
+   |       ------- this trait cannot be made into an object...
+LL |     type VRefCont<'a>: RefCont<'a, V>;
+   |          ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
+
+error[E0038]: the trait `MapLike` cannot be made into an object
   --> $DIR/issue-79422.rs:43:13
    |
 LL |     let m = Box::new(std::collections::BTreeMap::<u8, u8>::new())
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn RefCont`, found reference
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` cannot be made into an object
+   |
+   = help: consider moving `VRefCont` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/issue-79422.rs:21:10
    |
-   = note: expected trait object `(dyn RefCont<'_, u8> + 'static)`
-                 found reference `&'static u8`
-   = note: required for the cast to the object type `dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>`
+LL | trait MapLike<K, V> {
+   |       ------- this trait cannot be made into an object...
+LL |     type VRefCont<'a>: RefCont<'a, V>;
+   |          ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
+   = note: required because of the requirements on the impl of `CoerceUnsized<Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>>` for `Box<BTreeMap<u8, u8>>`
+   = note: required by cast to type `Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>`
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0107, E0271.
-For more information about an error, try `rustc --explain E0107`.
+Some errors have detailed explanations: E0038, E0107.
+For more information about an error, try `rustc --explain E0038`.
diff --git a/src/test/ui/generic-associated-types/issue-79636-1.rs b/src/test/ui/generic-associated-types/issue-79636-1.rs
new file mode 100644 (file)
index 0000000..17f9387
--- /dev/null
@@ -0,0 +1,24 @@
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait Monad {
+    type Unwrapped;
+    type Wrapped<B>;
+         //~^ ERROR: missing generics for associated type `Monad::Wrapped`
+
+    fn bind<B, F>(self, f: F) -> Self::Wrapped<B> {
+        todo!()
+    }
+}
+
+fn join<MOuter, MInner, A>(outer: MOuter) -> MOuter::Wrapped<A>
+where
+    MOuter: Monad<Unwrapped = MInner>,
+    MInner: Monad<Unwrapped = A, Wrapped = MOuter::Wrapped<A>>,
+{
+    outer.bind(|inner| inner)
+}
+
+fn main() {
+    assert_eq!(join(Some(Some(true))), Some(true));
+}
diff --git a/src/test/ui/generic-associated-types/issue-79636-1.stderr b/src/test/ui/generic-associated-types/issue-79636-1.stderr
new file mode 100644 (file)
index 0000000..58eeb43
--- /dev/null
@@ -0,0 +1,19 @@
+error[E0107]: missing generics for associated type `Monad::Wrapped`
+  --> $DIR/issue-79636-1.rs:6:10
+   |
+LL |     type Wrapped<B>;
+   |          ^^^^^^^ expected 1 type argument
+   |
+note: associated type defined here, with 1 type parameter: `B`
+  --> $DIR/issue-79636-1.rs:6:10
+   |
+LL |     type Wrapped<B>;
+   |          ^^^^^^^ -
+help: use angle brackets to add missing type argument
+   |
+LL |     type Wrapped<B><B>;
+   |                 ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
diff --git a/src/test/ui/generic-associated-types/issue-79636-2.rs b/src/test/ui/generic-associated-types/issue-79636-2.rs
new file mode 100644 (file)
index 0000000..5a65421
--- /dev/null
@@ -0,0 +1,18 @@
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
+
+trait SomeTrait {
+    type Wrapped<A>: SomeTrait;
+         //~^ ERROR: missing generics for associated type `SomeTrait::Wrapped`
+
+    fn f() -> ();
+}
+
+fn program<W>() -> ()
+where
+    W: SomeTrait<Wrapped = W>,
+{
+    return W::f();
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/issue-79636-2.stderr b/src/test/ui/generic-associated-types/issue-79636-2.stderr
new file mode 100644 (file)
index 0000000..d5e3c56
--- /dev/null
@@ -0,0 +1,19 @@
+error[E0107]: missing generics for associated type `SomeTrait::Wrapped`
+  --> $DIR/issue-79636-2.rs:5:10
+   |
+LL |     type Wrapped<A>: SomeTrait;
+   |          ^^^^^^^ expected 1 type argument
+   |
+note: associated type defined here, with 1 type parameter: `A`
+  --> $DIR/issue-79636-2.rs:5:10
+   |
+LL |     type Wrapped<A>: SomeTrait;
+   |          ^^^^^^^ -
+help: use angle brackets to add missing type argument
+   |
+LL |     type Wrapped<A><A>: SomeTrait;
+   |                 ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
diff --git a/src/test/ui/generic-associated-types/trait-objects.rs b/src/test/ui/generic-associated-types/trait-objects.rs
new file mode 100644 (file)
index 0000000..997a550
--- /dev/null
@@ -0,0 +1,16 @@
+#![feature(generic_associated_types)]
+#![allow(incomplete_features)]
+
+trait StreamingIterator {
+    type Item<'a> where Self: 'a;
+    fn size_hint(&self) -> (usize, Option<usize>);
+    // Uncommenting makes `StreamingIterator` not object safe
+//    fn next(&mut self) -> Self::Item<'_>;
+}
+
+fn min_size(x: &mut dyn for<'a> StreamingIterator<Item<'a> = &'a i32>) -> usize {
+    //~^ the trait `StreamingIterator` cannot be made into an object
+    x.size_hint().0
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/trait-objects.stderr b/src/test/ui/generic-associated-types/trait-objects.stderr
new file mode 100644 (file)
index 0000000..a8f1768
--- /dev/null
@@ -0,0 +1,18 @@
+error[E0038]: the trait `StreamingIterator` cannot be made into an object
+  --> $DIR/trait-objects.rs:11:16
+   |
+LL | fn min_size(x: &mut dyn for<'a> StreamingIterator<Item<'a> = &'a i32>) -> usize {
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `StreamingIterator` cannot be made into an object
+   |
+   = help: consider moving `Item` to another trait
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/trait-objects.rs:5:10
+   |
+LL | trait StreamingIterator {
+   |       ----------------- this trait cannot be made into an object...
+LL |     type Item<'a> where Self: 'a;
+   |          ^^^^ ...because it contains the generic associated type `Item`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0038`.
diff --git a/src/test/ui/generics/bad-mid-path-type-params.rs b/src/test/ui/generics/bad-mid-path-type-params.rs
new file mode 100644 (file)
index 0000000..c42ce60
--- /dev/null
@@ -0,0 +1,44 @@
+struct S<T> {
+    contents: T,
+}
+
+impl<T> S<T> {
+    fn new<U>(x: T, _: U) -> S<T> {
+        S {
+            contents: x,
+        }
+    }
+}
+
+trait Trait<T> {
+    fn new<U>(x: T, y: U) -> Self;
+}
+
+struct S2 {
+    contents: isize,
+}
+
+impl Trait<isize> for S2 {
+    fn new<U>(x: isize, _: U) -> S2 {
+        S2 {
+            contents: x,
+        }
+    }
+}
+
+fn foo<'a>() {
+    let _ = S::new::<isize,f64>(1, 1.0);
+    //~^ ERROR this associated function takes 1 type argument but 2 type arguments were supplied
+
+    let _ = S::<'a,isize>::new::<f64>(1, 1.0);
+    //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
+
+    let _: S2 = Trait::new::<isize,f64>(1, 1.0);
+    //~^ ERROR this associated function takes 1 type argument but 2 type arguments were supplied
+
+    let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
+    //~^ ERROR this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
+    //~| ERROR this associated function takes 1 type argument but 2 type arguments were supplied
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/bad-mid-path-type-params.stderr b/src/test/ui/generics/bad-mid-path-type-params.stderr
new file mode 100644 (file)
index 0000000..dd96856
--- /dev/null
@@ -0,0 +1,73 @@
+error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
+  --> $DIR/bad-mid-path-type-params.rs:30:16
+   |
+LL |     let _ = S::new::<isize,f64>(1, 1.0);
+   |                ^^^        ---- help: remove this type argument
+   |                |
+   |                expected 1 type argument
+   |
+note: associated function defined here, with 1 type parameter: `U`
+  --> $DIR/bad-mid-path-type-params.rs:6:8
+   |
+LL |     fn new<U>(x: T, _: U) -> S<T> {
+   |        ^^^ -
+
+error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
+  --> $DIR/bad-mid-path-type-params.rs:33:13
+   |
+LL |     let _ = S::<'a,isize>::new::<f64>(1, 1.0);
+   |             ^   --- help: remove this lifetime argument
+   |             |
+   |             expected 0 lifetime arguments
+   |
+note: struct defined here, with 0 lifetime parameters
+  --> $DIR/bad-mid-path-type-params.rs:1:8
+   |
+LL | struct S<T> {
+   |        ^
+
+error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
+  --> $DIR/bad-mid-path-type-params.rs:36:24
+   |
+LL |     let _: S2 = Trait::new::<isize,f64>(1, 1.0);
+   |                        ^^^        ---- help: remove this type argument
+   |                        |
+   |                        expected 1 type argument
+   |
+note: associated function defined here, with 1 type parameter: `U`
+  --> $DIR/bad-mid-path-type-params.rs:14:8
+   |
+LL |     fn new<U>(x: T, y: U) -> Self;
+   |        ^^^ -
+
+error[E0107]: this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
+  --> $DIR/bad-mid-path-type-params.rs:39:17
+   |
+LL |     let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
+   |                 ^^^^^   --- help: remove this lifetime argument
+   |                 |
+   |                 expected 0 lifetime arguments
+   |
+note: trait defined here, with 0 lifetime parameters
+  --> $DIR/bad-mid-path-type-params.rs:13:7
+   |
+LL | trait Trait<T> {
+   |       ^^^^^
+
+error[E0107]: this associated function takes 1 type argument but 2 type arguments were supplied
+  --> $DIR/bad-mid-path-type-params.rs:39:36
+   |
+LL |     let _: S2 = Trait::<'a,isize>::new::<f64,f64>(1, 1.0);
+   |                                    ^^^      ---- help: remove this type argument
+   |                                    |
+   |                                    expected 1 type argument
+   |
+note: associated function defined here, with 1 type parameter: `U`
+  --> $DIR/bad-mid-path-type-params.rs:14:8
+   |
+LL |     fn new<U>(x: T, y: U) -> Self;
+   |        ^^^ -
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0107`.
diff --git a/src/test/ui/generics/generic-function-item-where-type.rs b/src/test/ui/generics/generic-function-item-where-type.rs
new file mode 100644 (file)
index 0000000..e1b0578
--- /dev/null
@@ -0,0 +1,6 @@
+fn foo<U>() {}
+
+fn main() {
+    foo::<main>()
+    //~^ ERROR constant provided when a type was expected
+}
diff --git a/src/test/ui/generics/generic-function-item-where-type.stderr b/src/test/ui/generics/generic-function-item-where-type.stderr
new file mode 100644 (file)
index 0000000..8859412
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0747]: constant provided when a type was expected
+  --> $DIR/generic-function-item-where-type.rs:4:11
+   |
+LL |     foo::<main>()
+   |           ^^^^
+   |
+   = help: `main` is a function item, not a type
+   = help: function item types cannot be named directly
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0747`.
index cb2bb2832b70e956ed37e42acace9ad62ab312ad..16ea71d48c8259dfbeff4f387b37189b3ed9700e 100644 (file)
@@ -5,5 +5,6 @@
 
 struct Foo<A, B = Vec<C>, C>(A, B, C);
 //~^ ERROR generic parameters with a default must be trailing
+//~| ERROR generic parameters with a default cannot use
 
 fn main() {}
index 6d7686175033545215869f2b111652728784d654..713ba091b861c8f01fb9710cc125d345e51aa275 100644 (file)
@@ -10,5 +10,12 @@ error: generic parameters with a default must be trailing
 LL | struct Foo<A, B = Vec<C>, C>(A, B, C);
    |               ^
 
-error: aborting due to 2 previous errors
+error[E0128]: generic parameters with a default cannot use forward declared identifiers
+  --> $DIR/generic-non-trailing-defaults.rs:6:23
+   |
+LL | struct Foo<A, B = Vec<C>, C>(A, B, C);
+   |                       ^ defaulted generic parameters cannot be forward declared
+
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0128`.
index d24336883e9631f67b5b59b20dd005d56396db81..2913a955dce1b2cf37ff2c61d62528093fb1f549 100644 (file)
@@ -1,6 +1,9 @@
 error[E0599]: no method named `f` found for unit type `()` in the current scope
   --> $DIR/trait_items.rs:17:24
    |
+LL |         fn f(&self) {}
+   |            - the method is available for `()` here
+...
 LL |     fn f() { ::baz::m!(); }
    |              ------------ in this macro invocation
 ...
index 64ddcb81c0a9be3e6fb609e9d31946d9ff2ac054..b993115502fd5f8cde39577ec156aa9a5e18824c 100644 (file)
@@ -37,6 +37,9 @@ LL | use no_method_suggested_traits::Reexported;
 error[E0599]: no method named `method` found for type `char` in the current scope
   --> $DIR/no-method-suggested-traits.rs:30:9
    |
+LL |         fn method(&self) {}
+   |            ------ the method is available for `char` here
+...
 LL |     'a'.method();
    |         ^^^^^^ method not found in `char`
    |
@@ -63,6 +66,11 @@ error[E0599]: no method named `method` found for type `i32` in the current scope
    |
 LL |     1i32.method();
    |          ^^^^^^ method not found in `i32`
+   | 
+  ::: $DIR/auxiliary/no_method_suggested_traits.rs:8:12
+   |
+LL |         fn method(&self) {}
+   |            ------ the method is available for `i32` here
    |
    = help: items from traits can only be used if the trait is in scope
 help: the following trait is implemented but not in scope; perhaps add a `use` for it:
index e637edadb0074b889091f91bbc44a4283b2fd226..386de88bc3d62cb26ec5ab56e3ecec6a1bf5be8b 100644 (file)
@@ -1,48 +1,22 @@
-// `#[macro_export] macro_rules` that doesn't originate from macro expansions can be placed
-// into the root module soon enough to act as usual items and shadow globs and preludes.
+// Crate-local macro expanded `macro_export` macros cannot be accessed with module-relative paths.
 
-#![feature(decl_macro)]
-
-// `macro_export` shadows globs
-use inner1::*;
-
-mod inner1 {
-    pub macro exported() {}
-}
-
-exported!();
-
-mod deep {
-    fn deep() {
-        type Deeper = [u8; {
-            #[macro_export]
-            macro_rules! exported {
-                () => ( struct Б; ) //~ ERROR non-ascii idents are not fully supported
-            }
-
-            0
-        }];
+macro_rules! define_exported { () => {
+    #[macro_export]
+    macro_rules! exported {
+        () => ()
     }
-}
+}}
 
-// `macro_export` shadows std prelude
-fn main() {
-    panic!();
-}
+define_exported!();
 
-mod inner3 {
-    #[macro_export]
-    macro_rules! panic {
-        () => ( struct Г; ) //~ ERROR non-ascii idents are not fully supported
-    }
+mod m {
+    use exported;
+    //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot
+    //~| WARN this was previously accepted
 }
 
-// `macro_export` shadows builtin macros
-include!();
-
-mod inner4 {
-    #[macro_export]
-    macro_rules! include {
-        () => ( struct Д; ) //~ ERROR non-ascii idents are not fully supported
-    }
+fn main() {
+    ::exported!();
+    //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot
+    //~| WARN this was previously accepted
 }
index 714c04add5f797a91bdc008bd888ce2588b1b64b..5cc6fa1e40a8844f5d1bba9b2bf0e90497a0ba8f 100644 (file)
@@ -1,42 +1,43 @@
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/local-modularized-tricky-fail-2.rs:20:32
+error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
+  --> $DIR/local-modularized-tricky-fail-2.rs:13:9
    |
-LL | exported!();
-   | ------------ in this macro invocation
-...
-LL |                 () => ( struct Б; )
-   |                                ^
+LL |     use exported;
+   |         ^^^^^^^^
    |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/local-modularized-tricky-fail-2.rs:36:24
+   = note: `#[deny(macro_expanded_macro_exports_accessed_by_absolute_paths)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
+note: the macro is defined here
+  --> $DIR/local-modularized-tricky-fail-2.rs:5:5
    |
-LL |     panic!();
-   |     --------- in this macro invocation
+LL | /     macro_rules! exported {
+LL | |         () => ()
+LL | |     }
+   | |_____^
 ...
-LL |         () => ( struct Г; )
-   |                        ^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
+LL |   define_exported!();
+   |   ------------------- in this macro invocation
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/local-modularized-tricky-fail-2.rs:46:24
+error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
+  --> $DIR/local-modularized-tricky-fail-2.rs:19:5
    |
-LL | include!();
-   | ----------- in this macro invocation
-...
-LL |         () => ( struct Д; )
-   |                        ^
+LL |     ::exported!();
+   |     ^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
+note: the macro is defined here
+  --> $DIR/local-modularized-tricky-fail-2.rs:5:5
    |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
+LL | /     macro_rules! exported {
+LL | |         () => ()
+LL | |     }
+   | |_____^
+...
+LL |   define_exported!();
+   |   ------------------- in this macro invocation
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/imports/local-modularized-tricky-fail-3.rs b/src/test/ui/imports/local-modularized-tricky-fail-3.rs
deleted file mode 100644 (file)
index 386de88..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-// Crate-local macro expanded `macro_export` macros cannot be accessed with module-relative paths.
-
-macro_rules! define_exported { () => {
-    #[macro_export]
-    macro_rules! exported {
-        () => ()
-    }
-}}
-
-define_exported!();
-
-mod m {
-    use exported;
-    //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot
-    //~| WARN this was previously accepted
-}
-
-fn main() {
-    ::exported!();
-    //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot
-    //~| WARN this was previously accepted
-}
diff --git a/src/test/ui/imports/local-modularized-tricky-fail-3.stderr b/src/test/ui/imports/local-modularized-tricky-fail-3.stderr
deleted file mode 100644 (file)
index 4494a88..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
-  --> $DIR/local-modularized-tricky-fail-3.rs:13:9
-   |
-LL |     use exported;
-   |         ^^^^^^^^
-   |
-   = note: `#[deny(macro_expanded_macro_exports_accessed_by_absolute_paths)]` on by default
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
-note: the macro is defined here
-  --> $DIR/local-modularized-tricky-fail-3.rs:5:5
-   |
-LL | /     macro_rules! exported {
-LL | |         () => ()
-LL | |     }
-   | |_____^
-...
-LL |   define_exported!();
-   |   ------------------- in this macro invocation
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
-  --> $DIR/local-modularized-tricky-fail-3.rs:19:5
-   |
-LL |     ::exported!();
-   |     ^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
-note: the macro is defined here
-  --> $DIR/local-modularized-tricky-fail-3.rs:5:5
-   |
-LL | /     macro_rules! exported {
-LL | |         () => ()
-LL | |     }
-   | |_____^
-...
-LL |   define_exported!();
-   |   ------------------- in this macro invocation
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 2 previous errors
-
diff --git a/src/test/ui/imports/local-modularized-tricky-pass-1.rs b/src/test/ui/imports/local-modularized-tricky-pass-1.rs
new file mode 100644 (file)
index 0000000..b52ddaf
--- /dev/null
@@ -0,0 +1,19 @@
+// build-pass (FIXME(62277): could be check-pass?)
+
+macro_rules! define_exported { () => {
+    #[macro_export]
+    macro_rules! exported {
+        () => ()
+    }
+}}
+
+mod inner1 {
+    use super::*;
+    exported!();
+}
+
+mod inner2 {
+    define_exported!();
+}
+
+fn main() {}
diff --git a/src/test/ui/imports/local-modularized-tricky-pass-2.rs b/src/test/ui/imports/local-modularized-tricky-pass-2.rs
new file mode 100644 (file)
index 0000000..d5efbdf
--- /dev/null
@@ -0,0 +1,50 @@
+// check-pass
+//
+// `#[macro_export] macro_rules` that doesn't originate from macro expansions can be placed
+// into the root module soon enough to act as usual items and shadow globs and preludes.
+
+#![feature(decl_macro)]
+
+// `macro_export` shadows globs
+use inner1::*;
+
+mod inner1 {
+    pub macro exported() {}
+}
+
+exported!();
+
+mod deep {
+    fn deep() {
+        type Deeper = [u8; {
+            #[macro_export]
+            macro_rules! exported {
+                () => ( struct Б; )
+            }
+
+            0
+        }];
+    }
+}
+
+// `macro_export` shadows std prelude
+fn main() {
+    panic!();
+}
+
+mod inner3 {
+    #[macro_export]
+    macro_rules! panic {
+        () => ( struct Г; )
+    }
+}
+
+// `macro_export` shadows builtin macros
+include!();
+
+mod inner4 {
+    #[macro_export]
+    macro_rules! include {
+        () => ( struct Д; )
+    }
+}
diff --git a/src/test/ui/imports/local-modularized-tricky-pass.rs b/src/test/ui/imports/local-modularized-tricky-pass.rs
deleted file mode 100644 (file)
index b52ddaf..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-// build-pass (FIXME(62277): could be check-pass?)
-
-macro_rules! define_exported { () => {
-    #[macro_export]
-    macro_rules! exported {
-        () => ()
-    }
-}}
-
-mod inner1 {
-    use super::*;
-    exported!();
-}
-
-mod inner2 {
-    define_exported!();
-}
-
-fn main() {}
diff --git a/src/test/ui/intrinsics/bad-intrinsic-monomorphization.rs b/src/test/ui/intrinsics/bad-intrinsic-monomorphization.rs
new file mode 100644 (file)
index 0000000..f36a5f1
--- /dev/null
@@ -0,0 +1,32 @@
+// build-fail
+
+#![feature(repr_simd, platform_intrinsics, core_intrinsics)]
+#![allow(warnings)]
+#![crate_type = "rlib"]
+
+// Bad monomorphizations could previously cause LLVM asserts even though the
+// error was caught in the compiler.
+
+extern "platform-intrinsic" {
+    fn simd_add<T>(x: T, y: T) -> T;
+}
+
+use std::intrinsics;
+
+#[derive(Copy, Clone)]
+pub struct Foo(i64);
+
+pub fn test_cttz(v: Foo) -> Foo {
+    intrinsics::cttz(v)
+    //~^ ERROR `cttz` intrinsic: expected basic integer type, found `Foo`
+}
+
+pub unsafe fn test_fadd_fast(a: Foo, b: Foo) -> Foo {
+    intrinsics::fadd_fast(a, b)
+    //~^ ERROR `fadd_fast` intrinsic: expected basic float type, found `Foo`
+}
+
+pub unsafe fn test_simd_add(a: Foo, b: Foo) -> Foo {
+    simd_add(a, b)
+    //~^ ERROR `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
+}
diff --git a/src/test/ui/intrinsics/bad-intrinsic-monomorphization.stderr b/src/test/ui/intrinsics/bad-intrinsic-monomorphization.stderr
new file mode 100644 (file)
index 0000000..c070f01
--- /dev/null
@@ -0,0 +1,21 @@
+error[E0511]: invalid monomorphization of `cttz` intrinsic: expected basic integer type, found `Foo`
+  --> $DIR/bad-intrinsic-monomorphization.rs:20:5
+   |
+LL |     intrinsics::cttz(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error[E0511]: invalid monomorphization of `fadd_fast` intrinsic: expected basic float type, found `Foo`
+  --> $DIR/bad-intrinsic-monomorphization.rs:25:5
+   |
+LL |     intrinsics::fadd_fast(a, b)
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0511]: invalid monomorphization of `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo`
+  --> $DIR/bad-intrinsic-monomorphization.rs:30:5
+   |
+LL |     simd_add(a, b)
+   |     ^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0511`.
index 2bf40db5ad290056272a6b50018d40dc02c8c3f0..592409ba89f9b5379571b3fae1450b26750625de 100644 (file)
@@ -1,7 +1,7 @@
 // run-pass
 // ignore-wasm32-bare seems not important to test here
 
-#![feature(intrinsics, main)]
+#![feature(intrinsics)]
 
 mod rusti {
     extern "rust-intrinsic" {
@@ -21,7 +21,6 @@ mod rusti {
           target_os = "solaris",
           target_os = "vxworks"))]
 mod m {
-    #[main]
     #[cfg(target_arch = "x86")]
     pub fn main() {
         unsafe {
@@ -30,7 +29,6 @@ pub fn main() {
         }
     }
 
-    #[main]
     #[cfg(not(target_arch = "x86"))]
     pub fn main() {
         unsafe {
@@ -42,7 +40,6 @@ pub fn main() {
 
 #[cfg(target_env = "sgx")]
 mod m {
-    #[main]
     #[cfg(target_arch = "x86_64")]
     pub fn main() {
         unsafe {
@@ -54,7 +51,6 @@ pub fn main() {
 
 #[cfg(target_os = "windows")]
 mod m {
-    #[main]
     pub fn main() {
         unsafe {
             assert_eq!(::rusti::pref_align_of::<u64>(), 8);
@@ -62,3 +58,7 @@ pub fn main() {
         }
     }
 }
+
+fn main() {
+    m::main();
+}
index 3f63cb8e78fbaf5cc8935c6be1a96411260f5604..3a3767c349d651a65d7dd877b67aa1198de0005d 100644 (file)
@@ -3,6 +3,11 @@ error[E0599]: no method named `a` found for unit type `()` in the current scope
    |
 LL |     ().a();
    |        ^ method not found in `()`
+   | 
+  ::: $DIR/auxiliary/xcrate-issue-43189-a.rs:5:8
+   |
+LL |     fn a(&self) {}
+   |        - the method is available for `()` here
    |
    = help: items from traits can only be used if the trait is in scope
 help: the following trait is implemented but not in scope; perhaps add a `use` for it:
index 4c38ddfcdf189152b199e2398610fb6ece32644f..e4320b7dac592c41f7e035e0eee544410314ef8b 100644 (file)
@@ -1,5 +1,3 @@
-#![feature(non_ascii_idents)]
-
 pub fn main () {}
 
 fn საჭმელად_გემრიელი_სადილი ( ) -> isize { //~ ERROR mismatched types
index fc54e7c62bb247d63f7e676b06207571d2a10743..9e97012416a94dcad1a33d3210de7d85f070e0b8 100644 (file)
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/issue-44023.rs:5:36
+  --> $DIR/issue-44023.rs:3:36
    |
 LL | fn საჭმელად_გემრიელი_სადილი ( ) -> isize {
    |    ------------------------        ^^^^^ expected `isize`, found `()`
index ee3f609f47dca9a8ccd0b27223c32688b77cb6c1..e6b0fffce663dae0044dc9299e3126767d43178a 100644 (file)
@@ -3,6 +3,11 @@ error[E0599]: no method named `trait_method` found for struct `FooStruct` in the
    |
 LL |     reexported_trait::FooStruct.trait_method();
    |                                 ^^^^^^^^^^^^ method not found in `FooStruct`
+   | 
+  ::: $DIR/auxiliary/reexported-trait.rs:3:12
+   |
+LL |         fn trait_method(&self) {
+   |            ------------ the method is available for `FooStruct` here
    |
    = help: items from traits can only be used if the trait is in scope
 help: the following trait is implemented but not in scope; perhaps add a `use` for it:
@@ -15,6 +20,11 @@ error[E0599]: no method named `trait_method_b` found for struct `FooStruct` in t
    |
 LL |     reexported_trait::FooStruct.trait_method_b();
    |                                 ^^^^^^^^^^^^^^ method not found in `FooStruct`
+   | 
+  ::: $DIR/auxiliary/reexported-trait.rs:7:12
+   |
+LL |         fn trait_method_b(&self) {
+   |            -------------- the method is available for `FooStruct` here
    |
    = help: items from traits can only be used if the trait is in scope
 help: the following trait is implemented but not in scope; perhaps add a `use` for it:
index 0de535023972e0d80869bc0c07e93a127f497618..82866b355573c5bdb2ce22023d862ff814150521 100644 (file)
@@ -4,9 +4,9 @@ struct Struct {
     r: dyn A + 'static
 }
 
-fn new_struct(r: dyn A + 'static)
-    -> Struct { //~^ ERROR the size for values of type
-    //~^ ERROR the size for values of type
+fn new_struct(
+    r: dyn A + 'static //~ ERROR the size for values of type
+) -> Struct {          //~ ERROR the size for values of type
     Struct { r: r }
 }
 
index 48879eb798f0615aee569de2292ad5f3710927fe..de598a70ee06ac471677d071481a6498bba08410 100644 (file)
@@ -1,22 +1,21 @@
 error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time
-  --> $DIR/issue-5883.rs:7:15
+  --> $DIR/issue-5883.rs:8:5
    |
-LL | fn new_struct(r: dyn A + 'static)
-   |               ^ doesn't have a size known at compile-time
+LL |     r: dyn A + 'static
+   |     ^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `(dyn A + 'static)`
    = help: unsized fn params are gated as an unstable feature
 help: function arguments must have a statically known size, borrowed types always have a known size
    |
-LL | fn new_struct(&r: dyn A + 'static)
-   |               ^
+LL |     r: &dyn A + 'static
+   |        ^
 
 error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time
-  --> $DIR/issue-5883.rs:8:8
+  --> $DIR/issue-5883.rs:9:6
    |
-LL |     -> Struct {
-   |        ^^^^^^ doesn't have a size known at compile-time
-LL |
+LL | ) -> Struct {
+   |      ^^^^^^ doesn't have a size known at compile-time
 LL |     Struct { r: r }
    |     --------------- this returned value is of type `Struct`
    |
index d2dfc7ec327d3a70d3460173fffd84a152d565d6..037540a3e89e629bad4e92d301ca767f77d04625 100644 (file)
@@ -1,23 +1,16 @@
+// check-pass
+
 fn main() {
     for _ in [0..1] {}
-//~^ ERROR is not an iterator
     for _ in [0..=1] {}
-//~^ ERROR is not an iterator
     for _ in [0..] {}
-//~^ ERROR is not an iterator
     for _ in [..1] {}
-//~^ ERROR is not an iterator
     for _ in [..=1] {}
-//~^ ERROR is not an iterator
     let start = 0;
     let end = 0;
     for _ in [start..end] {}
-//~^ ERROR is not an iterator
     let array_of_range = [start..end];
     for _ in array_of_range {}
-//~^ ERROR is not an iterator
     for _ in [0..1, 2..3] {}
-//~^ ERROR is not an iterator
     for _ in [0..=1] {}
-//~^ ERROR is not an iterator
 }
diff --git a/src/test/ui/iterators/array-of-ranges.stderr b/src/test/ui/iterators/array-of-ranges.stderr
deleted file mode 100644 (file)
index 7d58eb9..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:2:14
-   |
-LL |     for _ in [0..1] {}
-   |              ^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
-   = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:4:14
-   |
-LL |     for _ in [0..=1] {}
-   |              ^^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]`
-   = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeInclusive<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeFrom<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:6:14
-   |
-LL |     for _ in [0..] {}
-   |              ^^^^^ if you meant to iterate from a value onwards, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeFrom<{integer}>; 1]`
-   = note: `[start..]` is an array of one `RangeFrom`; you might have meant to have a `RangeFrom` without the brackets: `start..`, keeping in mind that iterating over an unbounded iterator will run forever unless you `break` or `return` from within the loop
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeFrom<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeTo<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:8:14
-   |
-LL |     for _ in [..1] {}
-   |              ^^^^^ if you meant to iterate until a value, remove the square brackets and add a starting value
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeTo<{integer}>; 1]`
-   = note: `[..end]` is an array of one `RangeTo`; you might have meant to have a bounded `Range` without the brackets: `0..end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeTo<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeToInclusive<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:10:14
-   |
-LL |     for _ in [..=1] {}
-   |              ^^^^^^ if you meant to iterate until a value (including it), remove the square brackets and add a starting value
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeToInclusive<{integer}>; 1]`
-   = note: `[..=end]` is an array of one `RangeToInclusive`; you might have meant to have a bounded `RangeInclusive` without the brackets: `0..=end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeToInclusive<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:14:14
-   |
-LL |     for _ in [start..end] {}
-   |              ^^^^^^^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
-   = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:17:14
-   |
-LL |     for _ in array_of_range {}
-   |              ^^^^^^^^^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
-   = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 2]` is not an iterator
-  --> $DIR/array-of-ranges.rs:19:14
-   |
-LL |     for _ in [0..1, 2..3] {}
-   |              ^^^^^^^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
-   |
-   = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 2]`
-   = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
-   = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 2]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:21:14
-   |
-LL |     for _ in [0..=1] {}
-   |              ^^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]`
-   = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeInclusive<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error: aborting due to 9 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
index 33c84f6fa3582ce1f7046b89d805269d5543798e..5985c74e11fdf313cf6b3b24d1ab2b635f395e4b 100644 (file)
@@ -1,9 +1,8 @@
+// check-pass
+
 fn main() {
     for _ in [1, 2] {}
-//~^ ERROR is not an iterator
     let x = [1, 2];
     for _ in x {}
-//~^ ERROR is not an iterator
     for _ in [1.0, 2.0] {}
-//~^ ERROR is not an iterator
 }
diff --git a/src/test/ui/iterators/array.stderr b/src/test/ui/iterators/array.stderr
deleted file mode 100644 (file)
index 7e2b600..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-error[E0277]: `[{integer}; 2]` is not an iterator
-  --> $DIR/array.rs:2:14
-   |
-LL |     for _ in [1, 2] {}
-   |              ^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
-   |
-   = help: the trait `Iterator` is not implemented for `[{integer}; 2]`
-   = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
-   = note: required because of the requirements on the impl of `IntoIterator` for `[{integer}; 2]`
-   = note: required by `into_iter`
-
-error[E0277]: `[{integer}; 2]` is not an iterator
-  --> $DIR/array.rs:5:14
-   |
-LL |     for _ in x {}
-   |              ^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
-   |
-   = help: the trait `Iterator` is not implemented for `[{integer}; 2]`
-   = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
-   = note: required because of the requirements on the impl of `IntoIterator` for `[{integer}; 2]`
-   = note: required by `into_iter`
-
-error[E0277]: `[{float}; 2]` is not an iterator
-  --> $DIR/array.rs:7:14
-   |
-LL |     for _ in [1.0, 2.0] {}
-   |              ^^^^^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
-   |
-   = help: the trait `Iterator` is not implemented for `[{float}; 2]`
-   = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
-   = note: required because of the requirements on the impl of `IntoIterator` for `[{float}; 2]`
-   = note: required by `into_iter`
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/iterators/into-iter-on-arrays-2018.rs b/src/test/ui/iterators/into-iter-on-arrays-2018.rs
new file mode 100644 (file)
index 0000000..5661397
--- /dev/null
@@ -0,0 +1,39 @@
+// check-pass
+// edition:2018
+
+use std::array::IntoIter;
+use std::ops::Deref;
+use std::rc::Rc;
+use std::slice::Iter;
+
+fn main() {
+    let array = [0; 10];
+
+    // Before 2021, the method dispatched to `IntoIterator for &[T; N]`,
+    // which we continue to support for compatibility.
+    let _: Iter<'_, i32> = array.into_iter();
+    //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
+    //~| WARNING this was previously accepted by the compiler but is being phased out
+
+    let _: Iter<'_, i32> = Box::new(array).into_iter();
+    //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
+    //~| WARNING this was previously accepted by the compiler but is being phased out
+
+    // The `array_into_iter` lint doesn't cover other wrappers that deref to an array.
+    let _: Iter<'_, i32> = Rc::new(array).into_iter();
+    let _: Iter<'_, i32> = Array(array).into_iter();
+
+    // But you can always use the trait method explicitly as an array.
+    let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);
+}
+
+/// User type that dereferences to an array.
+struct Array([i32; 10]);
+
+impl Deref for Array {
+    type Target = [i32; 10];
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
diff --git a/src/test/ui/iterators/into-iter-on-arrays-2018.stderr b/src/test/ui/iterators/into-iter-on-arrays-2018.stderr
new file mode 100644 (file)
index 0000000..b433383
--- /dev/null
@@ -0,0 +1,42 @@
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-2018.rs:14:34
+   |
+LL |     let _: Iter<'_, i32> = array.into_iter();
+   |                                  ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = note: `#[warn(array_into_iter)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-2018.rs:18:44
+   |
+LL |     let _: Iter<'_, i32> = Box::new(array).into_iter();
+   |                                            ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+warning: 2 warnings emitted
+
+Future incompatibility report: Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-2018.rs:14:34
+   |
+LL |     let _: Iter<'_, i32> = array.into_iter();
+   |                                  ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = note: `#[warn(array_into_iter)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-2018.rs:18:44
+   |
+LL |     let _: Iter<'_, i32> = Box::new(array).into_iter();
+   |                                            ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
diff --git a/src/test/ui/iterators/into-iter-on-arrays-2021.rs b/src/test/ui/iterators/into-iter-on-arrays-2021.rs
new file mode 100644 (file)
index 0000000..ec54ed0
--- /dev/null
@@ -0,0 +1,33 @@
+// check-pass
+// edition:2021
+// compile-flags: -Zunstable-options
+
+use std::array::IntoIter;
+use std::ops::Deref;
+use std::rc::Rc;
+
+fn main() {
+    let array = [0; 10];
+
+    // In 2021, the method dispatches to `IntoIterator for [T; N]`.
+    let _: IntoIter<i32, 10> = array.into_iter();
+    let _: IntoIter<i32, 10> = Box::new(array).into_iter();
+
+    // The `array_into_iter` lint doesn't cover other wrappers that deref to an array.
+    let _: IntoIter<i32, 10> = Rc::new(array).into_iter();
+    let _: IntoIter<i32, 10> = Array(array).into_iter();
+
+    // You can always use the trait method explicitly as an array.
+    let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);
+}
+
+/// User type that dereferences to an array.
+struct Array([i32; 10]);
+
+impl Deref for Array {
+    type Target = [i32; 10];
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
diff --git a/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs
new file mode 100644 (file)
index 0000000..7f0ea73
--- /dev/null
@@ -0,0 +1,8 @@
+// check-fail
+
+struct Foo {}
+impl Foo {
+    fn bar(foo: Foo<Target = usize>) {}
+    //~^ associated type bindings are not allowed here
+}
+fn main() {}
diff --git a/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr
new file mode 100644 (file)
index 0000000..f7bdee6
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0229]: associated type bindings are not allowed here
+  --> $DIR/issue-83753-invalid-associated-type-supertrait-hrtb.rs:5:21
+   |
+LL |     fn bar(foo: Foo<Target = usize>) {}
+   |                     ^^^^^^^^^^^^^^ associated type not allowed here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0229`.
diff --git a/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs
new file mode 100644 (file)
index 0000000..604687c
--- /dev/null
@@ -0,0 +1,7 @@
+// check-fail
+
+static STATIC_VAR_FIVE: &One();
+//~^ cannot find type
+//~| free static item without body
+
+fn main() {}
diff --git a/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr
new file mode 100644 (file)
index 0000000..e57933d
--- /dev/null
@@ -0,0 +1,17 @@
+error: free static item without body
+  --> $DIR/issue-83907-invalid-fn-like-path.rs:3:1
+   |
+LL | static STATIC_VAR_FIVE: &One();
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+   |                               |
+   |                               help: provide a definition for the static: `= <expr>;`
+
+error[E0412]: cannot find type `One` in this scope
+  --> $DIR/issue-83907-invalid-fn-like-path.rs:3:26
+   |
+LL | static STATIC_VAR_FIVE: &One();
+   |                          ^^^ not found in this scope
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0412`.
index 5751c3194894e228583bcfed231c11d52877ddf3..bbd62902d9f44c01a4d945863cfeff0532352000 100644 (file)
@@ -12,7 +12,7 @@ error[E0384]: cannot assign to immutable argument `y`
   --> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:4:5
    |
 LL | fn foo(mut x: Ref, y: &u32) {
-   |                    - help: make this binding mutable: `mut y`
+   |                    - help: consider making this binding mutable: `mut y`
 LL |     y = x.b;
    |     ^^^^^^^ cannot assign to immutable argument
 
index c646912d3b67950583d857964329692d36846745..b47a47d631e5f35a7d7edee6c3a513c9a7d72c29 100644 (file)
@@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `x`
   --> $DIR/liveness-assign-imm-local-notes.rs:10:9
    |
 LL |     let x;
-   |         - help: make this binding mutable: `mut x`
+   |         - help: consider making this binding mutable: `mut x`
 ...
 LL |         x = 2;
    |         ----- first assignment to `x`
@@ -13,7 +13,7 @@ error[E0384]: cannot assign twice to immutable variable `x`
   --> $DIR/liveness-assign-imm-local-notes.rs:21:13
    |
 LL |         let x;
-   |             - help: make this binding mutable: `mut x`
+   |             - help: consider making this binding mutable: `mut x`
 ...
 LL |             x = 2;
    |             ----- first assignment to `x`
@@ -24,7 +24,7 @@ error[E0384]: cannot assign twice to immutable variable `x`
   --> $DIR/liveness-assign-imm-local-notes.rs:30:13
    |
 LL |     let x;
-   |         - help: make this binding mutable: `mut x`
+   |         - help: consider making this binding mutable: `mut x`
 ...
 LL |             x = 1;
    |             ^^^^^ cannot assign twice to immutable variable
@@ -33,7 +33,7 @@ error[E0384]: cannot assign twice to immutable variable `x`
   --> $DIR/liveness-assign-imm-local-notes.rs:32:13
    |
 LL |     let x;
-   |         - help: make this binding mutable: `mut x`
+   |         - help: consider making this binding mutable: `mut x`
 ...
 LL |             x = 1;
    |             ----- first assignment to `x`
diff --git a/src/test/ui/linkage-attr/bad-extern-link-attrs.rs b/src/test/ui/linkage-attr/bad-extern-link-attrs.rs
new file mode 100644 (file)
index 0000000..43fe8c1
--- /dev/null
@@ -0,0 +1,7 @@
+#[link()] //~ ERROR: specified without `name =
+#[link(name = "")] //~ ERROR: with empty name
+#[link(name = "foo")]
+#[link(name = "foo", kind = "bar")] //~ ERROR: unknown kind
+extern "C" {}
+
+fn main() {}
diff --git a/src/test/ui/linkage-attr/bad-extern-link-attrs.stderr b/src/test/ui/linkage-attr/bad-extern-link-attrs.stderr
new file mode 100644 (file)
index 0000000..525c605
--- /dev/null
@@ -0,0 +1,24 @@
+error[E0459]: `#[link(...)]` specified without `name = "foo"`
+  --> $DIR/bad-extern-link-attrs.rs:1:1
+   |
+LL | #[link()]
+   | ^^^^^^^^^ missing `name` argument
+
+error[E0454]: `#[link(name = "")]` given with empty name
+  --> $DIR/bad-extern-link-attrs.rs:2:1
+   |
+LL | #[link(name = "")]
+   | ^^^^^^^^^^^^^^^^^^ empty name given
+
+error[E0458]: unknown kind: `bar`
+  --> $DIR/bad-extern-link-attrs.rs:4:22
+   |
+LL | #[link(name = "foo", kind = "bar")]
+   | ---------------------^^^^^^^^^^^^--
+   |                      |
+   |                      unknown kind
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0454, E0458, E0459.
+For more information about an error, try `rustc --explain E0454`.
diff --git a/src/test/ui/lint/bad-lint-cap.rs b/src/test/ui/lint/bad-lint-cap.rs
new file mode 100644 (file)
index 0000000..e65c831
--- /dev/null
@@ -0,0 +1,4 @@
+// compile-flags: --cap-lints test
+// error-pattern: unknown lint level: `test`
+
+fn main() {}
diff --git a/src/test/ui/lint/bad-lint-cap.stderr b/src/test/ui/lint/bad-lint-cap.stderr
new file mode 100644 (file)
index 0000000..f284dbf
--- /dev/null
@@ -0,0 +1,2 @@
+error: unknown lint level: `test`
+
diff --git a/src/test/ui/lint/bad-lint-cap2.rs b/src/test/ui/lint/bad-lint-cap2.rs
new file mode 100644 (file)
index 0000000..8bc8aca
--- /dev/null
@@ -0,0 +1,8 @@
+// compile-flags: --cap-lints deny
+
+#![warn(unused)]
+#![deny(warnings)]
+
+use std::option; //~ ERROR
+
+fn main() {}
diff --git a/src/test/ui/lint/bad-lint-cap2.stderr b/src/test/ui/lint/bad-lint-cap2.stderr
new file mode 100644 (file)
index 0000000..3f3affe
--- /dev/null
@@ -0,0 +1,15 @@
+error: unused import: `std::option`
+  --> $DIR/bad-lint-cap2.rs:6:5
+   |
+LL | use std::option;
+   |     ^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/bad-lint-cap2.rs:4:9
+   |
+LL | #![deny(warnings)]
+   |         ^^^^^^^^
+   = note: `#[deny(unused_imports)]` implied by `#[deny(warnings)]`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lint/bad-lint-cap3.rs b/src/test/ui/lint/bad-lint-cap3.rs
new file mode 100644 (file)
index 0000000..c381058
--- /dev/null
@@ -0,0 +1,9 @@
+// check-pass
+// compile-flags: --cap-lints warn
+
+#![warn(unused)]
+#![deny(warnings)]
+
+use std::option; //~ WARN
+
+fn main() {}
diff --git a/src/test/ui/lint/bad-lint-cap3.stderr b/src/test/ui/lint/bad-lint-cap3.stderr
new file mode 100644 (file)
index 0000000..0fb6532
--- /dev/null
@@ -0,0 +1,15 @@
+warning: unused import: `std::option`
+  --> $DIR/bad-lint-cap3.rs:7:5
+   |
+LL | use std::option;
+   |     ^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/bad-lint-cap3.rs:5:9
+   |
+LL | #![deny(warnings)]
+   |         ^^^^^^^^
+   = note: `#[warn(unused_imports)]` implied by `#[warn(warnings)]`
+
+warning: 1 warning emitted
+
index 88db4f88c3feb8ff0890df2b0e2c6f1aa2eab26f..2cfe6e539dbf4fb92bbea103682d4a56eee64a4b 100644 (file)
@@ -1,6 +1,6 @@
 #![allow(unused_variables)]
 #![deny(dead_code)]
-#![feature(main, start)]
+#![feature(rustc_attrs, start)]
 
 struct Foo;
 
@@ -21,7 +21,7 @@ fn live_fn() {}
 
 fn dead_fn() {} //~ ERROR: function is never used
 
-#[main]
+#[rustc_main]
 fn dead_fn2() {} //~ ERROR: function is never used
 
 fn used_fn() {}
diff --git a/src/test/ui/lint/issue-83477.rs b/src/test/ui/lint/issue-83477.rs
new file mode 100644 (file)
index 0000000..0eba52a
--- /dev/null
@@ -0,0 +1,16 @@
+// check-pass
+#![warn(rustc::internal)]
+
+#[allow(rustc::foo::bar::default_hash_types)]
+//~^ WARN unknown lint: `rustc::foo::bar::default_hash_types`
+//~| HELP did you mean
+//~| SUGGESTION rustc::default_hash_types
+#[allow(rustc::foo::default_hash_types)]
+//~^ WARN unknown lint: `rustc::foo::default_hash_types`
+//~| HELP did you mean
+//~| SUGGESTION rustc::default_hash_types
+fn main() {
+    let _ = std::collections::HashMap::<String, String>::new();
+    //~^ WARN Prefer FxHashMap over HashMap, it has better performance
+    //~| HELP use
+}
diff --git a/src/test/ui/lint/issue-83477.stderr b/src/test/ui/lint/issue-83477.stderr
new file mode 100644 (file)
index 0000000..dbe0c9e
--- /dev/null
@@ -0,0 +1,30 @@
+warning: unknown lint: `rustc::foo::bar::default_hash_types`
+  --> $DIR/issue-83477.rs:4:9
+   |
+LL | #[allow(rustc::foo::bar::default_hash_types)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `rustc::default_hash_types`
+   |
+   = note: `#[warn(unknown_lints)]` on by default
+
+warning: unknown lint: `rustc::foo::default_hash_types`
+  --> $DIR/issue-83477.rs:8:9
+   |
+LL | #[allow(rustc::foo::default_hash_types)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `rustc::default_hash_types`
+
+warning: Prefer FxHashMap over HashMap, it has better performance
+  --> $DIR/issue-83477.rs:13:31
+   |
+LL |     let _ = std::collections::HashMap::<String, String>::new();
+   |                               ^^^^^^^ help: use: `FxHashMap`
+   |
+note: the lint level is defined here
+  --> $DIR/issue-83477.rs:2:9
+   |
+LL | #![warn(rustc::internal)]
+   |         ^^^^^^^^^^^^^^^
+   = note: `#[warn(rustc::default_hash_types)]` implied by `#[warn(rustc::internal)]`
+   = note: a `use rustc_data_structures::fx::FxHashMap` may be necessary
+
+warning: 3 warnings emitted
+
index 710eebe4b6525edca7d6c9d4abcd37bd5906639b..9f0c87dcaa61c74d23c8fb70d67339d806017faf 100644 (file)
@@ -3,7 +3,6 @@
 #![allow(dead_code)]
 // pretty-expanded FIXME #23616
 
-#![feature(non_ascii_idents)]
 #![deny(non_snake_case)]
 
 // This name is neither upper nor lower case
index 034499145b78051dc57619c7732dce70d79692b9..527d0ea943285ada57066bb4056fc7c230d21490 100644 (file)
@@ -1,7 +1,6 @@
 #![allow(dead_code)]
 
 #![forbid(non_camel_case_types)]
-#![feature(non_ascii_idents)]
 
 // Some scripts (e.g., hiragana) don't have a concept of
 // upper/lowercase
index 371002656591c133a642f5678d7df7c20c3cbbc1..6c2aa225e602e942d49e7e61293e28330ccdfc0f 100644 (file)
@@ -1,5 +1,5 @@
 error: type `χa` should have an upper camel case name
-  --> $DIR/lint-nonstandard-style-unicode-1.rs:15:8
+  --> $DIR/lint-nonstandard-style-unicode-1.rs:14:8
    |
 LL | struct χa;
    |        ^^ help: convert the identifier to upper camel case: `Χa`
@@ -11,37 +11,37 @@ LL | #![forbid(non_camel_case_types)]
    |           ^^^^^^^^^^^^^^^^^^^^
 
 error: type `__χa` should have an upper camel case name
-  --> $DIR/lint-nonstandard-style-unicode-1.rs:23:8
+  --> $DIR/lint-nonstandard-style-unicode-1.rs:22:8
    |
 LL | struct __χa;
    |        ^^^^ help: convert the identifier to upper camel case: `Χa`
 
 error: type `对__否` should have an upper camel case name
-  --> $DIR/lint-nonstandard-style-unicode-1.rs:28:8
+  --> $DIR/lint-nonstandard-style-unicode-1.rs:27:8
    |
 LL | struct 对__否;
    |        ^^^^^^ help: convert the identifier to upper camel case: `对_否`
 
 error: type `ヒ__χ` should have an upper camel case name
-  --> $DIR/lint-nonstandard-style-unicode-1.rs:31:8
+  --> $DIR/lint-nonstandard-style-unicode-1.rs:30:8
    |
 LL | struct ヒ__χ;
    |        ^^^^^ help: convert the identifier to upper camel case: `ヒΧ`
 
 error: type `Hello_你好` should have an upper camel case name
-  --> $DIR/lint-nonstandard-style-unicode-1.rs:37:8
+  --> $DIR/lint-nonstandard-style-unicode-1.rs:36:8
    |
 LL | struct Hello_你好;
    |        ^^^^^^^^^^ help: convert the identifier to upper camel case: `Hello你好`
 
 error: type `Hello_World` should have an upper camel case name
-  --> $DIR/lint-nonstandard-style-unicode-1.rs:40:8
+  --> $DIR/lint-nonstandard-style-unicode-1.rs:39:8
    |
 LL | struct Hello_World;
    |        ^^^^^^^^^^^ help: convert the identifier to upper camel case: `HelloWorld`
 
 error: type `你_ӟ` should have an upper camel case name
-  --> $DIR/lint-nonstandard-style-unicode-1.rs:43:8
+  --> $DIR/lint-nonstandard-style-unicode-1.rs:42:8
    |
 LL | struct 你_ӟ;
    |        ^^^^ help: convert the identifier to upper camel case: `你Ӟ`
index 0b52a5fde35dc8bb96108c89aad2cb7962a04f90..9690be5908b7376499a2a0d275ac02d846693849 100644 (file)
@@ -1,7 +1,6 @@
 #![allow(dead_code)]
 
 #![forbid(non_snake_case)]
-#![feature(non_ascii_idents)]
 
 // Some scripts (e.g., hiragana) don't have a concept of
 // upper/lowercase
index 0b309e315a4117dca42714e5fe5f8fa3b1d42f23..8eb0654e0a1933039e769de68b266fc571629e77 100644 (file)
@@ -1,5 +1,5 @@
 error: function `Ц` should have a snake case name
-  --> $DIR/lint-nonstandard-style-unicode-2.rs:18:4
+  --> $DIR/lint-nonstandard-style-unicode-2.rs:17:4
    |
 LL | fn Ц() {}
    |    ^ help: convert the identifier to snake case: `ц`
@@ -11,7 +11,7 @@ LL | #![forbid(non_snake_case)]
    |           ^^^^^^^^^^^^^^
 
 error: function `分__隔` should have a snake case name
-  --> $DIR/lint-nonstandard-style-unicode-2.rs:23:4
+  --> $DIR/lint-nonstandard-style-unicode-2.rs:22:4
    |
 LL | fn 分__隔() {}
    |    ^^^^^^ help: convert the identifier to snake case: `分_隔`
index b17c2de39a0c0e7b1963d50e6580a4a8b4f9e8f5..9175be7a0f49d4f1046b3af86437c31ba6ea3c62 100644 (file)
@@ -1,7 +1,6 @@
 #![allow(dead_code)]
 
 #![forbid(non_upper_case_globals)]
-#![feature(non_ascii_idents)]
 
 // Some scripts (e.g., hiragana) don't have a concept of
 // upper/lowercase
index 44bd5ad55ff5c8c702802d39a4a98bb69354eb89..970e6b838ada00eec7d852775851992e6b82f92f 100644 (file)
@@ -1,5 +1,5 @@
 error: static variable `τεχ` should have an upper case name
-  --> $DIR/lint-nonstandard-style-unicode-3.rs:18:8
+  --> $DIR/lint-nonstandard-style-unicode-3.rs:17:8
    |
 LL | static τεχ: f32 = 3.14159265;
    |        ^^^ help: convert the identifier to upper case: `ΤΕΧ`
index 2c711f994043f6f0b6a7e5814cdd6ce46d770b20..e7da825ae36d1587a151ff69833f9f4921568930 100644 (file)
@@ -1,4 +1,3 @@
-#![feature(non_ascii_idents)]
 #![deny(confusable_idents)]
 #![allow(uncommon_codepoints, non_upper_case_globals)]
 
index b9af60963adf6893c65f558a11dfc6b69a117fc6..e9906c83d126c1efffb28568aa074d76a8e6d2c9 100644 (file)
@@ -1,5 +1,5 @@
 error: identifier pair considered confusable between `s` and `s`
-  --> $DIR/lint-confusable-idents.rs:9:9
+  --> $DIR/lint-confusable-idents.rs:8:9
    |
 LL | const s: usize = 42;
    |       -- this is where the previous identifier occurred
@@ -8,13 +8,13 @@ LL |     let s = "rust";
    |         ^
    |
 note: the lint level is defined here
-  --> $DIR/lint-confusable-idents.rs:2:9
+  --> $DIR/lint-confusable-idents.rs:1:9
    |
 LL | #![deny(confusable_idents)]
    |         ^^^^^^^^^^^^^^^^^
 
 error: identifier pair considered confusable between `s_s` and `s_s`
-  --> $DIR/lint-confusable-idents.rs:10:9
+  --> $DIR/lint-confusable-idents.rs:9:9
    |
 LL | const s_s: usize = 42;
    |       --- this is where the previous identifier occurred
index a5b45466da5ca5c12ca970d1bc7d6258ae6bff1d..f62c8a19031dc01c0aa9255afe744d37af051208 100644 (file)
@@ -1,5 +1,4 @@
 // check-pass
-#![feature(non_ascii_idents)]
 #![deny(mixed_script_confusables)]
 
 struct ΑctuallyNotLatin;
index 4637b03f250de14f7336fdf8b3cd0ca04a6c310b..7ee9c41f6a09184b50f64445b1a5f0dce193cf82 100644 (file)
@@ -1,4 +1,3 @@
-#![feature(non_ascii_idents)]
 #![deny(mixed_script_confusables)]
 
 struct ΑctuallyNotLatin;
index 6f75a1ece37662769c292d2a9f544e7c9cd65867..4018b381fb8c5ba37f2120d0f2b58a2c54e64d78 100644 (file)
@@ -1,11 +1,11 @@
 error: The usage of Script Group `Greek` in this crate consists solely of mixed script confusables
-  --> $DIR/lint-mixed-script-confusables.rs:4:8
+  --> $DIR/lint-mixed-script-confusables.rs:3:8
    |
 LL | struct ΑctuallyNotLatin;
    |        ^^^^^^^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/lint-mixed-script-confusables.rs:2:9
+  --> $DIR/lint-mixed-script-confusables.rs:1:9
    |
 LL | #![deny(mixed_script_confusables)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL | #![deny(mixed_script_confusables)]
    = note: Please recheck to make sure their usages are indeed what you want.
 
 error: The usage of Script Group `Cyrillic` in this crate consists solely of mixed script confusables
-  --> $DIR/lint-mixed-script-confusables.rs:11:5
+  --> $DIR/lint-mixed-script-confusables.rs:10:5
    |
 LL | mod роре {
    |     ^^^^
@@ -22,7 +22,7 @@ LL | mod роре {
    = note: Please recheck to make sure their usages are indeed what you want.
 
 error: The usage of Script Group `Japanese, Katakana` in this crate consists solely of mixed script confusables
-  --> $DIR/lint-mixed-script-confusables.rs:13:11
+  --> $DIR/lint-mixed-script-confusables.rs:12:11
    |
 LL |     const エ: &'static str = "アイウ";
    |           ^^
index 20d00cf701a1569fe5f33ef0b0d0fdbcdc3900a1..8ae1744096dded8021b9e245a03391b1b886c406 100644 (file)
@@ -1,4 +1,3 @@
-#![feature(non_ascii_idents)]
 #![deny(non_ascii_idents)]
 
 const חלודה: usize = 2; //~ ERROR identifier contains non-ASCII characters
index 048b6ff5d687f011dac0ac01991bf589e4821f70..8ed7f093ce340a86ae2aa893d580635d145ceb5d 100644 (file)
@@ -1,23 +1,23 @@
 error: identifier contains non-ASCII characters
-  --> $DIR/lint-non-ascii-idents.rs:4:7
+  --> $DIR/lint-non-ascii-idents.rs:3:7
    |
 LL | const חלודה: usize = 2;
    |       ^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/lint-non-ascii-idents.rs:2:9
+  --> $DIR/lint-non-ascii-idents.rs:1:9
    |
 LL | #![deny(non_ascii_idents)]
    |         ^^^^^^^^^^^^^^^^
 
 error: identifier contains non-ASCII characters
-  --> $DIR/lint-non-ascii-idents.rs:6:4
+  --> $DIR/lint-non-ascii-idents.rs:5:4
    |
 LL | fn coöperation() {}
    |    ^^^^^^^^^^^
 
 error: identifier contains non-ASCII characters
-  --> $DIR/lint-non-ascii-idents.rs:9:9
+  --> $DIR/lint-non-ascii-idents.rs:8:9
    |
 LL |     let naïveté = 2;
    |         ^^^^^^^
index b5e251e047b5ad1f8ca37054603d4b1fb9577185..81a3427a10274b223cd68fe389c6bd2c74696821 100644 (file)
@@ -1,4 +1,3 @@
-#![feature(non_ascii_idents)]
 #![deny(uncommon_codepoints)]
 
 const µ: f64 = 0.000001; //~ ERROR identifier contains uncommon Unicode codepoints
index 05ea3d5de7dbc9759ec0482fd006d8fbad544a7c..d435282a6e855cca8c9b571bb9d59113da71e33d 100644 (file)
@@ -1,23 +1,23 @@
 error: identifier contains uncommon Unicode codepoints
-  --> $DIR/lint-uncommon-codepoints.rs:4:7
+  --> $DIR/lint-uncommon-codepoints.rs:3:7
    |
 LL | const µ: f64 = 0.000001;
    |       ^
    |
 note: the lint level is defined here
-  --> $DIR/lint-uncommon-codepoints.rs:2:9
+  --> $DIR/lint-uncommon-codepoints.rs:1:9
    |
 LL | #![deny(uncommon_codepoints)]
    |         ^^^^^^^^^^^^^^^^^^^
 
 error: identifier contains uncommon Unicode codepoints
-  --> $DIR/lint-uncommon-codepoints.rs:6:4
+  --> $DIR/lint-uncommon-codepoints.rs:5:4
    |
 LL | fn dijkstra() {}
    |    ^^^^^^^
 
 error: identifier contains uncommon Unicode codepoints
-  --> $DIR/lint-uncommon-codepoints.rs:9:9
+  --> $DIR/lint-uncommon-codepoints.rs:8:9
    |
 LL |     let ㇻㇲㇳ = "rust";
    |         ^^^^^^
index 71ebf05dd31abefac1404c4556372bb7c1d686de..761be61fa07f64ae823de69faad3186bea5068a7 100644 (file)
@@ -5,7 +5,6 @@
 
 // check-pass
 
-#![feature(non_ascii_idents)]
 #![allow(uncommon_codepoints, unused)]
 
 struct 𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝;
index e3b451a15a2cc4a50508507b2aac80602489eb8b..2aa13c33be3a6478f153bc73e0d2c60607f92350 100644 (file)
@@ -1,5 +1,5 @@
 warning: type `𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝` should have an upper camel case name
-  --> $DIR/special-upper-lower-cases.rs:11:8
+  --> $DIR/special-upper-lower-cases.rs:10:8
    |
 LL | struct 𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝;
    |        ^^^^^^^^^ should have an UpperCamelCase name
@@ -7,13 +7,13 @@ LL | struct 𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝;
    = note: `#[warn(non_camel_case_types)]` on by default
 
 warning: type `𝕟𝕠𝕥_𝕒_𝕔𝕒𝕞𝕖𝕝` should have an upper camel case name
-  --> $DIR/special-upper-lower-cases.rs:15:8
+  --> $DIR/special-upper-lower-cases.rs:14:8
    |
 LL | struct 𝕟𝕠𝕥_𝕒_𝕔𝕒𝕞𝕖𝕝;
    |        ^^^^^^^^^^^ should have an UpperCamelCase name
 
 warning: static variable `𝗻𝗼𝗻𝘂𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲` should have an upper case name
-  --> $DIR/special-upper-lower-cases.rs:18:8
+  --> $DIR/special-upper-lower-cases.rs:17:8
    |
 LL | static 𝗻𝗼𝗻𝘂𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲: i32 = 1;
    |        ^^^^^^^^^^^^ should have an UPPER_CASE name
@@ -21,7 +21,7 @@ LL | static 𝗻𝗼𝗻𝘂𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲: i32 = 1;
    = note: `#[warn(non_upper_case_globals)]` on by default
 
 warning: variable `𝓢𝓝𝓐𝓐𝓐𝓐𝓚𝓔𝓢` should have a snake case name
-  --> $DIR/special-upper-lower-cases.rs:22:9
+  --> $DIR/special-upper-lower-cases.rs:21:9
    |
 LL |     let 𝓢𝓝𝓐𝓐𝓐𝓐𝓚𝓔𝓢 = 1;
    |         ^^^^^^^^^ should have a snake_case name
index c9e1851b9a9803b548e43aadd9e5b3ebdded8301..08911c5bde781f2601e946698c16433b14de9569 100644 (file)
@@ -1,6 +1,6 @@
 fn test() {
     let v: isize;
-    //~^ HELP make this binding mutable
+    //~^ HELP consider making this binding mutable
     //~| SUGGESTION mut v
     loop {
         v = 1; //~ ERROR cannot assign twice to immutable variable `v`
index 69dff734ee4be0d64e0413db2e611383dc1bc197..66cdce7dacf834a4797c4f9290b2c09f5db18bcc 100644 (file)
@@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v`
   --> $DIR/liveness-assign-imm-local-in-loop.rs:6:9
    |
 LL |     let v: isize;
-   |         - help: make this binding mutable: `mut v`
+   |         - help: consider making this binding mutable: `mut v`
 ...
 LL |         v = 1;
    |         ^^^^^ cannot assign twice to immutable variable
index f24f7d2bcfc5271456f8438dae49df6159fee38f..1752d969086e897e6c0a41cf0c2f24ac84cc4558 100644 (file)
@@ -1,6 +1,6 @@
 fn test() {
     let v: isize;
-    //~^ HELP make this binding mutable
+    //~^ HELP consider making this binding mutable
     //~| SUGGESTION mut v
     v = 2;  //~ NOTE first assignment
     v += 1; //~ ERROR cannot assign twice to immutable variable `v`
index 182958dd492441d7ae88e87043a64afbe0a806f2..5db9539cbf1e5d932de8efce614fa4fb38664c3f 100644 (file)
@@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v`
   --> $DIR/liveness-assign-imm-local-in-op-eq.rs:6:5
    |
 LL |     let v: isize;
-   |         - help: make this binding mutable: `mut v`
+   |         - help: consider making this binding mutable: `mut v`
 ...
 LL |     v = 2;
    |     ----- first assignment to `v`
index 8963e32717e2295a7b6e0f1a2e833ba5333c9e5f..c9b16e43910e8e9f6215acc0ed4ed4b3ecca2c64 100644 (file)
@@ -1,6 +1,6 @@
 fn test() {
     let b = Box::new(1); //~ NOTE first assignment
-                         //~| HELP make this binding mutable
+                         //~| HELP consider making this binding mutable
                          //~| SUGGESTION mut b
     drop(b);
     b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b`
index 7c4af624b27353331bd4e11c037eade35c314a64..bb7e7e27a4ca004cae25829263f487619805562d 100644 (file)
@@ -5,7 +5,7 @@ LL |     let b = Box::new(1);
    |         -
    |         |
    |         first assignment to `b`
-   |         help: make this binding mutable: `mut b`
+   |         help: consider making this binding mutable: `mut b`
 ...
 LL |     b = Box::new(2);
    |     ^ cannot assign twice to immutable variable
index 4ab222af8d088bba6d8469d2cd610a304ffbb3b5..4bb2db27a165254332a01d35049c52a060bb7aba 100644 (file)
@@ -1,6 +1,6 @@
 fn test() {
     let v: isize = 1; //~ NOTE first assignment
-                      //~| HELP make this binding mutable
+                      //~| HELP consider making this binding mutable
                       //~| SUGGESTION mut v
     v.clone();
     v = 2; //~ ERROR cannot assign twice to immutable variable `v`
index 6f5d5574877c90916a470fdd6e9b19908cf7a8a6..80458a70a0107b0bba94e7eb1ff17c72411dc9db 100644 (file)
@@ -5,7 +5,7 @@ LL |     let v: isize = 1;
    |         -
    |         |
    |         first assignment to `v`
-   |         help: make this binding mutable: `mut v`
+   |         help: consider making this binding mutable: `mut v`
 ...
 LL |     v = 2;
    |     ^^^^^ cannot assign twice to immutable variable
index 9b0aa6be1e91ea99e0917a93fa02a18a76a5173a..3e5893f68b6c19219896a67a2a09eaa37d50a5ea 100644 (file)
@@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `x`
   --> $DIR/llvm-asm-out-assign-imm.rs:25:39
    |
 LL |     let x: isize;
-   |         - help: make this binding mutable: `mut x`
+   |         - help: consider making this binding mutable: `mut x`
 LL |     x = 1;
    |     ----- first assignment to `x`
 ...
diff --git a/src/test/ui/loops/loop-no-implicit-break.rs b/src/test/ui/loops/loop-no-implicit-break.rs
new file mode 100644 (file)
index 0000000..93078cb
--- /dev/null
@@ -0,0 +1,31 @@
+fn main() {
+    let a: i8 = loop {
+        1 //~ ERROR mismatched types
+    };
+
+    let b: i8 = loop {
+        break 1;
+    };
+}
+
+fn foo() -> i8 {
+    let a: i8 = loop {
+        1 //~ ERROR mismatched types
+    };
+
+    let b: i8 = loop {
+        break 1;
+    };
+
+    loop {
+        1 //~ ERROR mismatched types
+    }
+
+    loop {
+        return 1;
+    }
+
+    loop {
+        1 //~ ERROR mismatched types
+    }
+}
diff --git a/src/test/ui/loops/loop-no-implicit-break.stderr b/src/test/ui/loops/loop-no-implicit-break.stderr
new file mode 100644 (file)
index 0000000..5087662
--- /dev/null
@@ -0,0 +1,47 @@
+error[E0308]: mismatched types
+  --> $DIR/loop-no-implicit-break.rs:3:9
+   |
+LL |         1
+   |         ^ expected `()`, found integer
+   |
+help: you might have meant to break the loop with this value
+   |
+LL |         break 1;
+   |         ^^^^^  ^
+
+error[E0308]: mismatched types
+  --> $DIR/loop-no-implicit-break.rs:13:9
+   |
+LL |         1
+   |         ^ expected `()`, found integer
+   |
+help: you might have meant to break the loop with this value
+   |
+LL |         break 1;
+   |         ^^^^^  ^
+
+error[E0308]: mismatched types
+  --> $DIR/loop-no-implicit-break.rs:21:9
+   |
+LL |         1
+   |         ^ expected `()`, found integer
+   |
+help: you might have meant to return this value
+   |
+LL |         return 1;
+   |         ^^^^^^  ^
+
+error[E0308]: mismatched types
+  --> $DIR/loop-no-implicit-break.rs:29:9
+   |
+LL |         1
+   |         ^ expected `()`, found integer
+   |
+help: you might have meant to return this value
+   |
+LL |         return 1;
+   |         ^^^^^^  ^
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/macros/issue-84195-lint-anon-const.rs b/src/test/ui/macros/issue-84195-lint-anon-const.rs
new file mode 100644 (file)
index 0000000..71c7683
--- /dev/null
@@ -0,0 +1,14 @@
+// Regression test for issue #84195
+// Checks that we properly fire lints that occur inside
+// anon consts.
+
+#![deny(semicolon_in_expressions_from_macros)]
+
+macro_rules! len {
+    () => { 0; }; //~  ERROR trailing semicolon
+                  //~| WARN this was previously accepted
+}
+
+fn main() {
+    let val: [u8; len!()] = [];
+}
diff --git a/src/test/ui/macros/issue-84195-lint-anon-const.stderr b/src/test/ui/macros/issue-84195-lint-anon-const.stderr
new file mode 100644 (file)
index 0000000..558e034
--- /dev/null
@@ -0,0 +1,20 @@
+error: trailing semicolon in macro used in expression position
+  --> $DIR/issue-84195-lint-anon-const.rs:8:14
+   |
+LL |     () => { 0; };
+   |              ^
+...
+LL |     let val: [u8; len!()] = [];
+   |                   ------ in this macro invocation
+   |
+note: the lint level is defined here
+  --> $DIR/issue-84195-lint-anon-const.rs:5:9
+   |
+LL | #![deny(semicolon_in_expressions_from_macros)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/macros/macro-export-on-modularized-macros.rs b/src/test/ui/macros/macro-export-on-modularized-macros.rs
deleted file mode 100644 (file)
index 467c6ba..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#![feature(decl_macro)]
-#![feature(pub_macro_rules)]
-
-#[macro_export]
-macro m1() {} //~ ERROR `#[macro_export]` cannot be used on `macro` items
-
-#[macro_export]
-pub macro_rules! m2 { () => {} }
-//~^ ERROR `#[macro_export]` cannot be used on `macro_rules` with `pub`
-
-fn main() {}
diff --git a/src/test/ui/macros/macro-export-on-modularized-macros.stderr b/src/test/ui/macros/macro-export-on-modularized-macros.stderr
deleted file mode 100644 (file)
index 8bb031e..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-error: `#[macro_export]` cannot be used on `macro` items
-  --> $DIR/macro-export-on-modularized-macros.rs:5:1
-   |
-LL | macro m1() {}
-   | ^^^^^^^^^^^^^
-
-error: `#[macro_export]` cannot be used on `macro_rules` with `pub`
-  --> $DIR/macro-export-on-modularized-macros.rs:8:1
-   |
-LL | pub macro_rules! m2 { () => {} }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 2 previous errors
-
diff --git a/src/test/ui/macros/pub-macro-rules-fail.rs b/src/test/ui/macros/pub-macro-rules-fail.rs
deleted file mode 100644 (file)
index bdb4c73..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#![feature(pub_macro_rules)]
-
-#[macro_use]
-mod m {
-    pub macro_rules! mac { () => {} }
-
-    // `pub` `macro_rules` cannot be redefined in the same module.
-    pub macro_rules! mac { () => {} } //~ ERROR the name `mac` is defined multiple times
-
-    pub(self) macro_rules! private_mac { () => {} }
-}
-
-const _: () = {
-    pub macro_rules! block_mac { () => {} }
-};
-
-mod n {
-    // Scope of `pub` `macro_rules` is not extended by `#[macro_use]`.
-    mac!(); //~ ERROR cannot find macro `mac` in this scope
-
-    // `pub` `macro_rules` doesn't put the macro into the root module, unlike `#[macro_export]`.
-    crate::mac!(); //~ ERROR failed to resolve: maybe a missing crate `mac`
-    crate::block_mac!(); //~ ERROR failed to resolve: maybe a missing crate `block_mac`
-
-    crate::m::private_mac!(); //~ ERROR macro `private_mac` is private
-}
-
-fn main() {}
diff --git a/src/test/ui/macros/pub-macro-rules-fail.stderr b/src/test/ui/macros/pub-macro-rules-fail.stderr
deleted file mode 100644 (file)
index 588d79d..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-error[E0428]: the name `mac` is defined multiple times
-  --> $DIR/pub-macro-rules-fail.rs:8:5
-   |
-LL |     pub macro_rules! mac { () => {} }
-   |     -------------------- previous definition of the macro `mac` here
-...
-LL |     pub macro_rules! mac { () => {} }
-   |     ^^^^^^^^^^^^^^^^^^^^ `mac` redefined here
-   |
-   = note: `mac` must be defined only once in the macro namespace of this module
-
-error[E0433]: failed to resolve: maybe a missing crate `mac`?
-  --> $DIR/pub-macro-rules-fail.rs:22:12
-   |
-LL |     crate::mac!();
-   |            ^^^ maybe a missing crate `mac`?
-
-error[E0433]: failed to resolve: maybe a missing crate `block_mac`?
-  --> $DIR/pub-macro-rules-fail.rs:23:12
-   |
-LL |     crate::block_mac!();
-   |            ^^^^^^^^^ maybe a missing crate `block_mac`?
-
-error: cannot find macro `mac` in this scope
-  --> $DIR/pub-macro-rules-fail.rs:19:5
-   |
-LL |     mac!();
-   |     ^^^
-   |
-   = note: consider importing this macro:
-           m::mac
-
-error[E0603]: macro `private_mac` is private
-  --> $DIR/pub-macro-rules-fail.rs:25:15
-   |
-LL |     crate::m::private_mac!();
-   |               ^^^^^^^^^^^ private macro
-   |
-note: the macro `private_mac` is defined here
-  --> $DIR/pub-macro-rules-fail.rs:10:5
-   |
-LL |     pub(self) macro_rules! private_mac { () => {} }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 5 previous errors
-
-Some errors have detailed explanations: E0428, E0433, E0603.
-For more information about an error, try `rustc --explain E0428`.
diff --git a/src/test/ui/macros/pub-macro-rules.rs b/src/test/ui/macros/pub-macro-rules.rs
deleted file mode 100644 (file)
index cd4a845..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-// check-pass
-
-#![feature(pub_macro_rules)]
-
-mod m {
-    // `pub` `macro_rules` can be used earlier in item order than they are defined.
-    foo!();
-
-    pub macro_rules! foo { () => {} }
-
-    // `pub(...)` works too.
-    pub(super) macro_rules! bar { () => {} }
-}
-
-// `pub` `macro_rules` are available by module path.
-m::foo!();
-
-m::bar!();
-
-fn main() {}
index f75d08813cdcd99c08af1909ca05efe7f2d89f14..f3acd80a7a48f0797a456bbd2b16794a135bcade 100644 (file)
@@ -1,6 +1,6 @@
 mod m {
 //~^ ERROR `main` function not found
-    // An inferred main entry point (that doesn't use #[main])
+    // An inferred main entry point
     // must appear at the top of the crate
     fn main() { }
 }
index e301c2ff09ad75151a4b7024db910a4489ce5854..754ff0f80eb9afc7d8305c810aeb1f07b94eb407 100644 (file)
@@ -3,7 +3,7 @@ error[E0601]: `main` function not found in crate `main_wrong_location`
    |
 LL | / mod m {
 LL | |
-LL | |     // An inferred main entry point (that doesn't use #[main])
+LL | |     // An inferred main entry point
 LL | |     // must appear at the top of the crate
 LL | |     fn main() { }
 LL | | }
@@ -15,7 +15,7 @@ note: here is a function named `main`
 LL |     fn main() { }
    |     ^^^^^^^^^^^^^
    = note: you have one or more functions named `main` not defined at the crate level
-   = help: either move the `main` function definitions or attach the `#[main]` attribute to one of them
+   = help: consider moving the `main` function definitions
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/match/issue-82392.rs b/src/test/ui/match/issue-82392.rs
new file mode 100644 (file)
index 0000000..d26d883
--- /dev/null
@@ -0,0 +1,9 @@
+// https://github.com/rust-lang/rust/issues/82329
+// compile-flags: -Zunpretty=hir,typed
+// check-pass
+
+pub fn main() {
+    if true {
+    } else if let Some(a) = Some(3) {
+    }
+}
diff --git a/src/test/ui/match/issue-82392.stdout b/src/test/ui/match/issue-82392.stdout
new file mode 100644 (file)
index 0000000..8ff76c6
--- /dev/null
@@ -0,0 +1,20 @@
+#[prelude_import]
+use ::std::prelude::rust_2015::*;
+#[macro_use]
+extern crate std;
+// https://github.com/rust-lang/rust/issues/82329
+// compile-flags: -Zunpretty=hir,typed
+// check-pass
+
+pub fn main() ({
+                   (if (true as bool)
+                       ({ } as
+                           ()) else {match ((Some as
+                                                fn(i32) -> Option<i32> {Option::<i32>::Some})((3
+                                                                                                  as
+                                                                                                  i32))
+                                               as Option<i32>) {
+                                         Some(a) => { }
+                                         _ => { }
+                                     }} as ())
+                    } as ())
diff --git a/src/test/ui/match/issue-84434.rs b/src/test/ui/match/issue-84434.rs
new file mode 100644 (file)
index 0000000..423481f
--- /dev/null
@@ -0,0 +1,18 @@
+// https://github.com/rust-lang/rust/issues/84434
+// check-pass
+
+use std::path::Path;
+struct A {
+    pub func: fn(check: bool, a: &Path, b: Option<&Path>),
+}
+const MY_A: A = A {
+    func: |check, a, b| {
+        if check {
+            let _ = ();
+        } else if let Some(parent) = b.and_then(|p| p.parent()) {
+            let _ = ();
+        }
+    },
+};
+
+fn main() {}
diff --git a/src/test/ui/multiple-main-2.rs b/src/test/ui/multiple-main-2.rs
deleted file mode 100644 (file)
index e4685b1..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#![feature(main)]
-
-#[main]
-fn bar() {
-}
-
-#[main]
-fn foo() { //~ ERROR multiple functions with a `#[main]` attribute
-}
diff --git a/src/test/ui/multiple-main-2.stderr b/src/test/ui/multiple-main-2.stderr
deleted file mode 100644 (file)
index 24bc9a8..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-error[E0137]: multiple functions with a `#[main]` attribute
-  --> $DIR/multiple-main-2.rs:8:1
-   |
-LL | / fn bar() {
-LL | | }
-   | |_- first `#[main]` function
-...
-LL | / fn foo() {
-LL | | }
-   | |_^ additional `#[main]` function
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0137`.
diff --git a/src/test/ui/multiple-main-3.rs b/src/test/ui/multiple-main-3.rs
deleted file mode 100644 (file)
index d1b5ae9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#![feature(main)]
-
-#[main]
-fn main1() {
-}
-
-mod foo {
-    #[main]
-    fn main2() { //~ ERROR multiple functions with a `#[main]` attribute
-    }
-}
diff --git a/src/test/ui/multiple-main-3.stderr b/src/test/ui/multiple-main-3.stderr
deleted file mode 100644 (file)
index ec171b7..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-error[E0137]: multiple functions with a `#[main]` attribute
-  --> $DIR/multiple-main-3.rs:9:5
-   |
-LL | / fn main1() {
-LL | | }
-   | |_- first `#[main]` function
-...
-LL | /     fn main2() {
-LL | |     }
-   | |_____^ additional `#[main]` function
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0137`.
index eaa33453a75076dea22e05610241fd8df407675b..6583546aa5c1fe70bbecdb12f294372a20dcdd7c 100644 (file)
@@ -5,7 +5,7 @@ LL |     let &mut x = foo;
    |              -
    |              |
    |              first assignment to `x`
-   |              help: make this binding mutable: `mut x`
+   |              help: consider making this binding mutable: `mut x`
 LL |     x += 1;
    |     ^^^^^^ cannot assign twice to immutable variable
 
index c96c4784dcf32e3e8c216f9b64704bebc7bce3fd..6979c3ec44318b205641be98725b286057578ac5 100644 (file)
@@ -26,7 +26,9 @@ fn smeg() {
     foo(_x);
     //~^ ERROR the trait bound
     //~| NOTE the trait `ImplementedForUnitButNotNever` is not implemented
-    //~| NOTE the trait is implemented for `()`
+    //~| NOTE this trait is implemented for `()`
+    //~| NOTE this error might have been caused
+    //~| HELP did you intend
 }
 
 fn main() {
index 69691883de1e3b5f1be1ee848d2163f6dcb7d90f..99738375022f9ddb96f2f0f5af197807c8be4868 100644 (file)
@@ -7,7 +7,9 @@ LL | fn foo<T: ImplementedForUnitButNotNever>(_t: T) {}
 LL |     foo(_x);
    |     ^^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!`
    |
-   = note: the trait is implemented for `()`. Possibly this error has been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information). Consider whether you meant to use the type `()` here instead.
+   = note: this trait is implemented for `()`.
+   = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information).
+   = help: did you intend to use the type `()` here instead?
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/bad-crate-name.rs b/src/test/ui/parser/bad-crate-name.rs
new file mode 100644 (file)
index 0000000..837d5c3
--- /dev/null
@@ -0,0 +1,5 @@
+extern crate krate-name-here;
+//~^ ERROR crate name using dashes are not valid in `extern crate` statements
+//~| ERROR can't find crate for `krate_name_here`
+
+fn main() {}
diff --git a/src/test/ui/parser/bad-crate-name.stderr b/src/test/ui/parser/bad-crate-name.stderr
new file mode 100644 (file)
index 0000000..e015010
--- /dev/null
@@ -0,0 +1,20 @@
+error: crate name using dashes are not valid in `extern crate` statements
+  --> $DIR/bad-crate-name.rs:1:14
+   |
+LL | extern crate krate-name-here;
+   |              ^^^^^^^^^^^^^^^ dash-separated idents are not valid
+   |
+help: if the original crate name uses dashes you need to use underscores in the code
+   |
+LL | extern crate krate_name_here;
+   |                   ^    ^
+
+error[E0463]: can't find crate for `krate_name_here`
+  --> $DIR/bad-crate-name.rs:1:1
+   |
+LL | extern crate krate-name-here;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0463`.
index 8dc9351260ebcc6a4bb3979e2d665723bb5fddfa..37d04c5d65f0672a71b347dae1dd28c093bf1941 100644 (file)
@@ -10,7 +10,6 @@
 // ignore-pretty issue #37195
 // ignore-asmjs wasm2js does not support source maps yet
 
-#![feature(non_ascii_idents)]
 #![allow(uncommon_codepoints)]
 
 #[path = "issue-48508-aux.rs"]
index f3ae3aba9b9e68149f7eb9a5fbb764cbc0591763..7e7995d6724eb5432a36cb59d3ae4ce145497820 100644 (file)
@@ -4,9 +4,7 @@ fn main() {
     (()é);
     //~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator
     //~| ERROR: cannot find value `é` in this scope
-    //~| ERROR: non-ascii idents are not fully supported
     (()氷);
     //~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator
     //~| ERROR: cannot find value `氷` in this scope
-    //~| ERROR: non-ascii idents are not fully supported
 }
index 892cc92b1bdeb9437d248ee3703f680b65e8d180..21e71aa121560dc16d013e64f69f1a4fed5502e8 100644 (file)
@@ -8,7 +8,7 @@ LL |     (()é);
    |        help: missing `,`
 
 error: expected one of `)`, `,`, `.`, `?`, or an operator, found `氷`
-  --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8
+  --> $DIR/multibyte-char-use-seperator-issue-80134.rs:7:8
    |
 LL |     (()氷);
    |        -^
@@ -23,30 +23,11 @@ LL |     (()é);
    |        ^ not found in this scope
 
 error[E0425]: cannot find value `氷` in this scope
-  --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8
+  --> $DIR/multibyte-char-use-seperator-issue-80134.rs:7:8
    |
 LL |     (()氷);
    |        ^^ not found in this scope
 
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8
-   |
-LL |     (()é);
-   |        ^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8
-   |
-LL |     (()氷);
-   |        ^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
-Some errors have detailed explanations: E0425, E0658.
-For more information about an error, try `rustc --explain E0425`.
+For more information about this error, try `rustc --explain E0425`.
index 285c203f382df3b75e69c6e415741122779b937a..d0726f05cc3be01d2a48f2a667d86d52afdeacc5 100644 (file)
@@ -16,7 +16,7 @@ LL |     let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
    |                        ---
    |                        |
    |                        first assignment to `_x1`
-   |                        help: make this binding mutable: `mut _x1`
+   |                        help: consider making this binding mutable: `mut _x1`
 LL |     _x1 = U;
    |     ^^^^^^^ cannot assign twice to immutable variable
 
@@ -74,7 +74,7 @@ LL |     let (ref _x0, _x1, ref _x2, ..) = tup;
    |                   ---
    |                   |
    |                   first assignment to `_x1`
-   |                   help: make this binding mutable: `mut _x1`
+   |                   help: consider making this binding mutable: `mut _x1`
 LL |     _x1 = U;
    |     ^^^^^^^ cannot assign twice to immutable variable
 
index 714f28941f11ff49f188e2bd954079a09fed1a03..d09a8aae7480108eee0c477cc6e7493bdbd71d3c 100644 (file)
@@ -57,6 +57,9 @@ error[E0747]: constant provided when a type was expected
    |
 LL |     let _x: Box<Bar>;
    |                 ^^^
+   |
+   = help: `Bar` is a function item, not a type
+   = help: function item types cannot be named directly
 
 error: aborting due to 4 previous errors
 
index c7ad8ec503654c7e0b378840200705977fffeaf4..fdf0549cf50bc5fd5dcf4318b9a1f7a28d581fb1 100644 (file)
@@ -83,12 +83,18 @@ error[E0747]: constant provided when a type was expected
    |
 LL |     let _x : Box<Bar>;
    |                  ^^^
+   |
+   = help: `Bar` is a function item, not a type
+   = help: function item types cannot be named directly
 
 error[E0747]: constant provided when a type was expected
   --> $DIR/privacy-ns2.rs:48:17
    |
 LL |     let _x: Box<Bar>;
    |                 ^^^
+   |
+   = help: `Bar` is a function item, not a type
+   = help: function item types cannot be named directly
 
 error: aborting due to 8 previous errors
 
index 09baa059e55679f5bbaa9d3a76c9db8b8d6f1996..05535b92cca73eca6c261557bf57402aad41a551 100644 (file)
@@ -12,4 +12,12 @@ fn b() {
     //~^ ERROR casting
 }
 
+fn c() {
+    let _ = [
+        std::intrinsics::copy_nonoverlapping::<i32>,
+        std::intrinsics::copy::<i32>,
+        //~^ ERROR cannot coerce
+    ];
+}
+
 fn main() {}
index 675447f972179ea74403998c21f15297c024f19b..5d82fdbd31190bfdecbe4f57f95c87bfdd4e07ca 100644 (file)
@@ -19,7 +19,16 @@ error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_,
 LL |     let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error[E0308]: cannot coerce intrinsics to function pointers
+  --> $DIR/reify-intrinsic.rs:18:9
+   |
+LL |         std::intrinsics::copy::<i32>,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers
+   |
+   = note: expected type `unsafe extern "rust-intrinsic" fn(_, _, _) {copy_nonoverlapping::<i32>}`
+           found fn item `unsafe extern "rust-intrinsic" fn(_, _, _) {std::intrinsics::copy::<i32>}`
+
+error: aborting due to 3 previous errors
 
 Some errors have detailed explanations: E0308, E0606.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/resolve/auxiliary/issue-80079.rs b/src/test/ui/resolve/auxiliary/issue-80079.rs
new file mode 100644 (file)
index 0000000..190ca75
--- /dev/null
@@ -0,0 +1,18 @@
+#![crate_type = "lib"]
+
+pub mod public {
+    use private_import;
+
+    // should not be suggested since it is private
+    struct Foo;
+
+    mod private_module {
+        // should not be suggested since it is private
+        pub struct Foo;
+    }
+}
+
+mod private_import {
+    // should not be suggested since it is private
+    pub struct Foo;
+}
diff --git a/src/test/ui/resolve/bad-env-capture.rs b/src/test/ui/resolve/bad-env-capture.rs
new file mode 100644 (file)
index 0000000..83fd254
--- /dev/null
@@ -0,0 +1,6 @@
+// error-pattern: can't capture dynamic environment in a fn item
+fn foo() {
+    let x: isize;
+    fn bar() { log(debug, x); }
+}
+fn main() { foo(); }
diff --git a/src/test/ui/resolve/bad-env-capture.stderr b/src/test/ui/resolve/bad-env-capture.stderr
new file mode 100644 (file)
index 0000000..f78a38a
--- /dev/null
@@ -0,0 +1,24 @@
+error[E0434]: can't capture dynamic environment in a fn item
+  --> $DIR/bad-env-capture.rs:4:27
+   |
+LL |     fn bar() { log(debug, x); }
+   |                           ^
+   |
+   = help: use the `|| { ... }` closure form instead
+
+error[E0425]: cannot find function `log` in this scope
+  --> $DIR/bad-env-capture.rs:4:16
+   |
+LL |     fn bar() { log(debug, x); }
+   |                ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+  --> $DIR/bad-env-capture.rs:4:20
+   |
+LL |     fn bar() { log(debug, x); }
+   |                    ^^^^^ not found in this scope
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0425, E0434.
+For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/resolve/bad-env-capture2.rs b/src/test/ui/resolve/bad-env-capture2.rs
new file mode 100644 (file)
index 0000000..b04569c
--- /dev/null
@@ -0,0 +1,5 @@
+// error-pattern: can't capture dynamic environment in a fn item
+fn foo(x: isize) {
+    fn bar() { log(debug, x); }
+}
+fn main() { foo(2); }
diff --git a/src/test/ui/resolve/bad-env-capture2.stderr b/src/test/ui/resolve/bad-env-capture2.stderr
new file mode 100644 (file)
index 0000000..57c807f
--- /dev/null
@@ -0,0 +1,24 @@
+error[E0434]: can't capture dynamic environment in a fn item
+  --> $DIR/bad-env-capture2.rs:3:27
+   |
+LL |     fn bar() { log(debug, x); }
+   |                           ^
+   |
+   = help: use the `|| { ... }` closure form instead
+
+error[E0425]: cannot find function `log` in this scope
+  --> $DIR/bad-env-capture2.rs:3:16
+   |
+LL |     fn bar() { log(debug, x); }
+   |                ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+  --> $DIR/bad-env-capture2.rs:3:20
+   |
+LL |     fn bar() { log(debug, x); }
+   |                    ^^^^^ not found in this scope
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0425, E0434.
+For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/resolve/bad-env-capture3.rs b/src/test/ui/resolve/bad-env-capture3.rs
new file mode 100644 (file)
index 0000000..62f12fd
--- /dev/null
@@ -0,0 +1,8 @@
+// error-pattern: can't capture dynamic environment in a fn item
+fn foo(x: isize) {
+    fn mth() {
+        fn bar() { log(debug, x); }
+    }
+}
+
+fn main() { foo(2); }
diff --git a/src/test/ui/resolve/bad-env-capture3.stderr b/src/test/ui/resolve/bad-env-capture3.stderr
new file mode 100644 (file)
index 0000000..d6eb4f8
--- /dev/null
@@ -0,0 +1,24 @@
+error[E0434]: can't capture dynamic environment in a fn item
+  --> $DIR/bad-env-capture3.rs:4:31
+   |
+LL |         fn bar() { log(debug, x); }
+   |                               ^
+   |
+   = help: use the `|| { ... }` closure form instead
+
+error[E0425]: cannot find function `log` in this scope
+  --> $DIR/bad-env-capture3.rs:4:20
+   |
+LL |         fn bar() { log(debug, x); }
+   |                    ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+  --> $DIR/bad-env-capture3.rs:4:24
+   |
+LL |         fn bar() { log(debug, x); }
+   |                        ^^^^^ not found in this scope
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0425, E0434.
+For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/resolve/bad-expr-path.rs b/src/test/ui/resolve/bad-expr-path.rs
new file mode 100644 (file)
index 0000000..31fc9cf
--- /dev/null
@@ -0,0 +1,8 @@
+mod m1 {}
+
+fn main(arguments: Vec<String>) { //~ ERROR `main` function has wrong type
+    log(debug, m1::arguments);
+    //~^ ERROR cannot find function `log` in this scope
+    //~| ERROR cannot find value `debug` in this scope
+    //~| ERROR cannot find value `arguments` in module `m1`
+}
diff --git a/src/test/ui/resolve/bad-expr-path.stderr b/src/test/ui/resolve/bad-expr-path.stderr
new file mode 100644 (file)
index 0000000..77c48c9
--- /dev/null
@@ -0,0 +1,31 @@
+error[E0425]: cannot find function `log` in this scope
+  --> $DIR/bad-expr-path.rs:4:5
+   |
+LL |     log(debug, m1::arguments);
+   |     ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+  --> $DIR/bad-expr-path.rs:4:9
+   |
+LL |     log(debug, m1::arguments);
+   |         ^^^^^ not found in this scope
+
+error[E0425]: cannot find value `arguments` in module `m1`
+  --> $DIR/bad-expr-path.rs:4:20
+   |
+LL |     log(debug, m1::arguments);
+   |                    ^^^^^^^^^ not found in `m1`
+
+error[E0580]: `main` function has wrong type
+  --> $DIR/bad-expr-path.rs:3:1
+   |
+LL | fn main(arguments: Vec<String>) {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
+   |
+   = note: expected fn pointer `fn()`
+              found fn pointer `fn(Vec<String>)`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0425, E0580.
+For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/resolve/bad-expr-path2.rs b/src/test/ui/resolve/bad-expr-path2.rs
new file mode 100644 (file)
index 0000000..eb88edb
--- /dev/null
@@ -0,0 +1,10 @@
+mod m1 {
+    pub mod arguments {}
+}
+
+fn main(arguments: Vec<String>) { //~ ERROR `main` function has wrong type
+    log(debug, m1::arguments);
+    //~^ ERROR cannot find function `log` in this scope
+    //~| ERROR cannot find value `debug` in this scope
+    //~| ERROR expected value, found module `m1::arguments`
+}
diff --git a/src/test/ui/resolve/bad-expr-path2.stderr b/src/test/ui/resolve/bad-expr-path2.stderr
new file mode 100644 (file)
index 0000000..d06e102
--- /dev/null
@@ -0,0 +1,31 @@
+error[E0425]: cannot find function `log` in this scope
+  --> $DIR/bad-expr-path2.rs:6:5
+   |
+LL |     log(debug, m1::arguments);
+   |     ^^^ not found in this scope
+
+error[E0425]: cannot find value `debug` in this scope
+  --> $DIR/bad-expr-path2.rs:6:9
+   |
+LL |     log(debug, m1::arguments);
+   |         ^^^^^ not found in this scope
+
+error[E0423]: expected value, found module `m1::arguments`
+  --> $DIR/bad-expr-path2.rs:6:16
+   |
+LL |     log(debug, m1::arguments);
+   |                ^^^^^^^^^^^^^ not a value
+
+error[E0580]: `main` function has wrong type
+  --> $DIR/bad-expr-path2.rs:5:1
+   |
+LL | fn main(arguments: Vec<String>) {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
+   |
+   = note: expected fn pointer `fn()`
+              found fn pointer `fn(Vec<String>)`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0423, E0425, E0580.
+For more information about an error, try `rustc --explain E0423`.
diff --git a/src/test/ui/resolve/bad-module.rs b/src/test/ui/resolve/bad-module.rs
new file mode 100644 (file)
index 0000000..b23e97c
--- /dev/null
@@ -0,0 +1,7 @@
+fn main() {
+    let foo = thing::len(Vec::new());
+    //~^ ERROR failed to resolve: use of undeclared crate or module `thing`
+
+    let foo = foo::bar::baz();
+    //~^ ERROR failed to resolve: use of undeclared crate or module `foo`
+}
diff --git a/src/test/ui/resolve/bad-module.stderr b/src/test/ui/resolve/bad-module.stderr
new file mode 100644 (file)
index 0000000..581a661
--- /dev/null
@@ -0,0 +1,15 @@
+error[E0433]: failed to resolve: use of undeclared crate or module `thing`
+  --> $DIR/bad-module.rs:2:15
+   |
+LL |     let foo = thing::len(Vec::new());
+   |               ^^^^^ use of undeclared crate or module `thing`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `foo`
+  --> $DIR/bad-module.rs:5:15
+   |
+LL |     let foo = foo::bar::baz();
+   |               ^^^ use of undeclared crate or module `foo`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/src/test/ui/resolve/bad-type-env-capture.rs b/src/test/ui/resolve/bad-type-env-capture.rs
new file mode 100644 (file)
index 0000000..53dfb13
--- /dev/null
@@ -0,0 +1,4 @@
+fn foo<T>() {
+    fn bar(b: T) { } //~ ERROR can't use generic parameters from outer
+}
+fn main() { }
diff --git a/src/test/ui/resolve/bad-type-env-capture.stderr b/src/test/ui/resolve/bad-type-env-capture.stderr
new file mode 100644 (file)
index 0000000..6f24c0d
--- /dev/null
@@ -0,0 +1,13 @@
+error[E0401]: can't use generic parameters from outer function
+  --> $DIR/bad-type-env-capture.rs:2:15
+   |
+LL | fn foo<T>() {
+   |        - type parameter from outer function
+LL |     fn bar(b: T) { }
+   |        ---    ^ use of generic parameter from outer function
+   |        |
+   |        help: try using a local generic parameter instead: `bar<T>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0401`.
index 4d0b0af58a320a17701742135315e0ef0aff037b..6fc61cae84339961affc8a8340dfebb45fb7af02 100644 (file)
@@ -5,10 +5,9 @@ LL | impl Foo for S {
    |      ^^^ type aliases cannot be used as traits
    |
 help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
-  --> $DIR/issue-3907.rs:5:1
    |
-LL | type Foo = dyn issue_3907::Foo;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | trait Foo = dyn issue_3907::Foo;
+   |
 help: consider importing this trait instead
    |
 LL | use issue_3907::Foo;
index b2084a7a426a66c727edc89e4d72f1ef5cb3a15f..e94877fded78467dc24155ce46e4ccb842ff84ff 100644 (file)
@@ -8,8 +8,8 @@ LL | fn foo(_x: K) {}
    = help: unsized fn params are gated as an unstable feature
 help: function arguments must have a statically known size, borrowed types always have a known size
    |
-LL | fn foo(&_x: K) {}
-   |        ^
+LL | fn foo(_x: &K) {}
+   |            ^
 
 error: aborting due to previous error
 
index 41dff2fe542053054c3052affd941957cb735c47..a8aa50b7c3ab2247e8d64fa1e72dbeb921e95b26 100644 (file)
@@ -11,16 +11,16 @@ LL | trait I {}
    | ------- similarly named trait `I` defined here
 LL | type K = dyn I;
 LL | impl K for isize {}
-   |      ^
-   |      |
-   |      type aliases cannot be used as traits
-   |      help: a trait with a similar name exists: `I`
+   |      ^ type aliases cannot be used as traits
    |
 help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
-  --> $DIR/issue-5035.rs:2:1
    |
-LL | type K = dyn I;
-   | ^^^^^^^^^^^^^^^
+LL | trait K = dyn I;
+   |
+help: a trait with a similar name exists
+   |
+LL | impl I for isize {}
+   |      ^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/resolve/issue-80079.rs b/src/test/ui/resolve/issue-80079.rs
new file mode 100644 (file)
index 0000000..4795ed0
--- /dev/null
@@ -0,0 +1,12 @@
+// aux-build:issue-80079.rs
+
+// using a module from another crate should not cause errors to suggest private
+// items in that module
+
+extern crate issue_80079;
+
+use issue_80079::public;
+
+fn main() {
+    let _ = Foo; //~ ERROR cannot find value `Foo` in this scope
+}
diff --git a/src/test/ui/resolve/issue-80079.stderr b/src/test/ui/resolve/issue-80079.stderr
new file mode 100644 (file)
index 0000000..93e8c03
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0425]: cannot find value `Foo` in this scope
+  --> $DIR/issue-80079.rs:11:13
+   |
+LL |     let _ = Foo;
+   |             ^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
index 2974d08eb23b1976545bc2c6abfa2bfe86195c5d..8addc0303fb915aab5b859e12a47b7b93094ca02 100644 (file)
@@ -11,10 +11,9 @@ LL | fn g<F:Typedef(isize) -> isize>(x: F) {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^ type aliases cannot be used as traits
    |
 help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
-  --> $DIR/unboxed-closure-sugar-nonexistent-trait.rs:4:1
    |
-LL | type Typedef = isize;
-   | ^^^^^^^^^^^^^^^^^^^^^
+LL | trait Typedef = isize;
+   |
 
 error: aborting due to 2 previous errors
 
index 310545b92d5492d23f7d698790a9dfdcbe47001e..c07ba54af40325fa4ca2d7bd1ce1641219fe43ec 100644 (file)
@@ -1,5 +1,3 @@
-#![feature(non_ascii_idents)]
-
 extern crate ьаг; //~ ERROR cannot load a crate with a non-ascii name `ьаг`
 
 fn main() {}
index 11108f2fb86789ce88b273be499e3a7c0a54b750..a8f3abe599eb27d88f44ac207c758a2af78f1482 100644 (file)
@@ -1,5 +1,5 @@
 error: cannot load a crate with a non-ascii name `ьаг`
-  --> $DIR/crate_name_nonascii_forbidden-1.rs:3:1
+  --> $DIR/crate_name_nonascii_forbidden-1.rs:1:1
    |
 LL | extern crate ьаг;
    | ^^^^^^^^^^^^^^^^^
index 0249848b35ac0b12621a0b0739ba1e1dcd72f53f..f8e033937c607442bb442342807225f9ce24eb9d 100644 (file)
@@ -1,6 +1,5 @@
 // compile-flags:--extern му_сгате
 // edition:2018
-#![feature(non_ascii_idents)]
 
 use му_сгате::baz; //~  ERROR cannot load a crate with a non-ascii name `му_сгате`
 
index 8d3548ed33dcf8a70047df8c8cb1cbbe97382eab..05fc4fb22ad1ea6075c4811ae5e32c31a058dfd1 100644 (file)
@@ -1,5 +1,5 @@
 error: cannot load a crate with a non-ascii name `му_сгате`
-  --> $DIR/crate_name_nonascii_forbidden-2.rs:5:5
+  --> $DIR/crate_name_nonascii_forbidden-2.rs:4:5
    |
 LL | use му_сгате::baz;
    |     ^^^^^^^^
index c70ced731d54ddcf4fe42fe03ed286d01e00629a..ad6825404306a2f1f3440ebca24341b46ad8ed7b 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(extern_types)]
-#![feature(non_ascii_idents)]
 
 extern "C" {
     type 一; //~ items in `extern` blocks cannot use non-ascii identifiers
index 3b18c06ec5c4c371041e40fb041633d56115bd25..ab8832e91631582a9751289e63a98eba1e519c68 100644 (file)
@@ -1,5 +1,5 @@
 error: items in `extern` blocks cannot use non-ascii identifiers
-  --> $DIR/extern_block_nonascii_forbidden.rs:5:10
+  --> $DIR/extern_block_nonascii_forbidden.rs:4:10
    |
 LL | extern "C" {
    | ---------- in this `extern` block
@@ -9,7 +9,7 @@ LL |     type 一;
    = note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
 
 error: items in `extern` blocks cannot use non-ascii identifiers
-  --> $DIR/extern_block_nonascii_forbidden.rs:6:8
+  --> $DIR/extern_block_nonascii_forbidden.rs:5:8
    |
 LL | extern "C" {
    | ---------- in this `extern` block
@@ -20,7 +20,7 @@ LL |     fn 二();
    = note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
 
 error: items in `extern` blocks cannot use non-ascii identifiers
-  --> $DIR/extern_block_nonascii_forbidden.rs:7:12
+  --> $DIR/extern_block_nonascii_forbidden.rs:6:12
    |
 LL | extern "C" {
    | ---------- in this `extern` block
index 109cec7548e2f53afb28d7f6417226097b42fa49..1023fee37d5dd812c1862a009a8558d75b6ca199 100644 (file)
@@ -1,5 +1,4 @@
 // check-pass
-#![feature(non_ascii_idents)]
 
 struct Résumé; // ['LATIN SMALL LETTER E WITH ACUTE']
 
index efd2932f152948918e9c5b54ede2690c369fd2e4..e949e2319c1c48ebd4937528dd6bee1d3d750e04 100644 (file)
@@ -1,5 +1,3 @@
-#![feature(non_ascii_idents)]
-
 mod řųśť; //~ trying to load file for
 //~^ file not found for
 
index 6e06ab737c2150d7b05daadb66adb6cce8560a92..e857a1e60e5fefb4be8b315bbaf99f982521764f 100644 (file)
@@ -1,5 +1,5 @@
 error[E0583]: file not found for module `řųśť`
-  --> $DIR/mod_file_nonascii_forbidden.rs:3:1
+  --> $DIR/mod_file_nonascii_forbidden.rs:1:1
    |
 LL | mod řųśť;
    | ^^^^^^^^^
@@ -7,7 +7,7 @@ LL | mod řųśť;
    = help: to create the module `řųśť`, create file "$DIR/řųśť.rs"
 
 error[E0754]: trying to load file for module `řųśť` with non-ascii identifier name
-  --> $DIR/mod_file_nonascii_forbidden.rs:3:5
+  --> $DIR/mod_file_nonascii_forbidden.rs:1:5
    |
 LL | mod řųśť;
    |     ^^^^
index e9f3fba2fb01e61c38b71adf4c9f7eaad4d8efdc..94327846d616937df09d9cae6c5f74672a8c95f5 100644 (file)
@@ -1,5 +1,4 @@
 // check-pass
-#![feature(non_ascii_idents)]
 
 #[path="auxiliary/mod_file_nonascii_with_path_allowed-aux.rs"]
 mod řųśť;
index dd27da432ba657482691266cedb703f6c65f90e2..e1d836b7c3e34b7c6b735eab396c3d8f5ac8fe42 100644 (file)
@@ -1,5 +1,4 @@
 // check-pass
-#![feature(non_ascii_idents)]
 
 mod řųśť {
     const IS_GREAT: bool = true;
index a408c9757165c4979f40a36e889ff52b1924d4a3..0325d6436abcb4d20d3dc13ef11cee14273ec510 100644 (file)
@@ -1,5 +1,3 @@
-#![feature(non_ascii_idents)]
-
 #[no_mangle]
 pub fn řųśť() {}  //~ `#[no_mangle]` requires ASCII identifier
 
index 4ca83e41032081449cbb474c16a9650f33a2649c..b4b2b0c7ee001022dd3eedd26b67779dd2ea60cb 100644 (file)
@@ -1,5 +1,5 @@
 error[E0754]: `#[no_mangle]` requires ASCII identifier
-  --> $DIR/no_mangle_nonascii_forbidden.rs:4:1
+  --> $DIR/no_mangle_nonascii_forbidden.rs:2:1
    |
 LL | pub fn řųśť() {}
    | ^^^^^^^^^^^^^
index 6a511f4ed3ed8492c146ac20153ffb32e60a4dbe..c37990b1af3ddf4ab086779d294038264bb047de 100644 (file)
@@ -2,8 +2,8 @@
 
 // check-pass
 
-#![feature(const_fn)]
 #![feature(const_trait_impl)]
+#![feature(const_fn_trait_bound)]
 #![allow(incomplete_features)]
 
 struct S;
index b39d27779f45f72f5f546e2370d01e61881c37a5..d553b2ab8ec47b75e31dabed861570048e68238c 100644 (file)
@@ -1,8 +1,8 @@
 // check-pass
 
-#![feature(const_fn)]
 #![feature(const_trait_impl)]
 #![feature(const_trait_bound_opt_out)]
+#![feature(const_fn_trait_bound)]
 #![allow(incomplete_features)]
 
 struct S;
index e968e6ec7bb80ef33114e1845fd68e46ef1509ac..74b0d5fbe474ba767110754fbf0ddf0cfb5ab22a 100644 (file)
@@ -2,8 +2,8 @@
 
 // check-pass
 
-#![feature(const_fn)]
 #![feature(const_trait_impl)]
+#![feature(const_fn_trait_bound)]
 #![allow(incomplete_features)]
 
 struct S;
index 7829ffe2a38d9e1f0678d64d54046d301a9c0b3c..d3680820312605d8a57a14353e830254cfa405b4 100644 (file)
@@ -2,7 +2,7 @@
 
 #![allow(incomplete_features)]
 #![feature(const_trait_impl)]
-#![feature(const_fn)]
+#![feature(const_fn_trait_bound)]
 
 use std::marker::PhantomData;
 
index 54d7cfd5d797390023baa754edbe2faea4f13298..5b2ebccef1c6dd42c5654bafd35d0e1653d5eb31 100644 (file)
@@ -1,4 +1,4 @@
-error[E0723]: trait methods cannot be stable const fn
+error: trait methods cannot be stable const fn
   --> $DIR/stability.rs:14:5
    |
 LL | /     fn sub(self, rhs: Self) -> Self {
@@ -17,4 +17,3 @@ LL |     Int(1i32) + Int(2i32)
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0723`.
index 4b1898345a32ebe6c7536c942d5ad53136c53b9f..2cf5a073fe5c22334a30fb79d4f5b1c06f826b5f 100644 (file)
@@ -1,6 +1,9 @@
 error[E0599]: no method named `foobar` found for type `u32` in the current scope
   --> $DIR/trait-import-suggestions.rs:22:11
    |
+LL |             fn foobar(&self) { }
+   |                ------ the method is available for `u32` here
+...
 LL |         x.foobar();
    |           ^^^^^^ method not found in `u32`
    |
@@ -11,6 +14,9 @@ LL |         x.foobar();
 error[E0599]: no method named `bar` found for type `u32` in the current scope
   --> $DIR/trait-import-suggestions.rs:28:7
    |
+LL |         fn bar(&self) { }
+   |            --- the method is available for `u32` here
+...
 LL |     x.bar();
    |       ^^^ method not found in `u32`
    |
index cee73b0425ad52210f7032b7e6b9262babad2b51..9a26a351d992c257f4ca41a3a03c777533506d4d 100644 (file)
@@ -7,7 +7,7 @@
 // error-pattern: AddressSanitizer: stack-buffer-overflow
 // error-pattern: 'xs' (line 15) <== Memory access at offset
 
-#![feature(test)]
+#![feature(bench_black_box)]
 
 use std::hint::black_box;
 
index 88769b7cb4554562bf54b12e6f087eee82fb5d9e..bb6986740d9067f50b011649e1411061e8693647 100644 (file)
@@ -7,7 +7,7 @@
 // run-fail
 // error-pattern: HWAddressSanitizer: tag-mismatch
 
-#![feature(test)]
+#![feature(bench_black_box)]
 
 use std::hint::black_box;
 
index c9f10fe4f467ed73c8bdc7a4a92944bcf49c788e..f63f81352dada78fa272f309f8a9a64703462a71 100644 (file)
@@ -6,7 +6,7 @@
 // run-fail
 // error-pattern: LeakSanitizer: detected memory leaks
 
-#![feature(test)]
+#![feature(bench_black_box)]
 
 use std::hint::black_box;
 use std::mem;
index a26649a5800131e20a6820ec72a123263552a84a..48a482a13aaa9d47b5eca083994f4dc4b544acbe 100644 (file)
@@ -13,7 +13,7 @@
 
 #![feature(core_intrinsics)]
 #![feature(start)]
-#![feature(test)]
+#![feature(bench_black_box)]
 
 use std::hint::black_box;
 use std::mem::MaybeUninit;
index 362907ecbd7efeed96f65fef82aace80c04b7c0f..c3b9084affdb3cbabf6f24cd57e1adeee6414d52 100644 (file)
@@ -1,6 +1,9 @@
 error[E0599]: no method named `f` found for unit type `()` in the current scope
   --> $DIR/shadowed-trait-methods.rs:13:8
    |
+LL |     pub trait T { fn f(&self) {} }
+   |                      - the method is available for `()` here
+...
 LL |     ().f()
    |        ^ method not found in `()`
    |
index 220a59535cae42bb4a05430e8d46c5f25d0175a7..7a4f7466559ffdc69a7cf0b389825bcee9fa9ac0 100644 (file)
     fn simd_fabs<T>(x: T) -> T;
     fn simd_fsin<T>(x: T) -> T;
     fn simd_fcos<T>(x: T) -> T;
-    fn simd_ceil<T>(x: T) -> T;
     fn simd_fexp<T>(x: T) -> T;
     fn simd_fexp2<T>(x: T) -> T;
-    fn simd_floor<T>(x: T) -> T;
     fn simd_fma<T>(x: T, y: T, z: T) -> T;
     fn simd_flog<T>(x: T) -> T;
     fn simd_flog10<T>(x: T) -> T;
     fn simd_flog2<T>(x: T) -> T;
     fn simd_fpow<T>(x: T, y: T) -> T;
     fn simd_fpowi<T>(x: T, y: i32) -> T;
+
+    // rounding functions
+    fn simd_ceil<T>(x: T) -> T;
+    fn simd_floor<T>(x: T) -> T;
+    fn simd_round<T>(x: T) -> T;
+    fn simd_trunc<T>(x: T) -> T;
 }
 
 macro_rules! assert_approx_eq_f32 {
@@ -64,18 +68,12 @@ fn main() {
         let r = simd_fcos(z);
         assert_approx_eq!(x, r);
 
-        let r = simd_ceil(h);
-        assert_approx_eq!(x, r);
-
         let r = simd_fexp(z);
         assert_approx_eq!(x, r);
 
         let r = simd_fexp2(z);
         assert_approx_eq!(x, r);
 
-        let r = simd_floor(h);
-        assert_approx_eq!(z, r);
-
         let r = simd_fma(x, h, h);
         assert_approx_eq!(x, r);
 
@@ -99,5 +97,18 @@ fn main() {
 
         let r = simd_fsin(z);
         assert_approx_eq!(z, r);
+
+        // rounding functions
+        let r = simd_floor(h);
+        assert_eq!(z, r);
+
+        let r = simd_ceil(h);
+        assert_eq!(x, r);
+
+        let r = simd_round(h);
+        assert_eq!(x, r);
+
+        let r = simd_trunc(h);
+        assert_eq!(z, r);
     }
 }
diff --git a/src/test/ui/specialization/issue-33017.rs b/src/test/ui/specialization/issue-33017.rs
new file mode 100644 (file)
index 0000000..4d19230
--- /dev/null
@@ -0,0 +1,45 @@
+// Test to ensure that trait bounds are propertly
+// checked on specializable associated types
+
+#![allow(incomplete_features)]
+#![feature(specialization)]
+
+trait UncheckedCopy: Sized {
+    type Output: From<Self> + Copy + Into<Self>;
+}
+
+impl<T> UncheckedCopy for T {
+    default type Output = Self;
+    //~^ ERROR: the trait bound `T: Copy` is not satisfied
+}
+
+fn unchecked_copy<T: UncheckedCopy>(other: &T::Output) -> T {
+    (*other).into()
+}
+
+fn bug(origin: String) {
+    // Turn the String into it's Output type...
+    // Which we can just do by `.into()`, the assoc type states `From<Self>`.
+    let origin_output = origin.into();
+
+    // Make a copy of String::Output, which is a String...
+    let mut copy: String = unchecked_copy::<String>(&origin_output);
+
+    // Turn the Output type into a String again,
+    // Which we can just do by `.into()`, the assoc type states `Into<Self>`.
+    let mut origin: String = origin_output.into();
+
+    // assert both Strings use the same buffer.
+    assert_eq!(copy.as_ptr(), origin.as_ptr());
+
+    // Any use of the copy we made becomes invalid,
+    drop(origin);
+
+    // OH NO! UB UB UB UB!
+    copy.push_str(" world!");
+    println!("{}", copy);
+}
+
+fn main() {
+    bug(String::from("hello"));
+}
diff --git a/src/test/ui/specialization/issue-33017.stderr b/src/test/ui/specialization/issue-33017.stderr
new file mode 100644 (file)
index 0000000..bff4618
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0277]: the trait bound `T: Copy` is not satisfied
+  --> $DIR/issue-33017.rs:12:5
+   |
+LL |     type Output: From<Self> + Copy + Into<Self>;
+   |                               ---- required by this bound in `UncheckedCopy::Output`
+...
+LL |     default type Output = Self;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T`
+   |
+help: consider restricting type parameter `T`
+   |
+LL | impl<T: std::marker::Copy> UncheckedCopy for T {
+   |       ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/specialization/issue-51892.rs b/src/test/ui/specialization/issue-51892.rs
new file mode 100644 (file)
index 0000000..3cd0711
--- /dev/null
@@ -0,0 +1,19 @@
+#![allow(incomplete_features)]
+#![feature(const_generics)]
+#![feature(const_evaluatable_checked)]
+#![feature(specialization)]
+
+pub trait Trait {
+    type Type;
+}
+
+impl<T: ?Sized> Trait for T {
+    default type Type = [u8; 1];
+}
+
+impl<T: Trait> Trait for *const T {
+    type Type = [u8; std::mem::size_of::<<T as Trait>::Type>()];
+    //~^ ERROR: unconstrained generic constant
+}
+
+fn main() {}
diff --git a/src/test/ui/specialization/issue-51892.stderr b/src/test/ui/specialization/issue-51892.stderr
new file mode 100644 (file)
index 0000000..2d30164
--- /dev/null
@@ -0,0 +1,10 @@
+error: unconstrained generic constant
+  --> $DIR/issue-51892.rs:15:5
+   |
+LL |     type Type = [u8; std::mem::size_of::<<T as Trait>::Type>()];
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::<<T as Trait>::Type>()]:`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/static/bad-const-type.rs b/src/test/ui/static/bad-const-type.rs
new file mode 100644 (file)
index 0000000..934ee35
--- /dev/null
@@ -0,0 +1,4 @@
+static i: String = 10;
+//~^ ERROR mismatched types
+//~| expected struct `String`, found integer
+fn main() { println!("{}", i); }
diff --git a/src/test/ui/static/bad-const-type.stderr b/src/test/ui/static/bad-const-type.stderr
new file mode 100644 (file)
index 0000000..a9c84b4
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0308]: mismatched types
+  --> $DIR/bad-const-type.rs:1:20
+   |
+LL | static i: String = 10;
+   |                    ^^
+   |                    |
+   |                    expected struct `String`, found integer
+   |                    help: try using a conversion method: `10.to_string()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/suggestions/as-ref-2.fixed b/src/test/ui/suggestions/as-ref-2.fixed
new file mode 100644 (file)
index 0000000..13bbb23
--- /dev/null
@@ -0,0 +1,13 @@
+// run-rustfix
+
+struct Struct;
+
+fn bar(_: &Struct) -> Struct {
+    Struct
+}
+
+fn main() {
+    let foo = Some(Struct);
+    let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s));
+    let _y = foo; //~ERROR use of moved value: `foo`
+}
diff --git a/src/test/ui/suggestions/as-ref-2.rs b/src/test/ui/suggestions/as-ref-2.rs
new file mode 100644 (file)
index 0000000..74d61cd
--- /dev/null
@@ -0,0 +1,13 @@
+// run-rustfix
+
+struct Struct;
+
+fn bar(_: &Struct) -> Struct {
+    Struct
+}
+
+fn main() {
+    let foo = Some(Struct);
+    let _x: Option<Struct> = foo.map(|s| bar(&s));
+    let _y = foo; //~ERROR use of moved value: `foo`
+}
diff --git a/src/test/ui/suggestions/as-ref-2.stderr b/src/test/ui/suggestions/as-ref-2.stderr
new file mode 100644 (file)
index 0000000..f2eddf2
--- /dev/null
@@ -0,0 +1,23 @@
+error[E0382]: use of moved value: `foo`
+  --> $DIR/as-ref-2.rs:12:14
+   |
+LL |     let foo = Some(Struct);
+   |         --- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
+LL |     let _x: Option<Struct> = foo.map(|s| bar(&s));
+   |                                  ---------------- `foo` moved due to this method call
+LL |     let _y = foo;
+   |              ^^^ value used here after move
+   |
+note: this function takes ownership of the receiver `self`, which moves `foo`
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+   |
+LL |     pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
+   |                                      ^^^^
+help: consider calling `.as_ref()` to borrow the type's contents
+   |
+LL |     let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s));
+   |                                  ^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
index 03f04c389f1f3fbcbcbf9b0145fd133d0f06f7b8..46d9461538c7857a5aad9fa33e1c41c4168f679c 100644 (file)
@@ -1,25 +1,20 @@
 struct Foo;
+
 fn takes_ref(_: &Foo) {}
 
 fn main() {
-  let ref opt = Some(Foo);
-  opt.map(|arg| takes_ref(arg));
-  //~^ ERROR mismatched types [E0308]
-  opt.and_then(|arg| Some(takes_ref(arg)));
-  //~^ ERROR mismatched types [E0308]
-  let ref opt: Result<_, ()> = Ok(Foo);
-  opt.map(|arg| takes_ref(arg));
-  //~^ ERROR mismatched types [E0308]
-  opt.and_then(|arg| Ok(takes_ref(arg)));
-  //~^ ERROR mismatched types [E0308]
-  let x: &Option<usize> = &Some(3);
-  let y: Option<&usize> = x;
-  //~^ ERROR mismatched types [E0308]
-  let x: &Result<usize, usize> = &Ok(3);
-  let y: Result<&usize, &usize> = x;
-  //~^ ERROR mismatched types [E0308]
-  // note: do not suggest because of `E: usize`
-  let x: &Result<usize, usize> = &Ok(3);
-  let y: Result<&usize, usize> = x;
-  //~^ ERROR mismatched types [E0308]
+    let ref opt = Some(Foo);
+    opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
+    opt.and_then(|arg| Some(takes_ref(arg))); //~ ERROR mismatched types [E0308]
+    let ref opt: Result<_, ()> = Ok(Foo);
+    opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
+    opt.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308]
+    let x: &Option<usize> = &Some(3);
+    let y: Option<&usize> = x; //~ ERROR mismatched types [E0308]
+    let x: &Result<usize, usize> = &Ok(3);
+    let y: Result<&usize, &usize> = x;
+    //~^ ERROR mismatched types [E0308]
+    // note: do not suggest because of `E: usize`
+    let x: &Result<usize, usize> = &Ok(3);
+    let y: Result<&usize, usize> = x; //~ ERROR mismatched types [E0308]
 }
index 7e4d7fb3933d39152c65d6e6f85e9d0bf7ed01fb..dc5d7efd752cfd0ba65be033b8d6ca0c51aef9c3 100644 (file)
@@ -1,70 +1,70 @@
 error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:6:27
+  --> $DIR/as-ref.rs:7:29
    |
-LL |   opt.map(|arg| takes_ref(arg));
-   |       ---                 ^^^ expected `&Foo`, found struct `Foo`
-   |       |
-   |       help: consider using `as_ref` instead: `as_ref().map`
+LL |     opt.map(|arg| takes_ref(arg));
+   |         ---                 ^^^ expected `&Foo`, found struct `Foo`
+   |         |
+   |         help: consider using `as_ref` instead: `as_ref().map`
 
 error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:8:37
+  --> $DIR/as-ref.rs:8:39
    |
-LL |   opt.and_then(|arg| Some(takes_ref(arg)));
-   |       --------                      ^^^ expected `&Foo`, found struct `Foo`
-   |       |
-   |       help: consider using `as_ref` instead: `as_ref().and_then`
+LL |     opt.and_then(|arg| Some(takes_ref(arg)));
+   |         --------                      ^^^ expected `&Foo`, found struct `Foo`
+   |         |
+   |         help: consider using `as_ref` instead: `as_ref().and_then`
 
 error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:11:27
+  --> $DIR/as-ref.rs:10:29
    |
-LL |   opt.map(|arg| takes_ref(arg));
-   |       ---                 ^^^ expected `&Foo`, found struct `Foo`
-   |       |
-   |       help: consider using `as_ref` instead: `as_ref().map`
+LL |     opt.map(|arg| takes_ref(arg));
+   |         ---                 ^^^ expected `&Foo`, found struct `Foo`
+   |         |
+   |         help: consider using `as_ref` instead: `as_ref().map`
 
 error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:13:35
+  --> $DIR/as-ref.rs:11:37
    |
-LL |   opt.and_then(|arg| Ok(takes_ref(arg)));
-   |       --------                    ^^^ expected `&Foo`, found struct `Foo`
-   |       |
-   |       help: consider using `as_ref` instead: `as_ref().and_then`
+LL |     opt.and_then(|arg| Ok(takes_ref(arg)));
+   |         --------                    ^^^ expected `&Foo`, found struct `Foo`
+   |         |
+   |         help: consider using `as_ref` instead: `as_ref().and_then`
 
 error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:16:27
+  --> $DIR/as-ref.rs:13:29
    |
-LL |   let y: Option<&usize> = x;
-   |          --------------   ^
-   |          |                |
-   |          |                expected enum `Option`, found `&Option<usize>`
-   |          |                help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
-   |          expected due to this
+LL |     let y: Option<&usize> = x;
+   |            --------------   ^
+   |            |                |
+   |            |                expected enum `Option`, found `&Option<usize>`
+   |            |                help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
+   |            expected due to this
    |
    = note:   expected enum `Option<&usize>`
            found reference `&Option<usize>`
 
 error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:19:35
+  --> $DIR/as-ref.rs:15:37
    |
-LL |   let y: Result<&usize, &usize> = x;
-   |          ----------------------   ^ expected enum `Result`, found reference
-   |          |
-   |          expected due to this
+LL |     let y: Result<&usize, &usize> = x;
+   |            ----------------------   ^ expected enum `Result`, found reference
+   |            |
+   |            expected due to this
    |
    = note:   expected enum `Result<&usize, &usize>`
            found reference `&Result<usize, usize>`
 help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
    |
-LL |   let y: Result<&usize, &usize> = x.as_ref();
-   |                                   ^^^^^^^^^^
+LL |     let y: Result<&usize, &usize> = x.as_ref();
+   |                                     ^^^^^^^^^^
 
 error[E0308]: mismatched types
-  --> $DIR/as-ref.rs:23:34
+  --> $DIR/as-ref.rs:19:36
    |
-LL |   let y: Result<&usize, usize> = x;
-   |          ---------------------   ^ expected enum `Result`, found reference
-   |          |
-   |          expected due to this
+LL |     let y: Result<&usize, usize> = x;
+   |            ---------------------   ^ expected enum `Result`, found reference
+   |            |
+   |            expected due to this
    |
    = note:   expected enum `Result<&usize, usize>`
            found reference `&Result<usize, usize>`
diff --git a/src/test/ui/suggestions/import-trait-for-method-call.rs b/src/test/ui/suggestions/import-trait-for-method-call.rs
new file mode 100644 (file)
index 0000000..646f68d
--- /dev/null
@@ -0,0 +1,9 @@
+use std::hash::BuildHasher;
+
+fn next_u64() -> u64 {
+    let bh = std::collections::hash_map::RandomState::new();
+    let h = bh.build_hasher();
+    h.finish() //~ ERROR no method named `finish` found for struct `DefaultHasher`
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/import-trait-for-method-call.stderr b/src/test/ui/suggestions/import-trait-for-method-call.stderr
new file mode 100644 (file)
index 0000000..f3ae205
--- /dev/null
@@ -0,0 +1,20 @@
+error[E0599]: no method named `finish` found for struct `DefaultHasher` in the current scope
+  --> $DIR/import-trait-for-method-call.rs:6:7
+   |
+LL |     h.finish()
+   |       ^^^^^^ method not found in `DefaultHasher`
+   | 
+  ::: $SRC_DIR/core/src/hash/mod.rs:LL:COL
+   |
+LL |     fn finish(&self) -> u64;
+   |        ------ the method is available for `DefaultHasher` here
+   |
+   = help: items from traits can only be used if the trait is in scope
+help: the following trait is implemented but not in scope; perhaps add a `use` for it:
+   |
+LL | use std::hash::Hasher;
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
index 19fc3406ccfe43c165b809364beb1c34fa880e52..5919a6f7492058988705b4de0d2f85f846115d0a 100644 (file)
@@ -9,8 +9,8 @@ LL | fn f(p: Path) { }
    = help: unsized fn params are gated as an unstable feature
 help: function arguments must have a statically known size, borrowed types always have a known size
    |
-LL | fn f(&p: Path) { }
-   |      ^
+LL | fn f(p: &Path) { }
+   |         ^
 
 error: aborting due to previous error
 
index 628b1896ace4f32a4b1baae1e18cf57a23b3ac8e..0b2a9a3541b39346a057aa246d42d9b7df41a49f 100644 (file)
@@ -1,9 +1,9 @@
 // run-pass
 // compile-flags: --test
 
-#![feature(main)]
+#![feature(rustc_attrs)]
 
 #![deny(dead_code)]
 
-#[main]
+#[rustc_main]
 fn foo() { panic!(); }
index bf5482056d4b414378fd4aba1a029bdc127e5f79..346aa868eb4673a8ae6d15a870079fa8dbbd45a8 100644 (file)
@@ -1,14 +1,14 @@
 // run-pass
 // compile-flags: --test
 
-#![feature(main)]
+#![feature(rustc_attrs)]
 
 #![allow(dead_code)]
 
 mod a {
     fn b() {
         (|| {
-            #[main]
+            #[rustc_main]
             fn c() { panic!(); }
         })();
     }
diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed
new file mode 100644 (file)
index 0000000..8a94aba
--- /dev/null
@@ -0,0 +1,13 @@
+// Regression test of #43913.
+
+// run-rustfix
+
+#![feature(trait_alias)]
+#![allow(bare_trait_objects, dead_code)]
+
+trait Strings = Iterator<Item=String>;
+
+struct Struct<S: Strings>(S);
+//~^ ERROR: expected trait, found type alias `Strings`
+
+fn main() {}
diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs
new file mode 100644 (file)
index 0000000..40c678c
--- /dev/null
@@ -0,0 +1,13 @@
+// Regression test of #43913.
+
+// run-rustfix
+
+#![feature(trait_alias)]
+#![allow(bare_trait_objects, dead_code)]
+
+type Strings = Iterator<Item=String>;
+
+struct Struct<S: Strings>(S);
+//~^ ERROR: expected trait, found type alias `Strings`
+
+fn main() {}
diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr
new file mode 100644 (file)
index 0000000..6e03eea
--- /dev/null
@@ -0,0 +1,14 @@
+error[E0404]: expected trait, found type alias `Strings`
+  --> $DIR/suggest-trait-alias-instead-of-type.rs:10:18
+   |
+LL | struct Struct<S: Strings>(S);
+   |                  ^^^^^^^ type aliases cannot be used as traits
+   |
+help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias
+   |
+LL | trait Strings = Iterator<Item=String>;
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0404`.
diff --git a/src/test/ui/traits/bad-method-typaram-kind.rs b/src/test/ui/traits/bad-method-typaram-kind.rs
new file mode 100644 (file)
index 0000000..b088eae
--- /dev/null
@@ -0,0 +1,14 @@
+fn foo<T:'static>() {
+    1.bar::<T>(); //~ ERROR `T` cannot be sent between threads safely
+}
+
+trait Bar {
+    fn bar<T:Send>(&self);
+}
+
+impl Bar for usize {
+    fn bar<T:Send>(&self) {
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/bad-method-typaram-kind.stderr b/src/test/ui/traits/bad-method-typaram-kind.stderr
new file mode 100644 (file)
index 0000000..fd3999a
--- /dev/null
@@ -0,0 +1,14 @@
+error[E0277]: `T` cannot be sent between threads safely
+  --> $DIR/bad-method-typaram-kind.rs:2:7
+   |
+LL |     1.bar::<T>();
+   |       ^^^ `T` cannot be sent between threads safely
+   |
+help: consider further restricting this bound
+   |
+LL | fn foo<T:'static + std::marker::Send>() {
+   |                  ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/traits/bad-sized.rs b/src/test/ui/traits/bad-sized.rs
new file mode 100644 (file)
index 0000000..a152196
--- /dev/null
@@ -0,0 +1,9 @@
+trait Trait {}
+
+pub fn main() {
+    let x: Vec<dyn Trait + Sized> = Vec::new();
+    //~^ ERROR only auto traits can be used as additional traits in a trait object
+    //~| ERROR the size for values of type
+    //~| ERROR the size for values of type
+    //~| ERROR the size for values of type
+}
diff --git a/src/test/ui/traits/bad-sized.stderr b/src/test/ui/traits/bad-sized.stderr
new file mode 100644 (file)
index 0000000..768893d
--- /dev/null
@@ -0,0 +1,50 @@
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/bad-sized.rs:4:28
+   |
+LL |     let x: Vec<dyn Trait + Sized> = Vec::new();
+   |                    -----   ^^^^^ additional non-auto trait
+   |                    |
+   |                    first non-auto trait
+   |
+   = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Trait + Sized {}`
+   = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
+
+error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
+  --> $DIR/bad-sized.rs:4:12
+   |
+LL |     let x: Vec<dyn Trait + Sized> = Vec::new();
+   |            ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   | 
+  ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+   |
+LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
+   |                - required by this bound in `Vec`
+   |
+   = help: the trait `Sized` is not implemented for `dyn Trait`
+
+error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
+  --> $DIR/bad-sized.rs:4:37
+   |
+LL |     let x: Vec<dyn Trait + Sized> = Vec::new();
+   |                                     ^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `dyn Trait`
+   = note: required by `Vec::<T>::new`
+
+error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
+  --> $DIR/bad-sized.rs:4:37
+   |
+LL |     let x: Vec<dyn Trait + Sized> = Vec::new();
+   |                                     ^^^ doesn't have a size known at compile-time
+   | 
+  ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+   |
+LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
+   |                - required by this bound in `Vec`
+   |
+   = help: the trait `Sized` is not implemented for `dyn Trait`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0225, E0277.
+For more information about an error, try `rustc --explain E0225`.
index e7fc0fa5ec0ecb0b6cc50531bfc2e2ce2b61c9c4..b8ae88ace02ddded1dfe449a44b204722b67caf6 100644 (file)
@@ -16,8 +16,8 @@ LL | fn foo(_x: Foo + Send) {
    = help: unsized fn params are gated as an unstable feature
 help: function arguments must have a statically known size, borrowed types always have a known size
    |
-LL | fn foo(&_x: Foo + Send) {
-   |        ^
+LL | fn foo(_x: &Foo + Send) {
+   |            ^
 
 error: aborting due to previous error; 1 warning emitted
 
index 68d527dc786aa2cc8406d9cdda218097a0755837..30daf8e27702441b0504987446bc7d6c30d70a38 100644 (file)
@@ -20,6 +20,9 @@ error[E0599]: no method named `b` found for struct `S` in the current scope
 LL | struct S;
    | --------- method `b` not found for this
 ...
+LL |         fn b(&self) { }
+   |            - the method is available for `S` here
+...
 LL |     S.b();
    |       ^ method not found in `S`
    |
diff --git a/src/test/ui/typeof/type_mismatch.rs b/src/test/ui/typeof/type_mismatch.rs
new file mode 100644 (file)
index 0000000..3f8339f
--- /dev/null
@@ -0,0 +1,9 @@
+// Test that using typeof results in the correct type mismatch errors instead of always assuming
+// `usize`, in addition to the pre-existing "typeof is reserved and unimplemented" error
+fn main() {
+    const a: u8 = 1;
+    let b: typeof(a) = 1i8;
+    //~^ ERROR `typeof` is a reserved keyword but unimplemented
+    //~| ERROR mismatched types
+    //~| expected `u8`, found `i8`
+}
diff --git a/src/test/ui/typeof/type_mismatch.stderr b/src/test/ui/typeof/type_mismatch.stderr
new file mode 100644 (file)
index 0000000..12fd7c9
--- /dev/null
@@ -0,0 +1,23 @@
+error[E0516]: `typeof` is a reserved keyword but unimplemented
+  --> $DIR/type_mismatch.rs:5:12
+   |
+LL |     let b: typeof(a) = 1i8;
+   |            ^^^^^^^^^ reserved keyword
+
+error[E0308]: mismatched types
+  --> $DIR/type_mismatch.rs:5:24
+   |
+LL |     let b: typeof(a) = 1i8;
+   |            ---------   ^^^ expected `u8`, found `i8`
+   |            |
+   |            expected due to this
+   |
+help: change the type of the numeric literal from `i8` to `u8`
+   |
+LL |     let b: typeof(a) = 1u8;
+   |                        ^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0308, E0516.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/unsized-tuple-impls.rs b/src/test/ui/unsized-tuple-impls.rs
deleted file mode 100644 (file)
index 5e385f3..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// run-pass
-
-#![feature(unsized_tuple_coercion)]
-
-use std::collections::HashSet;
-
-fn main() {
-    let x : &(i32, i32, [i32]) = &(0, 1, [2, 3]);
-    let y : &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]);
-    let mut a = [y, x];
-    a.sort();
-    assert_eq!(a, [x, y]);
-
-    assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]");
-
-    let mut h = HashSet::new();
-    h.insert(x);
-    h.insert(y);
-    assert!(h.contains(x));
-    assert!(h.contains(y));
-}
diff --git a/src/test/ui/unsized.rs b/src/test/ui/unsized.rs
deleted file mode 100644 (file)
index 5430483..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-// run-pass
-
-#![allow(type_alias_bounds)]
-#![allow(dead_code)]
-// Test syntax checks for `?Sized` syntax.
-
-use std::marker::PhantomData;
-
-trait T1  { }
-pub trait T2  { }
-trait T3<X: T1> : T2 { }
-trait T4<X: ?Sized> { }
-trait T5<X: ?Sized, Y> { }
-trait T6<Y, X: ?Sized> { }
-trait T7<X: ?Sized, Y: ?Sized> { }
-trait T8<X: ?Sized+T2> { }
-trait T9<X: T2 + ?Sized> { }
-struct S1<X: ?Sized>(PhantomData<X>);
-enum E<X: ?Sized> { E1(PhantomData<X>) }
-impl <X: ?Sized> T1 for S1<X> {}
-fn f<X: ?Sized>() {}
-type TT<T: ?Sized> = T;
-
-pub fn main() {
-}
diff --git a/src/test/ui/unsized/unsized-fn-arg.fixed b/src/test/ui/unsized/unsized-fn-arg.fixed
new file mode 100644 (file)
index 0000000..2c686c6
--- /dev/null
@@ -0,0 +1,6 @@
+// run-rustfix
+#![crate_type="lib"]
+#![allow(unused)]
+
+fn f<T: ?Sized>(t: &T) {}
+//~^ ERROR the size for values of type `T` cannot be known at compilation time
diff --git a/src/test/ui/unsized/unsized-fn-arg.rs b/src/test/ui/unsized/unsized-fn-arg.rs
new file mode 100644 (file)
index 0000000..9fc08bd
--- /dev/null
@@ -0,0 +1,6 @@
+// run-rustfix
+#![crate_type="lib"]
+#![allow(unused)]
+
+fn f<T: ?Sized>(t: T) {}
+//~^ ERROR the size for values of type `T` cannot be known at compilation time
diff --git a/src/test/ui/unsized/unsized-fn-arg.stderr b/src/test/ui/unsized/unsized-fn-arg.stderr
new file mode 100644 (file)
index 0000000..6b802dd
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0277]: the size for values of type `T` cannot be known at compilation time
+  --> $DIR/unsized-fn-arg.rs:5:17
+   |
+LL | fn f<T: ?Sized>(t: T) {}
+   |      -          ^ doesn't have a size known at compile-time
+   |      |
+   |      this type parameter needs to be `std::marker::Sized`
+   |
+   = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL | fn f<T: ?Sized>(t: &T) {}
+   |                    ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized/unsized-tuple-impls.rs b/src/test/ui/unsized/unsized-tuple-impls.rs
new file mode 100644 (file)
index 0000000..5e385f3
--- /dev/null
@@ -0,0 +1,21 @@
+// run-pass
+
+#![feature(unsized_tuple_coercion)]
+
+use std::collections::HashSet;
+
+fn main() {
+    let x : &(i32, i32, [i32]) = &(0, 1, [2, 3]);
+    let y : &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]);
+    let mut a = [y, x];
+    a.sort();
+    assert_eq!(a, [x, y]);
+
+    assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]");
+
+    let mut h = HashSet::new();
+    h.insert(x);
+    h.insert(y);
+    assert!(h.contains(x));
+    assert!(h.contains(y));
+}
diff --git a/src/test/ui/unsized/unsized.rs b/src/test/ui/unsized/unsized.rs
new file mode 100644 (file)
index 0000000..5430483
--- /dev/null
@@ -0,0 +1,25 @@
+// run-pass
+
+#![allow(type_alias_bounds)]
+#![allow(dead_code)]
+// Test syntax checks for `?Sized` syntax.
+
+use std::marker::PhantomData;
+
+trait T1  { }
+pub trait T2  { }
+trait T3<X: T1> : T2 { }
+trait T4<X: ?Sized> { }
+trait T5<X: ?Sized, Y> { }
+trait T6<Y, X: ?Sized> { }
+trait T7<X: ?Sized, Y: ?Sized> { }
+trait T8<X: ?Sized+T2> { }
+trait T9<X: T2 + ?Sized> { }
+struct S1<X: ?Sized>(PhantomData<X>);
+enum E<X: ?Sized> { E1(PhantomData<X>) }
+impl <X: ?Sized> T1 for S1<X> {}
+fn f<X: ?Sized>() {}
+type TT<T: ?Sized> = T;
+
+pub fn main() {
+}
diff --git a/src/test/ui/unsized/unsized2.rs b/src/test/ui/unsized/unsized2.rs
new file mode 100644 (file)
index 0000000..be44063
--- /dev/null
@@ -0,0 +1,97 @@
+// run-pass
+
+#![allow(unconditional_recursion)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_imports)]
+#![feature(box_syntax)]
+
+// Test sized-ness checking in substitution.
+
+use std::marker;
+
+// Unbounded.
+fn f1<X: ?Sized>(x: &X) {
+    f1::<X>(x);
+}
+fn f2<X>(x: &X) {
+    f1::<X>(x);
+    f2::<X>(x);
+}
+
+// Bounded.
+trait T { fn dummy(&self) { } }
+fn f3<X: T+?Sized>(x: &X) {
+    f3::<X>(x);
+}
+fn f4<X: T>(x: &X) {
+    f3::<X>(x);
+    f4::<X>(x);
+}
+
+// Self type.
+trait T2 {
+    fn f() -> Box<Self>;
+}
+struct S;
+impl T2 for S {
+    fn f() -> Box<S> {
+        box S
+    }
+}
+fn f5<X: ?Sized+T2>(x: &X) {
+    let _: Box<X> = T2::f();
+}
+fn f6<X: T2>(x: &X) {
+    let _: Box<X> = T2::f();
+}
+
+trait T3 {
+    fn f() -> Box<Self>;
+}
+impl T3 for S {
+    fn f() -> Box<S> {
+        box S
+    }
+}
+fn f7<X: ?Sized+T3>(x: &X) {
+    // This is valid, but the unsized bound on X is irrelevant because any type
+    // which implements T3 must have statically known size.
+    let _: Box<X> = T3::f();
+}
+
+trait T4<X> {
+    fn dummy(&self) { }
+    fn m1(&self, x: &dyn T4<X>, y: X);
+    fn m2(&self, x: &dyn T5<X>, y: X);
+}
+trait T5<X: ?Sized> {
+    fn dummy(&self) { }
+    // not an error (for now)
+    fn m1(&self, x: &dyn T4<X>);
+    fn m2(&self, x: &dyn T5<X>);
+}
+
+trait T6<X: T> {
+    fn dummy(&self) { }
+    fn m1(&self, x: &dyn T4<X>);
+    fn m2(&self, x: &dyn T5<X>);
+}
+trait T7<X: ?Sized+T> {
+    fn dummy(&self) { }
+    // not an error (for now)
+    fn m1(&self, x: &dyn T4<X>);
+    fn m2(&self, x: &dyn T5<X>);
+}
+
+// The last field in a struct may be unsized
+struct S2<X: ?Sized> {
+    f: X,
+}
+struct S3<X: ?Sized> {
+    f1: isize,
+    f2: X,
+}
+
+pub fn main() {
+}
diff --git a/src/test/ui/unsized/unsized3-rpass.rs b/src/test/ui/unsized/unsized3-rpass.rs
new file mode 100644 (file)
index 0000000..65efbd6
--- /dev/null
@@ -0,0 +1,96 @@
+// run-pass
+// Test structs with always-unsized fields.
+
+
+#![allow(warnings)]
+#![feature(box_syntax, unsize, raw)]
+
+use std::mem;
+use std::raw;
+use std::slice;
+
+struct Foo<T> {
+    f: [T],
+}
+
+struct Bar {
+    f1: usize,
+    f2: [usize],
+}
+
+struct Baz {
+    f1: usize,
+    f2: str,
+}
+
+trait Tr {
+    fn foo(&self) -> usize;
+}
+
+struct St {
+    f: usize
+}
+
+impl Tr for St {
+    fn foo(&self) -> usize {
+        self.f
+    }
+}
+
+struct Qux<'a> {
+    f: Tr+'a
+}
+
+pub fn main() {
+    let _: &Foo<f64>;
+    let _: &Bar;
+    let _: &Baz;
+
+    let _: Box<Foo<i32>>;
+    let _: Box<Bar>;
+    let _: Box<Baz>;
+
+    let _ = mem::size_of::<Box<Foo<u8>>>();
+    let _ = mem::size_of::<Box<Bar>>();
+    let _ = mem::size_of::<Box<Baz>>();
+
+    unsafe {
+        struct Foo_<T> {
+            f: [T; 3]
+        }
+
+        let data: Box<Foo_<i32>> = box Foo_{f: [1, 2, 3] };
+        let x: &Foo<i32> = mem::transmute(slice::from_raw_parts(&*data, 3));
+        assert_eq!(x.f.len(), 3);
+        assert_eq!(x.f[0], 1);
+
+        struct Baz_ {
+            f1: usize,
+            f2: [u8; 5],
+        }
+
+        let data: Box<_> = box Baz_ {
+            f1: 42, f2: ['a' as u8, 'b' as u8, 'c' as u8, 'd' as u8, 'e' as u8] };
+        let x: &Baz = mem::transmute(slice::from_raw_parts(&*data, 5));
+        assert_eq!(x.f1, 42);
+        let chs: Vec<char> = x.f2.chars().collect();
+        assert_eq!(chs.len(), 5);
+        assert_eq!(chs[0], 'a');
+        assert_eq!(chs[1], 'b');
+        assert_eq!(chs[2], 'c');
+        assert_eq!(chs[3], 'd');
+        assert_eq!(chs[4], 'e');
+
+        struct Qux_ {
+            f: St
+        }
+
+        let obj: Box<St> = box St { f: 42 };
+        let obj: &Tr = &*obj;
+        let obj: raw::TraitObject = mem::transmute(&*obj);
+        let data: Box<_> = box Qux_{ f: St { f: 234 } };
+        let x: &Qux = mem::transmute(raw::TraitObject { vtable: obj.vtable,
+                                                        data: mem::transmute(&*data) });
+        assert_eq!(x.f.foo(), 234);
+    }
+}
diff --git a/src/test/ui/unsized/unsized3.rs b/src/test/ui/unsized/unsized3.rs
new file mode 100644 (file)
index 0000000..f5b5d02
--- /dev/null
@@ -0,0 +1,51 @@
+// Test sized-ness checking in substitution within fn bodies..
+
+use std::marker;
+
+// Unbounded.
+fn f1<X: ?Sized>(x: &X) {
+    f2::<X>(x);
+    //~^ ERROR the size for values of type
+}
+fn f2<X>(x: &X) {
+}
+
+// Bounded.
+trait T {
+    fn foo(&self) { }
+}
+fn f3<X: ?Sized + T>(x: &X) {
+    f4::<X>(x);
+    //~^ ERROR the size for values of type
+}
+fn f4<X: T>(x: &X) {
+}
+
+fn f5<Y>(x: &Y) {}
+fn f6<X: ?Sized>(x: &X) {}
+
+// Test with unsized struct.
+struct S<X: ?Sized> {
+    x: X,
+}
+
+fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) {
+    f5(x1);
+    //~^ ERROR the size for values of type
+    f6(x2); // ok
+}
+
+// Test some tuples.
+fn f9<X: ?Sized>(x1: Box<S<X>>) {
+    f5(&(*x1, 34));
+    //~^ ERROR the size for values of type
+}
+
+fn f10<X: ?Sized>(x1: Box<S<X>>) {
+    f5(&(32, *x1));
+    //~^ ERROR the size for values of type
+    //~| ERROR the size for values of type
+}
+
+pub fn main() {
+}
diff --git a/src/test/ui/unsized/unsized3.stderr b/src/test/ui/unsized/unsized3.stderr
new file mode 100644 (file)
index 0000000..bd36008
--- /dev/null
@@ -0,0 +1,109 @@
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized3.rs:7:13
+   |
+LL | fn f1<X: ?Sized>(x: &X) {
+   |       - this type parameter needs to be `std::marker::Sized`
+LL |     f2::<X>(x);
+   |             ^ doesn't have a size known at compile-time
+...
+LL | fn f2<X>(x: &X) {
+   |       - required by this bound in `f2`
+   |
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL | fn f2<X: ?Sized>(x: &X) {
+   |        ^^^^^^^^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized3.rs:18:13
+   |
+LL | fn f3<X: ?Sized + T>(x: &X) {
+   |       - this type parameter needs to be `std::marker::Sized`
+LL |     f4::<X>(x);
+   |             ^ doesn't have a size known at compile-time
+...
+LL | fn f4<X: T>(x: &X) {
+   |       - required by this bound in `f4`
+   |
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL | fn f4<X: T + ?Sized>(x: &X) {
+   |            ^^^^^^^^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized3.rs:33:8
+   |
+LL | fn f5<Y>(x: &Y) {}
+   |       - required by this bound in `f5`
+...
+LL | fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) {
+   |       - this type parameter needs to be `std::marker::Sized`
+LL |     f5(x1);
+   |        ^^ doesn't have a size known at compile-time
+   |
+note: required because it appears within the type `S<X>`
+  --> $DIR/unsized3.rs:28:8
+   |
+LL | struct S<X: ?Sized> {
+   |        ^
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL | fn f5<Y: ?Sized>(x: &Y) {}
+   |        ^^^^^^^^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized3.rs:40:8
+   |
+LL | fn f9<X: ?Sized>(x1: Box<S<X>>) {
+   |       - this type parameter needs to be `std::marker::Sized`
+LL |     f5(&(*x1, 34));
+   |        ^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+note: required because it appears within the type `S<X>`
+  --> $DIR/unsized3.rs:28:8
+   |
+LL | struct S<X: ?Sized> {
+   |        ^
+   = note: only the last element of a tuple may have a dynamically sized type
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized3.rs:45:9
+   |
+LL | fn f10<X: ?Sized>(x1: Box<S<X>>) {
+   |        - this type parameter needs to be `std::marker::Sized`
+LL |     f5(&(32, *x1));
+   |         ^^^^^^^^^ doesn't have a size known at compile-time
+   |
+note: required because it appears within the type `S<X>`
+  --> $DIR/unsized3.rs:28:8
+   |
+LL | struct S<X: ?Sized> {
+   |        ^
+   = note: required because it appears within the type `({integer}, S<X>)`
+   = note: tuples must have a statically known size to be initialized
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized3.rs:45:8
+   |
+LL | fn f5<Y>(x: &Y) {}
+   |       - required by this bound in `f5`
+...
+LL | fn f10<X: ?Sized>(x1: Box<S<X>>) {
+   |        - this type parameter needs to be `std::marker::Sized`
+LL |     f5(&(32, *x1));
+   |        ^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+note: required because it appears within the type `S<X>`
+  --> $DIR/unsized3.rs:28:8
+   |
+LL | struct S<X: ?Sized> {
+   |        ^
+   = note: required because it appears within the type `({integer}, S<X>)`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL | fn f5<Y: ?Sized>(x: &Y) {}
+   |        ^^^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized/unsized5.rs b/src/test/ui/unsized/unsized5.rs
new file mode 100644 (file)
index 0000000..befd224
--- /dev/null
@@ -0,0 +1,34 @@
+// Test `?Sized` types not allowed in fields (except the last one).
+
+struct S1<X: ?Sized> {
+    f1: X,
+    //~^ ERROR the size for values of type
+    f2: isize,
+}
+struct S2<X: ?Sized> {
+    f: isize,
+    g: X,
+    //~^ ERROR the size for values of type
+    h: isize,
+}
+struct S3 {
+    f: str,
+    //~^ ERROR the size for values of type
+    g: [usize]
+}
+struct S4 {
+    f: [u8],
+    //~^ ERROR the size for values of type
+    g: usize
+}
+enum E<X: ?Sized> {
+    V1(X, isize),
+    //~^ ERROR the size for values of type
+}
+enum F<X: ?Sized> {
+    V2{f1: X, f: isize},
+    //~^ ERROR the size for values of type
+}
+
+pub fn main() {
+}
diff --git a/src/test/ui/unsized/unsized5.stderr b/src/test/ui/unsized/unsized5.stderr
new file mode 100644 (file)
index 0000000..0bfd456
--- /dev/null
@@ -0,0 +1,116 @@
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized5.rs:4:9
+   |
+LL | struct S1<X: ?Sized> {
+   |           - this type parameter needs to be `std::marker::Sized`
+LL |     f1: X,
+   |         ^ doesn't have a size known at compile-time
+   |
+   = note: only the last field of a struct may have a dynamically sized type
+   = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+   |
+LL |     f1: &X,
+   |         ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+   |
+LL |     f1: Box<X>,
+   |         ^^^^ ^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized5.rs:10:8
+   |
+LL | struct S2<X: ?Sized> {
+   |           - this type parameter needs to be `std::marker::Sized`
+LL |     f: isize,
+LL |     g: X,
+   |        ^ doesn't have a size known at compile-time
+   |
+   = note: only the last field of a struct may have a dynamically sized type
+   = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+   |
+LL |     g: &X,
+   |        ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+   |
+LL |     g: Box<X>,
+   |        ^^^^ ^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized5.rs:15:8
+   |
+LL |     f: str,
+   |        ^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: only the last field of a struct may have a dynamically sized type
+   = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+   |
+LL |     f: &str,
+   |        ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+   |
+LL |     f: Box<str>,
+   |        ^^^^   ^
+
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+  --> $DIR/unsized5.rs:20:8
+   |
+LL |     f: [u8],
+   |        ^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[u8]`
+   = note: only the last field of a struct may have a dynamically sized type
+   = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+   |
+LL |     f: &[u8],
+   |        ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+   |
+LL |     f: Box<[u8]>,
+   |        ^^^^    ^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized5.rs:25:8
+   |
+LL | enum E<X: ?Sized> {
+   |        - this type parameter needs to be `std::marker::Sized`
+LL |     V1(X, isize),
+   |        ^ doesn't have a size known at compile-time
+   |
+   = note: no field of an enum variant may have a dynamically sized type
+   = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+   |
+LL |     V1(&X, isize),
+   |        ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+   |
+LL |     V1(Box<X>, isize),
+   |        ^^^^ ^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized5.rs:29:12
+   |
+LL | enum F<X: ?Sized> {
+   |        - this type parameter needs to be `std::marker::Sized`
+LL |     V2{f1: X, f: isize},
+   |            ^ doesn't have a size known at compile-time
+   |
+   = note: no field of an enum variant may have a dynamically sized type
+   = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+   |
+LL |     V2{f1: &X, f: isize},
+   |            ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+   |
+LL |     V2{f1: Box<X>, f: isize},
+   |            ^^^^ ^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized/unsized6.rs b/src/test/ui/unsized/unsized6.rs
new file mode 100644 (file)
index 0000000..7913355
--- /dev/null
@@ -0,0 +1,44 @@
+// Test `?Sized` local variables.
+
+trait T {}
+
+fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
+    let _: W; // <-- this is OK, no bindings created, no initializer.
+    let _: (isize, (X, isize));
+    //~^ ERROR the size for values of type
+    let y: Y;
+    //~^ ERROR the size for values of type
+    let y: (isize, (Z, usize));
+    //~^ ERROR the size for values of type
+}
+fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
+    let y: X;
+    //~^ ERROR the size for values of type
+    let y: (isize, (Y, isize));
+    //~^ ERROR the size for values of type
+}
+
+fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+    let y: X = *x1;
+    //~^ ERROR the size for values of type
+    let y = *x2;
+    //~^ ERROR the size for values of type
+    let (y, z) = (*x3, 4);
+    //~^ ERROR the size for values of type
+}
+fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+    let y: X = *x1;
+    //~^ ERROR the size for values of type
+    let y = *x2;
+    //~^ ERROR the size for values of type
+    let (y, z) = (*x3, 4);
+    //~^ ERROR the size for values of type
+}
+
+fn g1<X: ?Sized>(x: X) {}
+//~^ ERROR the size for values of type
+fn g2<X: ?Sized + T>(x: X) {}
+//~^ ERROR the size for values of type
+
+pub fn main() {
+}
diff --git a/src/test/ui/unsized/unsized6.stderr b/src/test/ui/unsized/unsized6.stderr
new file mode 100644 (file)
index 0000000..8e5734d
--- /dev/null
@@ -0,0 +1,157 @@
+error[E0277]: the size for values of type `Y` cannot be known at compilation time
+  --> $DIR/unsized6.rs:9:9
+   |
+LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
+   |                             - this type parameter needs to be `std::marker::Sized`
+...
+LL |     let y: Y;
+   |         ^ doesn't have a size known at compile-time
+   |
+   = note: all local variables must have a statically known size
+   = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:7:12
+   |
+LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
+   |                  - this type parameter needs to be `std::marker::Sized`
+LL |     let _: W; // <-- this is OK, no bindings created, no initializer.
+LL |     let _: (isize, (X, isize));
+   |            ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = note: only the last element of a tuple may have a dynamically sized type
+
+error[E0277]: the size for values of type `Z` cannot be known at compilation time
+  --> $DIR/unsized6.rs:11:12
+   |
+LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
+   |                                        - this type parameter needs to be `std::marker::Sized`
+...
+LL |     let y: (isize, (Z, usize));
+   |            ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = note: only the last element of a tuple may have a dynamically sized type
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:15:9
+   |
+LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
+   |       - this type parameter needs to be `std::marker::Sized`
+LL |     let y: X;
+   |         ^ doesn't have a size known at compile-time
+   |
+   = note: all local variables must have a statically known size
+   = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `Y` cannot be known at compilation time
+  --> $DIR/unsized6.rs:17:12
+   |
+LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
+   |                  - this type parameter needs to be `std::marker::Sized`
+...
+LL |     let y: (isize, (Y, isize));
+   |            ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = note: only the last element of a tuple may have a dynamically sized type
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:22:9
+   |
+LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+   |       - this type parameter needs to be `std::marker::Sized`
+LL |     let y: X = *x1;
+   |         ^ doesn't have a size known at compile-time
+   |
+   = note: all local variables must have a statically known size
+   = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:24:9
+   |
+LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+   |       - this type parameter needs to be `std::marker::Sized`
+...
+LL |     let y = *x2;
+   |         ^ doesn't have a size known at compile-time
+   |
+   = note: all local variables must have a statically known size
+   = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:26:10
+   |
+LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+   |       - this type parameter needs to be `std::marker::Sized`
+...
+LL |     let (y, z) = (*x3, 4);
+   |          ^ doesn't have a size known at compile-time
+   |
+   = note: all local variables must have a statically known size
+   = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:30:9
+   |
+LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+   |       - this type parameter needs to be `std::marker::Sized`
+LL |     let y: X = *x1;
+   |         ^ doesn't have a size known at compile-time
+   |
+   = note: all local variables must have a statically known size
+   = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:32:9
+   |
+LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+   |       - this type parameter needs to be `std::marker::Sized`
+...
+LL |     let y = *x2;
+   |         ^ doesn't have a size known at compile-time
+   |
+   = note: all local variables must have a statically known size
+   = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:34:10
+   |
+LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
+   |       - this type parameter needs to be `std::marker::Sized`
+...
+LL |     let (y, z) = (*x3, 4);
+   |          ^ doesn't have a size known at compile-time
+   |
+   = note: all local variables must have a statically known size
+   = help: unsized locals are gated as an unstable feature
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:38:18
+   |
+LL | fn g1<X: ?Sized>(x: X) {}
+   |       -          ^ doesn't have a size known at compile-time
+   |       |
+   |       this type parameter needs to be `std::marker::Sized`
+   |
+   = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL | fn g1<X: ?Sized>(x: &X) {}
+   |                     ^
+
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized6.rs:40:22
+   |
+LL | fn g2<X: ?Sized + T>(x: X) {}
+   |       -              ^ doesn't have a size known at compile-time
+   |       |
+   |       this type parameter needs to be `std::marker::Sized`
+   |
+   = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL | fn g2<X: ?Sized + T>(x: &X) {}
+   |                         ^
+
+error: aborting due to 13 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized/unsized7.rs b/src/test/ui/unsized/unsized7.rs
new file mode 100644 (file)
index 0000000..422a784
--- /dev/null
@@ -0,0 +1,16 @@
+// Test sized-ness checking in substitution in impls.
+
+trait T {}
+
+// I would like these to fail eventually.
+// impl - bounded
+trait T1<Z: T> {
+    fn dummy(&self) -> Z;
+}
+
+struct S3<Y: ?Sized>(Box<Y>);
+impl<X: ?Sized + T> T1<X> for S3<X> {
+    //~^ ERROR the size for values of type
+}
+
+fn main() { }
diff --git a/src/test/ui/unsized/unsized7.stderr b/src/test/ui/unsized/unsized7.stderr
new file mode 100644 (file)
index 0000000..7dbddd4
--- /dev/null
@@ -0,0 +1,19 @@
+error[E0277]: the size for values of type `X` cannot be known at compilation time
+  --> $DIR/unsized7.rs:12:21
+   |
+LL | trait T1<Z: T> {
+   |          - required by this bound in `T1`
+...
+LL | impl<X: ?Sized + T> T1<X> for S3<X> {
+   |      -              ^^^^^ doesn't have a size known at compile-time
+   |      |
+   |      this type parameter needs to be `std::marker::Sized`
+   |
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL | trait T1<Z: T + ?Sized> {
+   |               ^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized2.rs b/src/test/ui/unsized2.rs
deleted file mode 100644 (file)
index be44063..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-// run-pass
-
-#![allow(unconditional_recursion)]
-#![allow(dead_code)]
-#![allow(unused_variables)]
-#![allow(unused_imports)]
-#![feature(box_syntax)]
-
-// Test sized-ness checking in substitution.
-
-use std::marker;
-
-// Unbounded.
-fn f1<X: ?Sized>(x: &X) {
-    f1::<X>(x);
-}
-fn f2<X>(x: &X) {
-    f1::<X>(x);
-    f2::<X>(x);
-}
-
-// Bounded.
-trait T { fn dummy(&self) { } }
-fn f3<X: T+?Sized>(x: &X) {
-    f3::<X>(x);
-}
-fn f4<X: T>(x: &X) {
-    f3::<X>(x);
-    f4::<X>(x);
-}
-
-// Self type.
-trait T2 {
-    fn f() -> Box<Self>;
-}
-struct S;
-impl T2 for S {
-    fn f() -> Box<S> {
-        box S
-    }
-}
-fn f5<X: ?Sized+T2>(x: &X) {
-    let _: Box<X> = T2::f();
-}
-fn f6<X: T2>(x: &X) {
-    let _: Box<X> = T2::f();
-}
-
-trait T3 {
-    fn f() -> Box<Self>;
-}
-impl T3 for S {
-    fn f() -> Box<S> {
-        box S
-    }
-}
-fn f7<X: ?Sized+T3>(x: &X) {
-    // This is valid, but the unsized bound on X is irrelevant because any type
-    // which implements T3 must have statically known size.
-    let _: Box<X> = T3::f();
-}
-
-trait T4<X> {
-    fn dummy(&self) { }
-    fn m1(&self, x: &dyn T4<X>, y: X);
-    fn m2(&self, x: &dyn T5<X>, y: X);
-}
-trait T5<X: ?Sized> {
-    fn dummy(&self) { }
-    // not an error (for now)
-    fn m1(&self, x: &dyn T4<X>);
-    fn m2(&self, x: &dyn T5<X>);
-}
-
-trait T6<X: T> {
-    fn dummy(&self) { }
-    fn m1(&self, x: &dyn T4<X>);
-    fn m2(&self, x: &dyn T5<X>);
-}
-trait T7<X: ?Sized+T> {
-    fn dummy(&self) { }
-    // not an error (for now)
-    fn m1(&self, x: &dyn T4<X>);
-    fn m2(&self, x: &dyn T5<X>);
-}
-
-// The last field in a struct may be unsized
-struct S2<X: ?Sized> {
-    f: X,
-}
-struct S3<X: ?Sized> {
-    f1: isize,
-    f2: X,
-}
-
-pub fn main() {
-}
diff --git a/src/test/ui/unsized3-rpass.rs b/src/test/ui/unsized3-rpass.rs
deleted file mode 100644 (file)
index 65efbd6..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-// run-pass
-// Test structs with always-unsized fields.
-
-
-#![allow(warnings)]
-#![feature(box_syntax, unsize, raw)]
-
-use std::mem;
-use std::raw;
-use std::slice;
-
-struct Foo<T> {
-    f: [T],
-}
-
-struct Bar {
-    f1: usize,
-    f2: [usize],
-}
-
-struct Baz {
-    f1: usize,
-    f2: str,
-}
-
-trait Tr {
-    fn foo(&self) -> usize;
-}
-
-struct St {
-    f: usize
-}
-
-impl Tr for St {
-    fn foo(&self) -> usize {
-        self.f
-    }
-}
-
-struct Qux<'a> {
-    f: Tr+'a
-}
-
-pub fn main() {
-    let _: &Foo<f64>;
-    let _: &Bar;
-    let _: &Baz;
-
-    let _: Box<Foo<i32>>;
-    let _: Box<Bar>;
-    let _: Box<Baz>;
-
-    let _ = mem::size_of::<Box<Foo<u8>>>();
-    let _ = mem::size_of::<Box<Bar>>();
-    let _ = mem::size_of::<Box<Baz>>();
-
-    unsafe {
-        struct Foo_<T> {
-            f: [T; 3]
-        }
-
-        let data: Box<Foo_<i32>> = box Foo_{f: [1, 2, 3] };
-        let x: &Foo<i32> = mem::transmute(slice::from_raw_parts(&*data, 3));
-        assert_eq!(x.f.len(), 3);
-        assert_eq!(x.f[0], 1);
-
-        struct Baz_ {
-            f1: usize,
-            f2: [u8; 5],
-        }
-
-        let data: Box<_> = box Baz_ {
-            f1: 42, f2: ['a' as u8, 'b' as u8, 'c' as u8, 'd' as u8, 'e' as u8] };
-        let x: &Baz = mem::transmute(slice::from_raw_parts(&*data, 5));
-        assert_eq!(x.f1, 42);
-        let chs: Vec<char> = x.f2.chars().collect();
-        assert_eq!(chs.len(), 5);
-        assert_eq!(chs[0], 'a');
-        assert_eq!(chs[1], 'b');
-        assert_eq!(chs[2], 'c');
-        assert_eq!(chs[3], 'd');
-        assert_eq!(chs[4], 'e');
-
-        struct Qux_ {
-            f: St
-        }
-
-        let obj: Box<St> = box St { f: 42 };
-        let obj: &Tr = &*obj;
-        let obj: raw::TraitObject = mem::transmute(&*obj);
-        let data: Box<_> = box Qux_{ f: St { f: 234 } };
-        let x: &Qux = mem::transmute(raw::TraitObject { vtable: obj.vtable,
-                                                        data: mem::transmute(&*data) });
-        assert_eq!(x.f.foo(), 234);
-    }
-}
diff --git a/src/test/ui/unsized3.rs b/src/test/ui/unsized3.rs
deleted file mode 100644 (file)
index f5b5d02..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-// Test sized-ness checking in substitution within fn bodies..
-
-use std::marker;
-
-// Unbounded.
-fn f1<X: ?Sized>(x: &X) {
-    f2::<X>(x);
-    //~^ ERROR the size for values of type
-}
-fn f2<X>(x: &X) {
-}
-
-// Bounded.
-trait T {
-    fn foo(&self) { }
-}
-fn f3<X: ?Sized + T>(x: &X) {
-    f4::<X>(x);
-    //~^ ERROR the size for values of type
-}
-fn f4<X: T>(x: &X) {
-}
-
-fn f5<Y>(x: &Y) {}
-fn f6<X: ?Sized>(x: &X) {}
-
-// Test with unsized struct.
-struct S<X: ?Sized> {
-    x: X,
-}
-
-fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) {
-    f5(x1);
-    //~^ ERROR the size for values of type
-    f6(x2); // ok
-}
-
-// Test some tuples.
-fn f9<X: ?Sized>(x1: Box<S<X>>) {
-    f5(&(*x1, 34));
-    //~^ ERROR the size for values of type
-}
-
-fn f10<X: ?Sized>(x1: Box<S<X>>) {
-    f5(&(32, *x1));
-    //~^ ERROR the size for values of type
-    //~| ERROR the size for values of type
-}
-
-pub fn main() {
-}
diff --git a/src/test/ui/unsized3.stderr b/src/test/ui/unsized3.stderr
deleted file mode 100644 (file)
index bd36008..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized3.rs:7:13
-   |
-LL | fn f1<X: ?Sized>(x: &X) {
-   |       - this type parameter needs to be `std::marker::Sized`
-LL |     f2::<X>(x);
-   |             ^ doesn't have a size known at compile-time
-...
-LL | fn f2<X>(x: &X) {
-   |       - required by this bound in `f2`
-   |
-help: consider relaxing the implicit `Sized` restriction
-   |
-LL | fn f2<X: ?Sized>(x: &X) {
-   |        ^^^^^^^^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized3.rs:18:13
-   |
-LL | fn f3<X: ?Sized + T>(x: &X) {
-   |       - this type parameter needs to be `std::marker::Sized`
-LL |     f4::<X>(x);
-   |             ^ doesn't have a size known at compile-time
-...
-LL | fn f4<X: T>(x: &X) {
-   |       - required by this bound in `f4`
-   |
-help: consider relaxing the implicit `Sized` restriction
-   |
-LL | fn f4<X: T + ?Sized>(x: &X) {
-   |            ^^^^^^^^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized3.rs:33:8
-   |
-LL | fn f5<Y>(x: &Y) {}
-   |       - required by this bound in `f5`
-...
-LL | fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) {
-   |       - this type parameter needs to be `std::marker::Sized`
-LL |     f5(x1);
-   |        ^^ doesn't have a size known at compile-time
-   |
-note: required because it appears within the type `S<X>`
-  --> $DIR/unsized3.rs:28:8
-   |
-LL | struct S<X: ?Sized> {
-   |        ^
-help: consider relaxing the implicit `Sized` restriction
-   |
-LL | fn f5<Y: ?Sized>(x: &Y) {}
-   |        ^^^^^^^^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized3.rs:40:8
-   |
-LL | fn f9<X: ?Sized>(x1: Box<S<X>>) {
-   |       - this type parameter needs to be `std::marker::Sized`
-LL |     f5(&(*x1, 34));
-   |        ^^^^^^^^^^ doesn't have a size known at compile-time
-   |
-note: required because it appears within the type `S<X>`
-  --> $DIR/unsized3.rs:28:8
-   |
-LL | struct S<X: ?Sized> {
-   |        ^
-   = note: only the last element of a tuple may have a dynamically sized type
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized3.rs:45:9
-   |
-LL | fn f10<X: ?Sized>(x1: Box<S<X>>) {
-   |        - this type parameter needs to be `std::marker::Sized`
-LL |     f5(&(32, *x1));
-   |         ^^^^^^^^^ doesn't have a size known at compile-time
-   |
-note: required because it appears within the type `S<X>`
-  --> $DIR/unsized3.rs:28:8
-   |
-LL | struct S<X: ?Sized> {
-   |        ^
-   = note: required because it appears within the type `({integer}, S<X>)`
-   = note: tuples must have a statically known size to be initialized
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized3.rs:45:8
-   |
-LL | fn f5<Y>(x: &Y) {}
-   |       - required by this bound in `f5`
-...
-LL | fn f10<X: ?Sized>(x1: Box<S<X>>) {
-   |        - this type parameter needs to be `std::marker::Sized`
-LL |     f5(&(32, *x1));
-   |        ^^^^^^^^^^ doesn't have a size known at compile-time
-   |
-note: required because it appears within the type `S<X>`
-  --> $DIR/unsized3.rs:28:8
-   |
-LL | struct S<X: ?Sized> {
-   |        ^
-   = note: required because it appears within the type `({integer}, S<X>)`
-help: consider relaxing the implicit `Sized` restriction
-   |
-LL | fn f5<Y: ?Sized>(x: &Y) {}
-   |        ^^^^^^^^
-
-error: aborting due to 6 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized5.rs b/src/test/ui/unsized5.rs
deleted file mode 100644 (file)
index befd224..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-// Test `?Sized` types not allowed in fields (except the last one).
-
-struct S1<X: ?Sized> {
-    f1: X,
-    //~^ ERROR the size for values of type
-    f2: isize,
-}
-struct S2<X: ?Sized> {
-    f: isize,
-    g: X,
-    //~^ ERROR the size for values of type
-    h: isize,
-}
-struct S3 {
-    f: str,
-    //~^ ERROR the size for values of type
-    g: [usize]
-}
-struct S4 {
-    f: [u8],
-    //~^ ERROR the size for values of type
-    g: usize
-}
-enum E<X: ?Sized> {
-    V1(X, isize),
-    //~^ ERROR the size for values of type
-}
-enum F<X: ?Sized> {
-    V2{f1: X, f: isize},
-    //~^ ERROR the size for values of type
-}
-
-pub fn main() {
-}
diff --git a/src/test/ui/unsized5.stderr b/src/test/ui/unsized5.stderr
deleted file mode 100644 (file)
index 0bfd456..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized5.rs:4:9
-   |
-LL | struct S1<X: ?Sized> {
-   |           - this type parameter needs to be `std::marker::Sized`
-LL |     f1: X,
-   |         ^ doesn't have a size known at compile-time
-   |
-   = note: only the last field of a struct may have a dynamically sized type
-   = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
-   |
-LL |     f1: &X,
-   |         ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
-   |
-LL |     f1: Box<X>,
-   |         ^^^^ ^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized5.rs:10:8
-   |
-LL | struct S2<X: ?Sized> {
-   |           - this type parameter needs to be `std::marker::Sized`
-LL |     f: isize,
-LL |     g: X,
-   |        ^ doesn't have a size known at compile-time
-   |
-   = note: only the last field of a struct may have a dynamically sized type
-   = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
-   |
-LL |     g: &X,
-   |        ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
-   |
-LL |     g: Box<X>,
-   |        ^^^^ ^
-
-error[E0277]: the size for values of type `str` cannot be known at compilation time
-  --> $DIR/unsized5.rs:15:8
-   |
-LL |     f: str,
-   |        ^^^ doesn't have a size known at compile-time
-   |
-   = help: the trait `Sized` is not implemented for `str`
-   = note: only the last field of a struct may have a dynamically sized type
-   = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
-   |
-LL |     f: &str,
-   |        ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
-   |
-LL |     f: Box<str>,
-   |        ^^^^   ^
-
-error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
-  --> $DIR/unsized5.rs:20:8
-   |
-LL |     f: [u8],
-   |        ^^^^ doesn't have a size known at compile-time
-   |
-   = help: the trait `Sized` is not implemented for `[u8]`
-   = note: only the last field of a struct may have a dynamically sized type
-   = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
-   |
-LL |     f: &[u8],
-   |        ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
-   |
-LL |     f: Box<[u8]>,
-   |        ^^^^    ^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized5.rs:25:8
-   |
-LL | enum E<X: ?Sized> {
-   |        - this type parameter needs to be `std::marker::Sized`
-LL |     V1(X, isize),
-   |        ^ doesn't have a size known at compile-time
-   |
-   = note: no field of an enum variant may have a dynamically sized type
-   = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
-   |
-LL |     V1(&X, isize),
-   |        ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
-   |
-LL |     V1(Box<X>, isize),
-   |        ^^^^ ^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized5.rs:29:12
-   |
-LL | enum F<X: ?Sized> {
-   |        - this type parameter needs to be `std::marker::Sized`
-LL |     V2{f1: X, f: isize},
-   |            ^ doesn't have a size known at compile-time
-   |
-   = note: no field of an enum variant may have a dynamically sized type
-   = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
-   |
-LL |     V2{f1: &X, f: isize},
-   |            ^
-help: the `Box` type always has a statically known size and allocates its contents in the heap
-   |
-LL |     V2{f1: Box<X>, f: isize},
-   |            ^^^^ ^
-
-error: aborting due to 6 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized6.rs b/src/test/ui/unsized6.rs
deleted file mode 100644 (file)
index 7913355..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// Test `?Sized` local variables.
-
-trait T {}
-
-fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
-    let _: W; // <-- this is OK, no bindings created, no initializer.
-    let _: (isize, (X, isize));
-    //~^ ERROR the size for values of type
-    let y: Y;
-    //~^ ERROR the size for values of type
-    let y: (isize, (Z, usize));
-    //~^ ERROR the size for values of type
-}
-fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
-    let y: X;
-    //~^ ERROR the size for values of type
-    let y: (isize, (Y, isize));
-    //~^ ERROR the size for values of type
-}
-
-fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
-    let y: X = *x1;
-    //~^ ERROR the size for values of type
-    let y = *x2;
-    //~^ ERROR the size for values of type
-    let (y, z) = (*x3, 4);
-    //~^ ERROR the size for values of type
-}
-fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
-    let y: X = *x1;
-    //~^ ERROR the size for values of type
-    let y = *x2;
-    //~^ ERROR the size for values of type
-    let (y, z) = (*x3, 4);
-    //~^ ERROR the size for values of type
-}
-
-fn g1<X: ?Sized>(x: X) {}
-//~^ ERROR the size for values of type
-fn g2<X: ?Sized + T>(x: X) {}
-//~^ ERROR the size for values of type
-
-pub fn main() {
-}
diff --git a/src/test/ui/unsized6.stderr b/src/test/ui/unsized6.stderr
deleted file mode 100644 (file)
index f9f7877..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-error[E0277]: the size for values of type `Y` cannot be known at compilation time
-  --> $DIR/unsized6.rs:9:9
-   |
-LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
-   |                             - this type parameter needs to be `std::marker::Sized`
-...
-LL |     let y: Y;
-   |         ^ doesn't have a size known at compile-time
-   |
-   = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:7:12
-   |
-LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
-   |                  - this type parameter needs to be `std::marker::Sized`
-LL |     let _: W; // <-- this is OK, no bindings created, no initializer.
-LL |     let _: (isize, (X, isize));
-   |            ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
-   |
-   = note: only the last element of a tuple may have a dynamically sized type
-
-error[E0277]: the size for values of type `Z` cannot be known at compilation time
-  --> $DIR/unsized6.rs:11:12
-   |
-LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) {
-   |                                        - this type parameter needs to be `std::marker::Sized`
-...
-LL |     let y: (isize, (Z, usize));
-   |            ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
-   |
-   = note: only the last element of a tuple may have a dynamically sized type
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:15:9
-   |
-LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
-   |       - this type parameter needs to be `std::marker::Sized`
-LL |     let y: X;
-   |         ^ doesn't have a size known at compile-time
-   |
-   = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `Y` cannot be known at compilation time
-  --> $DIR/unsized6.rs:17:12
-   |
-LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) {
-   |                  - this type parameter needs to be `std::marker::Sized`
-...
-LL |     let y: (isize, (Y, isize));
-   |            ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
-   |
-   = note: only the last element of a tuple may have a dynamically sized type
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:22:9
-   |
-LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
-   |       - this type parameter needs to be `std::marker::Sized`
-LL |     let y: X = *x1;
-   |         ^ doesn't have a size known at compile-time
-   |
-   = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:24:9
-   |
-LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
-   |       - this type parameter needs to be `std::marker::Sized`
-...
-LL |     let y = *x2;
-   |         ^ doesn't have a size known at compile-time
-   |
-   = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:26:10
-   |
-LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
-   |       - this type parameter needs to be `std::marker::Sized`
-...
-LL |     let (y, z) = (*x3, 4);
-   |          ^ doesn't have a size known at compile-time
-   |
-   = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:30:9
-   |
-LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
-   |       - this type parameter needs to be `std::marker::Sized`
-LL |     let y: X = *x1;
-   |         ^ doesn't have a size known at compile-time
-   |
-   = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:32:9
-   |
-LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
-   |       - this type parameter needs to be `std::marker::Sized`
-...
-LL |     let y = *x2;
-   |         ^ doesn't have a size known at compile-time
-   |
-   = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:34:10
-   |
-LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
-   |       - this type parameter needs to be `std::marker::Sized`
-...
-LL |     let (y, z) = (*x3, 4);
-   |          ^ doesn't have a size known at compile-time
-   |
-   = note: all local variables must have a statically known size
-   = help: unsized locals are gated as an unstable feature
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:38:18
-   |
-LL | fn g1<X: ?Sized>(x: X) {}
-   |       -          ^ doesn't have a size known at compile-time
-   |       |
-   |       this type parameter needs to be `std::marker::Sized`
-   |
-   = help: unsized fn params are gated as an unstable feature
-help: function arguments must have a statically known size, borrowed types always have a known size
-   |
-LL | fn g1<X: ?Sized>(&x: X) {}
-   |                  ^
-
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized6.rs:40:22
-   |
-LL | fn g2<X: ?Sized + T>(x: X) {}
-   |       -              ^ doesn't have a size known at compile-time
-   |       |
-   |       this type parameter needs to be `std::marker::Sized`
-   |
-   = help: unsized fn params are gated as an unstable feature
-help: function arguments must have a statically known size, borrowed types always have a known size
-   |
-LL | fn g2<X: ?Sized + T>(&x: X) {}
-   |                      ^
-
-error: aborting due to 13 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized7.rs b/src/test/ui/unsized7.rs
deleted file mode 100644 (file)
index 422a784..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-// Test sized-ness checking in substitution in impls.
-
-trait T {}
-
-// I would like these to fail eventually.
-// impl - bounded
-trait T1<Z: T> {
-    fn dummy(&self) -> Z;
-}
-
-struct S3<Y: ?Sized>(Box<Y>);
-impl<X: ?Sized + T> T1<X> for S3<X> {
-    //~^ ERROR the size for values of type
-}
-
-fn main() { }
diff --git a/src/test/ui/unsized7.stderr b/src/test/ui/unsized7.stderr
deleted file mode 100644 (file)
index 7dbddd4..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-error[E0277]: the size for values of type `X` cannot be known at compilation time
-  --> $DIR/unsized7.rs:12:21
-   |
-LL | trait T1<Z: T> {
-   |          - required by this bound in `T1`
-...
-LL | impl<X: ?Sized + T> T1<X> for S3<X> {
-   |      -              ^^^^^ doesn't have a size known at compile-time
-   |      |
-   |      this type parameter needs to be `std::marker::Sized`
-   |
-help: consider relaxing the implicit `Sized` restriction
-   |
-LL | trait T1<Z: T + ?Sized> {
-   |               ^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0277`.
index 582b67bc2992489c04026689a57d99ad7bd09b29..206744a58fde41554ccc68bb59714102bb4e940d 100644 (file)
@@ -2,8 +2,6 @@
 //
 #![allow(non_snake_case)]
 
-#![feature(non_ascii_idents)]
-
 pub fn main() {
     let ε = 0.00001f64;
     let Π = 3.14f64;
index 6c54086cc2009a4059218f16825fd85dde53da3f..1f6326dd94b5ce31450ff3700ff81d3662495a75 100644 (file)
@@ -1,15 +1,16 @@
-#![allow(mixed_script_confusables)]
+// check-pass
+//
+#![allow(mixed_script_confusables, non_camel_case_types)]
 
 fn foo<
-    'β, //~ ERROR non-ascii idents are not fully supported
-    γ  //~ ERROR non-ascii idents are not fully supported
-       //~^ WARN type parameter `γ` should have an upper camel case name
+    'β,
+    γ
 >() {}
 
 struct X {
-    δ: usize //~ ERROR non-ascii idents are not fully supported
+    δ: usize
 }
 
 pub fn main() {
-    let α = 0.00001f64; //~ ERROR non-ascii idents are not fully supported
+    let α = 0.00001f64;
 }
diff --git a/src/test/ui/utf8_idents.stderr b/src/test/ui/utf8_idents.stderr
deleted file mode 100644 (file)
index 2fc0b1c..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/utf8_idents.rs:4:5
-   |
-LL |     'β,
-   |     ^^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/utf8_idents.rs:5:5
-   |
-LL |     γ
-   |     ^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/utf8_idents.rs:10:5
-   |
-LL |     δ: usize
-   |     ^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-error[E0658]: non-ascii idents are not fully supported
-  --> $DIR/utf8_idents.rs:14:9
-   |
-LL |     let α = 0.00001f64;
-   |         ^
-   |
-   = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information
-   = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable
-
-warning: type parameter `γ` should have an upper camel case name
-  --> $DIR/utf8_idents.rs:5:5
-   |
-LL |     γ
-   |     ^ help: convert the identifier to upper camel case: `Γ`
-   |
-   = note: `#[warn(non_camel_case_types)]` on by default
-
-error: aborting due to 4 previous errors; 1 warning emitted
-
-For more information about this error, try `rustc --explain E0658`.
index 65d57e6f384c2317f76626eac116f683e2b63665..4369396ce7d270972955d876eaa4954bea56bcd9 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 65d57e6f384c2317f76626eac116f683e2b63665
+Subproject commit 4369396ce7d270972955d876eaa4954bea56bcd9
index a3f114e0bb34f4e64c8eb233afa49b344d106f33..780ee9d63dfd298afbe988e78d88d0a65b26d6d2 100644 (file)
@@ -3,7 +3,9 @@ Thank you for making Clippy better!
 We're collecting our changelog from pull request descriptions.
 If your PR only includes internal changes, you can just write
 `changelog: none`. Otherwise, please write a short comment
-explaining your change.
+explaining your change. Also, it's helpful for us that
+the lint name is put into brackets `[]` and backticks `` ` ` ``,
+e.g. ``[`lint_name`]``.
 
 If your PR fixes an issue, you can add "fixes #issue_number" into this
 PR description. This way the issue will be automatically closed when
@@ -29,4 +31,5 @@ Delete this line and everything above before opening your PR.
 ---
 
 *Please write a short comment explaining your change (or "none" for internal only changes)*
+
 changelog:
index 47253eecc4c4c03ccbcb2fbd10bf32ea15d22dc2..ae6f1aa1b30be0c3ae0c57f1e8d163eea13ef41b 100644 (file)
@@ -34,15 +34,16 @@ jobs:
       run: |
         MESSAGE=$(git log --format=%B -n 1)
         PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
-        output=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
-          python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \
-          grep "^changelog: " | \
-          sed "s/changelog: //g")
-        if [[ -z "$output" ]]; then
+        body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
+          python -c "import sys, json; print(json.load(sys.stdin)['body'])")
+        output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
           echo "ERROR: PR body must contain 'changelog: ...'"
           exit 1
-        elif [[ "$output" = "none" ]]; then
+        }
+        if [[ "$output" = "none" ]]; then
           echo "WARNING: changelog is 'none'"
+        else
+          echo "changelog: $output"
         fi
       env:
         PYTHONIOENCODING: 'utf-8'
index 73997192ae0d5537db33b9c7a1f52990cf0d3c21..204d56e2a9854bd137520e1a0280b64624d5066a 100644 (file)
@@ -179,7 +179,7 @@ Current stable, released 2021-03-25
 
 * Replace [`find_map`] with [`manual_find_map`]
   [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
-* [`unknown_clippy_lints`] Now integrated in the `unknown_lints` rustc lint
+* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
   [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 
 ### Enhancements
@@ -280,7 +280,7 @@ Released 2021-02-11
 
 * Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
   as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
-* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt`
+* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic`
   [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 * Move [`map_err_ignore`] to `restriction`
   [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
@@ -419,7 +419,7 @@ Released 2020-12-31
   [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 * Rename `zero_width_space` to [`invisible_characters`]
   [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
-* Deprecate [`drop_bounds`] (uplifted)
+* Deprecate `drop_bounds` (uplifted)
   [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 * Move [`string_lit_as_bytes`] to `nursery`
   [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
@@ -1018,7 +1018,7 @@ Released 2020-03-12
   [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 * Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 * Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
-* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
+* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 
 ### Enhancements
 
@@ -1046,7 +1046,7 @@ Released 2020-03-12
 * [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 * [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 * [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
-* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
+* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 * [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 * [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 * [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
@@ -1080,7 +1080,7 @@ Released 2020-01-30
   [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
   details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 * Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
-* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
+* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 * Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
   [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 * Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
@@ -1282,7 +1282,7 @@ Released 2019-05-20
 
 [1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 
-* New lint: [`drop_bounds`] to detect `T: Drop` bounds
+* New lint: `drop_bounds` to detect `T: Drop` bounds
 * Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 * Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 * Move [`get_unwrap`] to the restriction category
@@ -1375,7 +1375,7 @@ Released 2019-01-17
 
 * New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
   [`redundant_clone`], [`wildcard_dependencies`],
-  [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`],
+  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
   [`mem_discriminant_non_enum`], [`cargo_common_metadata`]
 * Add support for `u128` and `i128` to integer related lints
 * Add float support to `mistyped_literal_suffixes`
@@ -1649,7 +1649,7 @@ Released 2018-09-13
 
 ## 0.0.166
 * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
-* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`],
+* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
   [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
   [`transmute_int_to_float`]
 
@@ -2037,7 +2037,7 @@ Released 2018-09-13
 
 ## 0.0.64 — 2016-04-26
 * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
-* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`]
+* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 
 ## 0.0.63 — 2016-04-08
 * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
@@ -2091,7 +2091,7 @@ Released 2018-09-13
 
 ## 0.0.49 — 2016-03-09
 * Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
-* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`]
+* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 
 ## 0.0.48 — 2016-03-07
 * Fixed: ICE in [`needless_range_loop`] with globals
@@ -2124,6 +2124,7 @@ Released 2018-09-13
 [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
 [`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
+[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
@@ -2148,6 +2149,7 @@ Released 2018-09-13
 [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
+[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
@@ -2178,7 +2180,6 @@ Released 2018-09-13
 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
-[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
 [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
@@ -2216,6 +2217,7 @@ Released 2018-09-13
 [`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 [`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 [`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
+[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 [`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
@@ -2264,10 +2266,9 @@ Released 2018-09-13
 [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
-[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
 [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
-[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
+[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
@@ -2402,7 +2403,6 @@ Released 2018-09-13
 [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
-[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
 [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
@@ -2488,7 +2488,6 @@ Released 2018-09-13
 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
-[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
 [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 [`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
@@ -2517,13 +2516,13 @@ Released 2018-09-13
 [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
-[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
 [`unnecessary_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_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
 [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
@@ -2541,7 +2540,6 @@ Released 2018-09-13
 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
-[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
 [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
index e0a4d4455e9c6c2182ebb60639f228f35eaec621..02d2b63c9e199c936a7c86fbbd479f85ff924afe 100644 (file)
@@ -21,10 +21,10 @@ All contributors are expected to follow the [Rust Code of Conduct].
     - [IntelliJ Rust](#intellij-rust)
     - [Rust Analyzer](#rust-analyzer)
   - [How Clippy works](#how-clippy-works)
-  - [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust)
+  - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
     - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
-    - [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
-    - [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust)
+    - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
+    - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
     - [Defining remotes](#defining-remotes)
   - [Issue and PR triage](#issue-and-pr-triage)
   - [Bors and Homu](#bors-and-homu)
index a0993bb6913e7c62a3ac67be103e223d88b2ea76..c565e29d07801d3a449aeabe40276f4c8315cff3 100644 (file)
@@ -127,10 +127,9 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
             _ => &block.expr,
         };
         // function call
-        if let Some(args) = match_panic_call(cx, begin_panic_call);
-        if args.len() == 1;
+        if let Some(arg) = match_panic_call(cx, begin_panic_call);
         // bind the second argument of the `assert!` macro if it exists
-        if let panic_message = snippet_opt(cx, args[0].span);
+        if let panic_message = snippet_opt(cx, arg.span);
         // second argument of begin_panic is irrelevant
         // as is the second match arm
         then {
diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
new file mode 100644 (file)
index 0000000..bee706e
--- /dev/null
@@ -0,0 +1,75 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{ast_utils, is_direct_expn_of};
+use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** This lint warns about boolean comparisons in assert-like macros.
+    ///
+    /// **Why is this bad?** It is shorter to use the equivalent.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// // Bad
+    /// assert_eq!("a".is_empty(), false);
+    /// assert_ne!("a".is_empty(), true);
+    ///
+    /// // Good
+    /// assert!(!"a".is_empty());
+    /// ```
+    pub BOOL_ASSERT_COMPARISON,
+    style,
+    "Using a boolean as comparison value in an assert_* macro when there is no need"
+}
+
+declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
+
+fn is_bool_lit(e: &Expr) -> bool {
+    matches!(
+        e.kind,
+        ExprKind::Lit(Lit {
+            kind: LitKind::Bool(_),
+            ..
+        })
+    ) && !e.span.from_expansion()
+}
+
+impl EarlyLintPass for BoolAssertComparison {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
+        let macros = ["assert_eq", "debug_assert_eq"];
+        let inverted_macros = ["assert_ne", "debug_assert_ne"];
+
+        for mac in macros.iter().chain(inverted_macros.iter()) {
+            if let Some(span) = is_direct_expn_of(e.span, mac) {
+                if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) {
+                    let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
+
+                    if nb_bool_args != 1 {
+                        // If there are two boolean arguments, we definitely don't understand
+                        // what's going on, so better leave things as is...
+                        //
+                        // Or there is simply no boolean and then we can leave things as is!
+                        return;
+                    }
+
+                    let non_eq_mac = &mac[..mac.len() - 3];
+                    span_lint_and_sugg(
+                        cx,
+                        BOOL_ASSERT_COMPARISON,
+                        span,
+                        &format!("used `{}!` with a literal bool", mac),
+                        "replace it with",
+                        format!("{}!(..)", non_eq_mac),
+                        Applicability::MaybeIncorrect,
+                    );
+                    return;
+                }
+            }
+        }
+    }
+}
index 58d9aa9c005c2226966d0f148413a83f9b5ade15..67f0e0c78700bc56f9bcd19373b6a27f205ad8bc 100644 (file)
@@ -261,7 +261,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
             }
             METHODS_WITH_NEGATION
                 .iter()
-                .cloned()
+                .copied()
                 .flat_map(|(a, b)| vec![(a, b), (b, a)])
                 .find(|&(a, _)| {
                     let path: &str = &path.ident.name.as_str();
index 9113e5a0920a250b57c849c6e13b9fac06c45114..3132d3a5cf0976d7dece539347c09c9f61832871 100644 (file)
@@ -1,8 +1,8 @@
 use std::borrow::Cow;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::meets_msrv;
 use clippy_utils::sugg::Sugg;
+use clippy_utils::{meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
 
 use super::PTR_AS_PTR;
 
-const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0);
-
 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) {
-    if !meets_msrv(msrv.as_ref(), &PTR_AS_PTR_MSRV) {
+    if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
         return;
     }
 
index d7136f84cc3af3d4d9471cb367c086e4cf994872..8d3123e1ec8eefa00ee7e5a4b4ba65fd82314066 100644 (file)
@@ -2,7 +2,7 @@
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{meets_msrv, SpanlessEq};
+use clippy_utils::{meets_msrv, msrvs, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -12,8 +12,6 @@
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for explicit bounds checking when casting.
     ///
@@ -58,7 +56,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
     fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) {
             return;
         }
 
@@ -323,7 +321,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function:
         if let [int] = &*tp.segments;
         then {
             let name = &int.ident.name.as_str();
-            candidates.iter().find(|c| name == *c).cloned()
+            candidates.iter().find(|c| name == *c).copied()
         } else {
             None
         }
@@ -337,7 +335,7 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
         if let [ty] = &*path.segments;
         then {
             let name = &ty.ident.name.as_str();
-            INTS.iter().find(|c| name == *c).cloned()
+            INTS.iter().find(|c| name == *c).copied()
         } else {
             None
         }
index 04fff237bb4cbe4fa576aa2c0332284bdabeada2..ab22578abd674efec12970050170789ec492d5d9 100644 (file)
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::visitors::LocalUsedVisitor;
-use clippy_utils::{path_to_local, SpanlessEq};
+use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
 use if_chain::if_chain;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
+use rustc_hir::LangItem::OptionNone;
+use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults};
+use rustc_middle::ty::TypeckResults;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{MultiSpan, Span};
 
@@ -52,7 +52,7 @@
 impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
         if let ExprKind::Match(_expr, arms, _source) = expr.kind {
-            if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) {
+            if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
                 for arm in arms {
                     check_arm(arm, wild_arm, cx);
                 }
@@ -75,7 +75,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
         // match <local> { .. }
         if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
         // one of the branches must be "wild-like"
-        if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
+        if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
         let (wild_inner_arm, non_wild_inner_arm) =
             (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
         if !pat_contains_or(non_wild_inner_arm.pat);
@@ -126,13 +126,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir>
 /// A "wild-like" pattern is wild ("_") or `None`.
 /// For this lint to apply, both the outer and inner match expressions
 /// must have "wild-like" branches that can be combined.
-fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool {
+fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
     if arm.guard.is_some() {
         return false;
     }
     match arm.pat.kind {
         PatKind::Binding(..) | PatKind::Wild => true,
-        PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true,
+        PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
         _ => false,
     }
 }
@@ -164,17 +164,6 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
     result
 }
 
-fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
-    if let Some(none_id) = tcx.lang_items().option_none_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res {
-            if let Some(variant_id) = tcx.parent(id) {
-                return variant_id == none_id;
-            }
-        }
-    }
-    false
-}
-
 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
index 31ae63b51849cb6e9a2b7ba32c48e45d3f530dcb..42e153909ce75b29f8264d6c4982ec732e2a435b 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq};
+use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -60,7 +60,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         }
 
         // We only care about the top-most `if` in the chain
-        if parent_node_is_if_expr(expr, cx) {
+        if is_else_clause(cx.tcx, expr) {
             return;
         }
 
index 8b503c9a0306b383c415b3335562036d8423d03e..f956d171bfbe0519e9d222eebd4c581aa39c6c46 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 use clippy_utils::{
-    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr,
+    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
     run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash,
 };
 use if_chain::if_chain;
@@ -188,13 +188,18 @@ fn lint_same_then_else<'tcx>(
     expr: &'tcx Expr<'_>,
 ) {
     // We only lint ifs with multiple blocks
-    if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) {
+    if blocks.len() < 2 || is_else_clause(cx.tcx, expr) {
         return;
     }
 
     // Check if each block has shared code
     let has_expr = blocks[0].expr.is_some();
-    let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks);
+
+    let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) {
+        (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
+    } else {
+        return;
+    };
 
     // BRANCHES_SHARING_CODE prerequisites
     if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
@@ -290,7 +295,19 @@ fn lint_same_then_else<'tcx>(
     }
 }
 
-fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) {
+struct BlockEqual {
+    /// The amount statements that are equal from the start
+    start_eq: usize,
+    /// The amount statements that are equal from the end
+    end_eq: usize,
+    ///  An indication if the block expressions are the same. This will also be true if both are
+    /// `None`
+    expr_eq: bool,
+}
+
+/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
+/// abort any further processing and avoid duplicate lint triggers.
+fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
     let mut start_eq = usize::MAX;
     let mut end_eq = usize::MAX;
     let mut expr_eq = true;
@@ -332,7 +349,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize,
                     "same as this",
                 );
 
-                return (0, 0, false);
+                return None;
             }
         }
 
@@ -352,7 +369,11 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize,
         end_eq = min_block_size - start_eq;
     }
 
-    (start_eq, end_eq, expr_eq)
+    Some(BlockEqual {
+        start_eq,
+        end_eq,
+        expr_eq,
+    })
 }
 
 fn check_for_warn_of_moved_symbol(
index 89088c533ed50b305a6f4e0b0cbe4114921fe81d..4688b3d51050d63c77da6ae1d6663dd64ba20991 100644 (file)
@@ -93,15 +93,6 @@ macro_rules! declare_deprecated_lint {
     "the replacement suggested by this lint had substantially different behavior"
 }
 
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been superseded by the warn-by-default
-    /// `invalid_value` rustc lint.
-    pub INVALID_REF,
-    "superseded by rustc lint `invalid_value`"
-}
-
 declare_deprecated_lint! {
     /// **What it does:** Nothing. This lint has been deprecated.
     ///
@@ -110,24 +101,6 @@ macro_rules! declare_deprecated_lint {
     "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
 }
 
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `array_into_iter`.
-    pub INTO_ITER_ON_ARRAY,
-    "this lint has been uplifted to rustc and is now called `array_into_iter`"
-}
-
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `unused_labels`.
-    pub UNUSED_LABEL,
-    "this lint has been uplifted to rustc and is now called `unused_labels`"
-}
-
 declare_deprecated_lint! {
     /// **What it does:** Nothing. This lint has been deprecated.
     ///
@@ -147,44 +120,17 @@ macro_rules! declare_deprecated_lint {
 declare_deprecated_lint! {
     /// **What it does:** Nothing. This lint has been deprecated.
     ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `drop_bounds`.
-    pub DROP_BOUNDS,
-    "this lint has been uplifted to rustc and is now called `drop_bounds`"
-}
-
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `temporary_cstring_as_ptr`.
-    pub TEMPORARY_CSTRING_AS_PTR,
-    "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`"
-}
-
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `panic_fmt`.
-    pub PANIC_PARAMS,
-    "this lint has been uplifted to rustc and is now called `panic_fmt`"
-}
-
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been integrated into the `unknown_lints`
-    /// rustc lint.
-    pub UNKNOWN_CLIPPY_LINTS,
-    "this lint has been integrated into the `unknown_lints` rustc lint"
+    /// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
+    /// more specific lint.
+    pub FIND_MAP,
+    "this lint has been replaced by `manual_find_map`, a more specific lint"
 }
 
 declare_deprecated_lint! {
     /// **What it does:** Nothing. This lint has been deprecated.
     ///
-    /// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
+    /// **Deprecation reason:** This lint has been replaced by `manual_filter_map`, a
     /// more specific lint.
-    pub FIND_MAP,
-    "this lint has been replaced by `manual_find_map`, a more specific lint"
+    pub FILTER_MAP,
+    "this lint has been replaced by `manual_filter_map`, a more specific lint"
 }
index a815df1691a1c9f8d9334d1695d268c2978a3c9b..8db5050a5ac30510ccc424c15781860db49ba2da 100644 (file)
@@ -1,17 +1,19 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
-use clippy_utils::SpanlessEq;
-use clippy_utils::{get_item_name, paths};
-use if_chain::if_chain;
+use clippy_utils::{
+    can_move_expr_to_closure_no_visit,
+    diagnostics::span_lint_and_sugg,
+    is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
+    source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
+    SpanlessEq,
+};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp};
+use rustc_hir::{
+    intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+    Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp,
+};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{Span, SyntaxContext, DUMMY_SP};
+use std::fmt::Write;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap`
     ///
     /// **Why is this bad?** Using `entry` is more efficient.
     ///
-    /// **Known problems:** Some false negatives, eg.:
+    /// **Known problems:** The suggestion may have type inference errors in some cases. e.g.
     /// ```rust
-    /// # use std::collections::HashMap;
-    /// # let mut map = HashMap::new();
-    /// # let v = 1;
-    /// # let k = 1;
-    /// if !map.contains_key(&k) {
-    ///     map.insert(k.clone(), v);
-    /// }
+    /// let mut map = std::collections::HashMap::new();
+    /// let _ = if !map.contains_key(&0) {
+    ///     map.insert(0, 0)
+    /// } else {
+    ///     None
+    /// };
     /// ```
     ///
     /// **Example:**
 declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
 
 impl<'tcx> LateLintPass<'tcx> for HashMapPass {
+    #[allow(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::If(check, then_block, ref else_block) = expr.kind {
-            if let ExprKind::Unary(UnOp::Not, check) = check.kind {
-                if let Some((ty, map, key)) = check_cond(cx, check) {
-                    // in case of `if !m.contains_key(&k) { m.insert(k, v); }`
-                    // we can give a better error message
-                    let sole_expr = {
-                        else_block.is_none()
-                            && if let ExprKind::Block(then_block, _) = then_block.kind {
-                                (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
-                            } else {
-                                true
-                            }
-                        // XXXManishearth we can also check for if/else blocks containing `None`.
-                    };
-
-                    let mut visitor = InsertVisitor {
-                        cx,
-                        span: expr.span,
-                        ty,
-                        map,
-                        key,
-                        sole_expr,
-                    };
-
-                    walk_expr(&mut visitor, then_block);
+        let (cond_expr, then_expr, else_expr) = match expr.kind {
+            ExprKind::If(c, t, e) => (c, t, e),
+            _ => return,
+        };
+        let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
+            Some(x) => x,
+            None => return,
+        };
+
+        let then_search = match find_insert_calls(cx, &contains_expr, then_expr) {
+            Some(x) => x,
+            None => return,
+        };
+
+        let mut app = Applicability::MachineApplicable;
+        let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0;
+        let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0;
+        let sugg = if let Some(else_expr) = else_expr {
+            let else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
+                Some(search) => search,
+                None => return,
+            };
+
+            if then_search.edits.is_empty() && else_search.edits.is_empty() {
+                // No insertions
+                return;
+            } else if then_search.edits.is_empty() || else_search.edits.is_empty() {
+                // if .. { insert } else { .. } or if .. { .. } else { insert }
+                let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
+                    (true, true) => (
+                        then_search.snippet_vacant(cx, then_expr.span, &mut app),
+                        snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+                    ),
+                    (true, false) => (
+                        then_search.snippet_occupied(cx, then_expr.span, &mut app),
+                        snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+                    ),
+                    (false, true) => (
+                        else_search.snippet_occupied(cx, else_expr.span, &mut app),
+                        snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+                    ),
+                    (false, false) => (
+                        else_search.snippet_vacant(cx, else_expr.span, &mut app),
+                        snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+                    ),
+                };
+                format!(
+                    "if let {}::{} = {}.entry({}) {} else {}",
+                    map_ty.entry_path(),
+                    entry_kind,
+                    map_str,
+                    key_str,
+                    then_str,
+                    else_str,
+                )
+            } else {
+                // if .. { insert } else { insert }
+                let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
+                    (
+                        then_search.snippet_vacant(cx, then_expr.span, &mut app),
+                        else_search.snippet_occupied(cx, else_expr.span, &mut app),
+                    )
+                } else {
+                    (
+                        then_search.snippet_occupied(cx, then_expr.span, &mut app),
+                        else_search.snippet_vacant(cx, else_expr.span, &mut app),
+                    )
+                };
+                let indent_str = snippet_indent(cx, expr.span);
+                let indent_str = indent_str.as_deref().unwrap_or("");
+                format!(
+                    "match {}.entry({}) {{\n{indent}    {entry}::{} => {}\n\
+                        {indent}    {entry}::{} => {}\n{indent}}}",
+                    map_str,
+                    key_str,
+                    then_entry,
+                    reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
+                    else_entry,
+                    reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
+                    entry = map_ty.entry_path(),
+                    indent = indent_str,
+                )
+            }
+        } else {
+            if then_search.edits.is_empty() {
+                // no insertions
+                return;
+            }
+
+            // if .. { insert }
+            if !then_search.allow_insert_closure {
+                let (body_str, entry_kind) = if contains_expr.negated {
+                    then_search.snippet_vacant(cx, then_expr.span, &mut app)
+                } else {
+                    then_search.snippet_occupied(cx, then_expr.span, &mut app)
+                };
+                format!(
+                    "if let {}::{} = {}.entry({}) {}",
+                    map_ty.entry_path(),
+                    entry_kind,
+                    map_str,
+                    key_str,
+                    body_str,
+                )
+            } else if let Some(insertion) = then_search.as_single_insertion() {
+                let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
+                if contains_expr.negated {
+                    if insertion.value.can_have_side_effects() {
+                        format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str)
+                    } else {
+                        format!("{}.entry({}).or_insert({});", map_str, key_str, value_str)
+                    }
+                } else {
+                    // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
+                    // This would need to be a different lint.
+                    return;
                 }
-            } else if let Some(else_block) = *else_block {
-                if let Some((ty, map, key)) = check_cond(cx, check) {
-                    let mut visitor = InsertVisitor {
-                        cx,
-                        span: expr.span,
-                        ty,
-                        map,
-                        key,
-                        sole_expr: false,
-                    };
-
-                    walk_expr(&mut visitor, else_block);
+            } else {
+                let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
+                if contains_expr.negated {
+                    format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str)
+                } else {
+                    // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
+                    // This would need to be a different lint.
+                    return;
                 }
             }
-        }
+        };
+
+        span_lint_and_sugg(
+            cx,
+            MAP_ENTRY,
+            expr.span,
+            &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()),
+            "try this",
+            sugg,
+            app,
+        );
     }
 }
 
-fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> {
-    if_chain! {
-        if let ExprKind::MethodCall(path, _, params, _) = check.kind;
-        if params.len() >= 2;
-        if path.ident.name == sym!(contains_key);
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind;
-        then {
-            let map = &params[0];
-            let obj_ty = cx.typeck_results().expr_ty(map).peel_refs();
+#[derive(Clone, Copy)]
+enum MapType {
+    Hash,
+    BTree,
+}
+impl MapType {
+    fn name(self) -> &'static str {
+        match self {
+            Self::Hash => "HashMap",
+            Self::BTree => "BTreeMap",
+        }
+    }
+    fn entry_path(self) -> &'static str {
+        match self {
+            Self::Hash => "std::collections::hash_map::Entry",
+            Self::BTree => "std::collections::btree_map::Entry",
+        }
+    }
+}
 
-            return if match_type(cx, obj_ty, &paths::BTREEMAP) {
-                Some(("BTreeMap", map, key))
-            }
-            else if is_type_diagnostic_item(cx, obj_ty, sym::hashmap_type) {
-                Some(("HashMap", map, key))
-            }
-            else {
-                None
+struct ContainsExpr<'tcx> {
+    negated: bool,
+    map: &'tcx Expr<'tcx>,
+    key: &'tcx Expr<'tcx>,
+    call_ctxt: SyntaxContext,
+}
+fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
+    let mut negated = false;
+    let expr = peel_hir_expr_while(expr, |e| match e.kind {
+        ExprKind::Unary(UnOp::Not, e) => {
+            negated = !negated;
+            Some(e)
+        },
+        _ => None,
+    });
+    match expr.kind {
+        ExprKind::MethodCall(
+            _,
+            _,
+            [map, Expr {
+                kind: ExprKind::AddrOf(_, _, key),
+                span: key_span,
+                ..
+            }],
+            _,
+        ) if key_span.ctxt() == expr.span.ctxt() => {
+            let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+            let expr = ContainsExpr {
+                negated,
+                map,
+                key,
+                call_ctxt: expr.span.ctxt(),
             };
+            if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) {
+                Some((MapType::BTree, expr))
+            } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) {
+                Some((MapType::Hash, expr))
+            } else {
+                None
+            }
+        },
+        _ => None,
+    }
+}
+
+struct InsertExpr<'tcx> {
+    map: &'tcx Expr<'tcx>,
+    key: &'tcx Expr<'tcx>,
+    value: &'tcx Expr<'tcx>,
+}
+fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
+    if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
+        let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+        if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
+            Some(InsertExpr { map, key, value })
+        } else {
+            None
         }
+    } else {
+        None
     }
+}
 
-    None
+/// An edit that will need to be made to move the expression to use the entry api
+#[derive(Clone, Copy)]
+enum Edit<'tcx> {
+    /// A semicolon that needs to be removed. Used to create a closure for `insert_with`.
+    RemoveSemi(Span),
+    /// An insertion into the map.
+    Insertion(Insertion<'tcx>),
+}
+impl Edit<'tcx> {
+    fn as_insertion(self) -> Option<Insertion<'tcx>> {
+        if let Self::Insertion(i) = self { Some(i) } else { None }
+    }
+}
+#[derive(Clone, Copy)]
+struct Insertion<'tcx> {
+    call: &'tcx Expr<'tcx>,
+    value: &'tcx Expr<'tcx>,
 }
 
-struct InsertVisitor<'a, 'tcx, 'b> {
-    cx: &'a LateContext<'tcx>,
-    span: Span,
-    ty: &'static str,
-    map: &'b Expr<'b>,
-    key: &'b Expr<'b>,
-    sole_expr: bool,
+/// This visitor needs to do a multiple things:
+/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
+/// * Determine if there's an insertion using the same key. There's no need for the entry api
+///   otherwise.
+/// * Determine if the final statement executed is an insertion. This is needed to use
+///   `or_insert_with`.
+/// * Determine if there's any sub-expression that can't be placed in a closure.
+/// * Determine if there's only a single insert statement. `or_insert` can be used in this case.
+#[allow(clippy::struct_excessive_bools)]
+struct InsertSearcher<'cx, 'tcx> {
+    cx: &'cx LateContext<'tcx>,
+    /// The map expression used in the contains call.
+    map: &'tcx Expr<'tcx>,
+    /// The key expression used in the contains call.
+    key: &'tcx Expr<'tcx>,
+    /// The context of the top level block. All insert calls must be in the same context.
+    ctxt: SyntaxContext,
+    /// Whether this expression can be safely moved into a closure.
+    allow_insert_closure: bool,
+    /// Whether this expression can use the entry api.
+    can_use_entry: bool,
+    /// Whether this expression is the final expression in this code path. This may be a statement.
+    in_tail_pos: bool,
+    // Is this expression a single insert. A slightly better suggestion can be made in this case.
+    is_single_insert: bool,
+    /// If the visitor has seen the map being used.
+    is_map_used: bool,
+    /// The locations where changes need to be made for the suggestion.
+    edits: Vec<Edit<'tcx>>,
+    /// A stack of loops the visitor is currently in.
+    loops: Vec<HirId>,
 }
+impl<'tcx> InsertSearcher<'_, 'tcx> {
+    /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
+    /// only if they are on separate code paths. This will return whether the map was used in the
+    /// given expression.
+    fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool {
+        let is_map_used = self.is_map_used;
+        let in_tail_pos = self.in_tail_pos;
+        self.visit_expr(e);
+        let res = self.is_map_used;
+        self.is_map_used = is_map_used;
+        self.in_tail_pos = in_tail_pos;
+        res
+    }
 
-impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> {
-    type Map = Map<'tcx>;
+    /// Visits an expression which is not itself in a tail position, but other sibling expressions
+    /// may be. e.g. if conditions
+    fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
+        let in_tail_pos = self.in_tail_pos;
+        self.in_tail_pos = false;
+        self.visit_expr(e);
+        self.in_tail_pos = in_tail_pos;
+    }
+}
+impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
+    type Map = ErasedMap<'tcx>;
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+        match stmt.kind {
+            StmtKind::Semi(e) => {
+                self.visit_expr(e);
+
+                if self.in_tail_pos && self.allow_insert_closure {
+                    // The spans are used to slice the top level expression into multiple parts. This requires that
+                    // they all come from the same part of the source code.
+                    if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt {
+                        self.edits
+                            .push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP)));
+                    } else {
+                        self.allow_insert_closure = false;
+                    }
+                }
+            },
+            StmtKind::Expr(e) => self.visit_expr(e),
+            StmtKind::Local(Local { init: Some(e), .. }) => {
+                self.allow_insert_closure &= !self.in_tail_pos;
+                self.in_tail_pos = false;
+                self.is_single_insert = false;
+                self.visit_expr(e);
+            },
+            _ => {
+                self.allow_insert_closure &= !self.in_tail_pos;
+                self.is_single_insert = false;
+            },
+        }
+    }
+
+    fn visit_block(&mut self, block: &'tcx Block<'_>) {
+        // If the block is in a tail position, then the last expression (possibly a statement) is in the
+        // tail position. The rest, however, are not.
+        match (block.stmts, block.expr) {
+            ([], None) => {
+                self.allow_insert_closure &= !self.in_tail_pos;
+            },
+            ([], Some(expr)) => self.visit_expr(expr),
+            (stmts, Some(expr)) => {
+                let in_tail_pos = self.in_tail_pos;
+                self.in_tail_pos = false;
+                for stmt in stmts {
+                    self.visit_stmt(stmt);
+                }
+                self.in_tail_pos = in_tail_pos;
+                self.visit_expr(expr);
+            },
+            ([stmts @ .., stmt], None) => {
+                let in_tail_pos = self.in_tail_pos;
+                self.in_tail_pos = false;
+                for stmt in stmts {
+                    self.visit_stmt(stmt);
+                }
+                self.in_tail_pos = in_tail_pos;
+                self.visit_stmt(stmt);
+            },
+        }
+    }
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::MethodCall(path, _, params, _) = expr.kind;
-            if params.len() == 3;
-            if path.ident.name == sym!(insert);
-            if get_item_name(self.cx, self.map) == get_item_name(self.cx, &params[0]);
-            if SpanlessEq::new(self.cx).eq_expr(self.key, &params[1]);
-            if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span);
-            then {
-                span_lint_and_then(self.cx, MAP_ENTRY, self.span,
-                                   &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| {
-                    if self.sole_expr {
-                        let mut app = Applicability::MachineApplicable;
-                        let help = format!("{}.entry({}).or_insert({});",
-                                           snippet_with_applicability(self.cx, self.map.span, "map", &mut app),
-                                           snippet_with_applicability(self.cx, params[1].span, "..", &mut app),
-                                           snippet_with_applicability(self.cx, params[2].span, "..", &mut app));
-
-                        diag.span_suggestion(
-                            self.span,
-                            "consider using",
-                            help,
-                            Applicability::MachineApplicable, // snippet
-                        );
+        if !self.can_use_entry {
+            return;
+        }
+
+        match try_parse_insert(self.cx, expr) {
+            Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
+                // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
+                if self.is_map_used
+                    || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)
+                    || expr.span.ctxt() != self.ctxt
+                {
+                    self.can_use_entry = false;
+                    return;
+                }
+
+                self.edits.push(Edit::Insertion(Insertion {
+                    call: expr,
+                    value: insert_expr.value,
+                }));
+                self.is_map_used = true;
+                self.allow_insert_closure &= self.in_tail_pos;
+
+                // The value doesn't affect whether there is only a single insert expression.
+                let is_single_insert = self.is_single_insert;
+                self.visit_non_tail_expr(insert_expr.value);
+                self.is_single_insert = is_single_insert;
+            },
+            _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => {
+                self.is_map_used = true;
+            },
+            _ => match expr.kind {
+                ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
+                    self.is_single_insert = false;
+                    self.visit_non_tail_expr(cond_expr);
+                    // Each branch may contain it's own insert expression.
+                    let mut is_map_used = self.visit_cond_arm(then_expr);
+                    is_map_used |= self.visit_cond_arm(else_expr);
+                    self.is_map_used = is_map_used;
+                },
+                ExprKind::Match(scrutinee_expr, arms, _) => {
+                    self.is_single_insert = false;
+                    self.visit_non_tail_expr(scrutinee_expr);
+                    // Each branch may contain it's own insert expression.
+                    let mut is_map_used = self.is_map_used;
+                    for arm in arms {
+                        if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
+                            self.visit_non_tail_expr(guard)
+                        }
+                        is_map_used |= self.visit_cond_arm(arm.body);
                     }
-                    else {
-                        let help = format!("consider using `{}.entry({})`",
-                                           snippet(self.cx, self.map.span, "map"),
-                                           snippet(self.cx, params[1].span, ".."));
-
-                        diag.span_label(
-                            self.span,
-                            &help,
-                        );
+                    self.is_map_used = is_map_used;
+                },
+                ExprKind::Loop(block, ..) => {
+                    self.loops.push(expr.hir_id);
+                    self.is_single_insert = false;
+                    self.allow_insert_closure &= !self.in_tail_pos;
+                    // Don't allow insertions inside of a loop.
+                    let edit_len = self.edits.len();
+                    self.visit_block(block);
+                    if self.edits.len() != edit_len {
+                        self.can_use_entry = false;
                     }
-                });
-            }
+                    self.loops.pop();
+                },
+                ExprKind::Block(block, _) => self.visit_block(block),
+                ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => {
+                    self.can_use_entry = false;
+                },
+                _ => {
+                    self.allow_insert_closure &= !self.in_tail_pos;
+                    self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops);
+                    // Sub expressions are no longer in the tail position.
+                    self.is_single_insert = false;
+                    self.in_tail_pos = false;
+                    walk_expr(self, expr);
+                },
+            },
         }
+    }
+}
+
+struct InsertSearchResults<'tcx> {
+    edits: Vec<Edit<'tcx>>,
+    allow_insert_closure: bool,
+    is_single_insert: bool,
+}
+impl InsertSearchResults<'tcx> {
+    fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
+        self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
+    }
 
-        if !self.sole_expr {
-            walk_expr(self, expr);
+    fn snippet(
+        &self,
+        cx: &LateContext<'_>,
+        mut span: Span,
+        app: &mut Applicability,
+        write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability),
+    ) -> String {
+        let ctxt = span.ctxt();
+        let mut res = String::new();
+        for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) {
+            res.push_str(&snippet_with_applicability(
+                cx,
+                span.until(insertion.call.span),
+                "..",
+                app,
+            ));
+            if is_expr_used_or_unified(cx.tcx, insertion.call) {
+                write_wrapped(&mut res, insertion, ctxt, app);
+            } else {
+                let _ = write!(
+                    res,
+                    "e.insert({})",
+                    snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
+                );
+            }
+            span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
         }
+        res.push_str(&snippet_with_applicability(cx, span, "..", app));
+        res
     }
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
+
+    fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
+        (
+            self.snippet(cx, span, app, |res, insertion, ctxt, app| {
+                // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
+                let _ = write!(
+                    res,
+                    "Some(e.insert({}))",
+                    snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
+                );
+            }),
+            "Occupied(mut e)",
+        )
     }
+
+    fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
+        (
+            self.snippet(cx, span, app, |res, insertion, ctxt, app| {
+                // Insertion into a map would return `None`, but the entry returns a mutable reference.
+                let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) {
+                    write!(
+                        res,
+                        "e.insert({});\n{}None",
+                        snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
+                        snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""),
+                    )
+                } else {
+                    write!(
+                        res,
+                        "{{ e.insert({}); None }}",
+                        snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
+                    )
+                };
+            }),
+            "Vacant(e)",
+        )
+    }
+
+    fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String {
+        let ctxt = span.ctxt();
+        let mut res = String::new();
+        for edit in &self.edits {
+            match *edit {
+                Edit::Insertion(insertion) => {
+                    // Cut out the value from `map.insert(key, value)`
+                    res.push_str(&snippet_with_applicability(
+                        cx,
+                        span.until(insertion.call.span),
+                        "..",
+                        app,
+                    ));
+                    res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0);
+                    span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
+                },
+                Edit::RemoveSemi(semi_span) => {
+                    // Cut out the semicolon. This allows the value to be returned from the closure.
+                    res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app));
+                    span = span.trim_start(semi_span).unwrap_or(DUMMY_SP);
+                },
+            }
+        }
+        res.push_str(&snippet_with_applicability(cx, span, "..", app));
+        res
+    }
+}
+
+fn find_insert_calls(
+    cx: &LateContext<'tcx>,
+    contains_expr: &ContainsExpr<'tcx>,
+    expr: &'tcx Expr<'_>,
+) -> Option<InsertSearchResults<'tcx>> {
+    let mut s = InsertSearcher {
+        cx,
+        map: contains_expr.map,
+        key: contains_expr.key,
+        ctxt: expr.span.ctxt(),
+        edits: Vec::new(),
+        is_map_used: false,
+        allow_insert_closure: true,
+        can_use_entry: true,
+        in_tail_pos: true,
+        is_single_insert: true,
+        loops: Vec::new(),
+    };
+    s.visit_expr(expr);
+    let allow_insert_closure = s.allow_insert_closure;
+    let is_single_insert = s.is_single_insert;
+    let edits = s.edits;
+    s.can_use_entry.then(|| InsertSearchResults {
+        edits,
+        allow_insert_closure,
+        is_single_insert,
+    })
 }
index 249ee27330bf92537e7686b922fd109ff649ccee..4e2dbf005d51c7cd9f2f5ffb9fde6563d73c9def 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{in_macro, match_path_ast};
+use clippy_utils::in_macro;
 use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -126,7 +126,9 @@ fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
 
 fn is_bool_ty(ty: &Ty) -> bool {
     if let TyKind::Path(None, path) = &ty.kind {
-        return match_path_ast(path, &["bool"]);
+        if let [name] = path.segments.as_slice() {
+            return name.ident.name == sym::bool;
+        }
     }
     false
 }
index 4729abbd8e3f7ff99ad2f2e29dfe653043243262..c2b055ed6488e4e9bdeb0d34e1980a16c8e09fdc 100644 (file)
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::paths;
 use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
 use if_chain::if_chain;
@@ -100,15 +101,15 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &
                     return Some(format!("{:?}.to_string()", s.as_str()));
                 }
             } else {
-                let snip = snippet(cx, format_args.span, "<arg>");
+                let sugg = Sugg::hir(cx, format_args, "<arg>");
                 if let ExprKind::MethodCall(path, _, _, _) = format_args.kind {
                     if path.ident.name == sym!(to_string) {
-                        return Some(format!("{}", snip));
+                        return Some(format!("{}", sugg));
                     }
                 } else if let ExprKind::Binary(..) = format_args.kind {
-                    return Some(format!("{}", snip));
+                    return Some(format!("{}", sugg));
                 }
-                return Some(format!("{}.to_string()", snip));
+                return Some(format!("{}.to_string()", sugg.maybe_par()));
             }
         }
     }
@@ -136,7 +137,7 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Strin
                 if let Some(s_src) = snippet_opt(cx, lit.span) {
                     // Simulate macro expansion, converting {{ and }} to { and }.
                     let s_expand = s_src.replace("{{", "{").replace("}}", "}");
-                    return Some(format!("{}.to_string()", s_expand))
+                    return Some(format!("{}.to_string()", s_expand));
                 }
             } else if s.as_str().is_empty() {
                 return on_argumentv1_new(cx, &tup[0], arms);
index 48612befc68d905a5e10a42ad314b6f047dd3bf8..3bd6a09d3653aeb0a60887d8df220196f910d16b 100644 (file)
@@ -215,9 +215,22 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
         // the snippet should look like " else \n    " with maybe comments anywhere
         // it’s bad when there is a ‘\n’ after the “else”
         if let Some(else_snippet) = snippet_opt(cx, else_span);
-        if let Some(else_pos) = else_snippet.find("else");
-        if else_snippet[else_pos..].contains('\n');
+        if let Some((pre_else, post_else)) = else_snippet.split_once("else");
+        if let Some((_, post_else_post_eol)) = post_else.split_once('\n');
+
         then {
+            // Allow allman style braces `} \n else \n {`
+            if_chain! {
+                if is_block(else_);
+                if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
+                // Exactly one eol before and after the else
+                if !pre_else_post_eol.contains('\n');
+                if !post_else_post_eol.contains('\n');
+                then {
+                    return;
+                }
+            }
+
             let else_desc = if is_if(else_) { "if" } else { "{..}" };
             span_lint_and_note(
                 cx,
index e5ec245e5029bca40396ab43226625f7bc9eaea7..3560672a7481208b238a5cc8d96925f6ae267515 100644 (file)
@@ -1,14 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::paths::INTO;
-use clippy_utils::{match_def_path, meets_msrv};
+use clippy_utils::{match_def_path, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
     ///
@@ -57,7 +55,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl LateLintPass<'_> for FromOverInto {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) {
             return;
         }
 
@@ -73,7 +71,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
                     cx.tcx.sess.source_map().guess_head_span(item.span),
                     "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
                     None,
-                    "consider to implement `From` instead",
+                    &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()),
                 );
             }
         }
index ee16519692f9a8b83e64728b2270ec316569edf8..eadcd0867a8809c841f167dfcf7c397bf1d3d6e4 100644 (file)
@@ -1,15 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr};
+use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs};
 use if_chain::if_chain;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for if-else that could be written to `bool::then`.
     ///
@@ -58,7 +57,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl LateLintPass<'_> for IfThenSomeElseNone {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) {
             return;
         }
 
@@ -67,7 +66,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
         }
 
         // We only care about the top-most `if` in the chain
-        if parent_node_is_if_expr(expr, cx) {
+        if is_else_clause(cx.tcx, expr) {
             return;
         }
 
@@ -77,12 +76,12 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
             if let Some(then_expr) = then_block.expr;
             if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
             if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
-            if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME);
+            if is_lang_ctor(cx, then_call_qpath, OptionSome);
             if let ExprKind::Block(els_block, _) = els.kind;
             if els_block.stmts.is_empty();
             if let Some(els_expr) = els_block.expr;
-            if let ExprKind::Path(ref els_call_qpath) = els_expr.kind;
-            if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE);
+            if let ExprKind::Path(ref qpath) = els_expr.kind;
+            if is_lang_ctor(cx, qpath, OptionNone);
             then {
                 let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
                 let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
index 77a38544edc87151198181708d4d83f1d8261e19..03fe0d16d480f792f8138a3480812604ab183cb4 100644 (file)
@@ -22,7 +22,7 @@
 use clippy_utils::paths;
 use clippy_utils::source::{snippet, snippet_opt};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, match_path};
+use clippy_utils::{differing_macro_contexts, match_def_path};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for public `impl` or `fn` missing generalization
@@ -333,12 +333,13 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
             if let ExprKind::Call(fun, args) = e.kind;
             if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind;
             if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
+            if let Some(ty_did) = ty_path.res.opt_def_id();
             then {
                 if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) {
                     return;
                 }
 
-                if match_path(ty_path, &paths::HASHMAP) {
+                if match_def_path(self.cx, ty_did, &paths::HASHMAP) {
                     if method.ident.name == sym::new {
                         self.suggestions
                             .insert(e.span, "HashMap::default()".to_string());
@@ -351,7 +352,7 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
                             ),
                         );
                     }
-                } else if match_path(ty_path, &paths::HASHSET) {
+                } else if match_def_path(self.cx, ty_did, &paths::HASHSET) {
                     if method.ident.name == sym::new {
                         self.suggestions
                             .insert(e.span, "HashSet::default()".to_string());
index cba3183e86950aaf83e8e9cbfe6a0e759a572b42..4069a685ea0a44e9eff0d141fa0c67feba6b769c 100644 (file)
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{in_macro, match_qpath, SpanlessEq};
+use clippy_utils::{in_macro, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind};
+use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -87,7 +87,13 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 
                 // Get the variable name
                 let var_name = ares_path.segments[0].ident.name.as_str();
-                const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"];
+                const INT_TYPES: [LangItem; 5] = [
+                    LangItem::I8,
+                    LangItem::I16,
+                    LangItem::I32,
+                    LangItem::I64,
+                    LangItem::Isize
+                ];
 
                 match cond_num_val.kind {
                     ExprKind::Lit(ref cond_lit) => {
@@ -99,17 +105,30 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
                             };
                         }
                     },
-                    ExprKind::Path(ref cond_num_path) => {
-                        if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) {
-                            print_lint_and_sugg(cx, &var_name, expr);
-                        };
+                    ExprKind::Path(QPath::TypeRelative(_, name)) => {
+                        if_chain! {
+                            if name.ident.as_str() == "MIN";
+                            if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id);
+                            if let Some(impl_id) = cx.tcx.impl_of_method(const_id);
+                            let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
+                            if int_ids.any(|int_id| int_id == impl_id);
+                            then {
+                                print_lint_and_sugg(cx, &var_name, expr)
+                            }
+                        }
                     },
-                    ExprKind::Call(func, _) => {
-                        if let ExprKind::Path(ref cond_num_path) = func.kind {
-                            if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) {
-                                print_lint_and_sugg(cx, &var_name, expr);
+                    ExprKind::Call(func, []) => {
+                        if_chain! {
+                            if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind;
+                            if name.ident.as_str() == "min_value";
+                            if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id);
+                            if let Some(impl_id) = cx.tcx.impl_of_method(func_id);
+                            let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
+                            if int_ids.any(|int_id| int_id == impl_id);
+                            then {
+                                print_lint_and_sugg(cx, &var_name, expr)
                             }
-                        };
+                        }
                     },
                     _ => (),
                 }
index d7ca24487a884ba0d374c91ea44bb93898a5099f..d138c3a8acfefc0ff50b822ce6bb0d926b2f0116 100644 (file)
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
@@ -66,6 +67,7 @@
 impl LateLintPass<'_> for InconsistentStructConstructor {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
+            if !in_macro(expr.span);
             if let ExprKind::Struct(qpath, fields, base) = expr.kind;
             let ty = cx.typeck_results().expr_ty(expr);
             if let Some(adt_def) = ty.ty_adt_def();
index bbb4ddc613af5ca60cdd773dba6b5a193d042b41..afee20ce43e48088b46e2cf3d9cb4c39a272057e 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::{implements_trait, match_type};
-use clippy_utils::{get_trait_def_id, higher, match_qpath, paths};
+use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths};
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -163,7 +163,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
         ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
         ExprKind::Call(path, _) => {
             if let ExprKind::Path(ref qpath) = path.kind {
-                match_qpath(qpath, &paths::REPEAT).into()
+                is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into()
             } else {
                 Finite
             }
index 11fef30945d7884a0b655318369979a1bbc326bd..757d7669bd806719ea3c63dfac9f846f7759af80 100644 (file)
@@ -179,6 +179,7 @@ macro_rules! extract_msrv_attr {
 mod bit_mask;
 mod blacklisted_name;
 mod blocks_in_if_conditions;
+mod bool_assert_comparison;
 mod booleans;
 mod bytecount;
 mod cargo_common_metadata;
@@ -357,6 +358,7 @@ macro_rules! extract_msrv_attr {
 mod unit_return_expecting_ord;
 mod unit_types;
 mod unnamed_address;
+mod unnecessary_self_imports;
 mod unnecessary_sort_by;
 mod unnecessary_wraps;
 mod unnested_or_patterns;
@@ -391,6 +393,7 @@ macro_rules! extract_msrv_attr {
 ///
 /// Used in `./src/driver.rs`.
 pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
+    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
     store.register_pre_expansion_pass(|| box write::Write::default());
     store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
     store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
@@ -494,22 +497,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         "clippy::unsafe_vector_initialization",
         "the replacement suggested by this lint had substantially different behavior",
     );
-    store.register_removed(
-        "clippy::invalid_ref",
-        "superseded by rustc lint `invalid_value`",
-    );
     store.register_removed(
         "clippy::unused_collect",
         "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
     );
-    store.register_removed(
-        "clippy::into_iter_on_array",
-        "this lint has been uplifted to rustc and is now called `array_into_iter`",
-    );
-    store.register_removed(
-        "clippy::unused_label",
-        "this lint has been uplifted to rustc and is now called `unused_labels`",
-    );
     store.register_removed(
         "clippy::replace_consts",
         "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
@@ -518,26 +509,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         "clippy::regex_macro",
         "the regex! macro has been removed from the regex crate in 2018",
     );
-    store.register_removed(
-        "clippy::drop_bounds",
-        "this lint has been uplifted to rustc and is now called `drop_bounds`",
-    );
-    store.register_removed(
-        "clippy::temporary_cstring_as_ptr",
-        "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`",
-    );
-    store.register_removed(
-        "clippy::panic_params",
-        "this lint has been uplifted to rustc and is now called `panic_fmt`",
-    );
-    store.register_removed(
-        "clippy::unknown_clippy_lints",
-        "this lint has been integrated into the `unknown_lints` rustc lint",
-    );
     store.register_removed(
         "clippy::find_map",
         "this lint has been replaced by `manual_find_map`, a more specific lint",
     );
+    store.register_removed(
+        "clippy::filter_map",
+        "this lint has been replaced by `manual_filter_map`, a more specific lint",
+    );
     // end deprecated lints, do not remove this comment, it’s used in `update_lints`
 
     // begin register lints, do not remove this comment, it’s used in `update_lints`
@@ -592,6 +571,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         bit_mask::VERBOSE_BIT_MASK,
         blacklisted_name::BLACKLISTED_NAME,
         blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
+        bool_assert_comparison::BOOL_ASSERT_COMPARISON,
         booleans::LOGIC_BUG,
         booleans::NONMINIMAL_BOOL,
         bytecount::NAIVE_BYTECOUNT,
@@ -783,17 +763,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         methods::BYTES_NTH,
         methods::CHARS_LAST_CMP,
         methods::CHARS_NEXT_CMP,
+        methods::CLONED_INSTEAD_OF_COPIED,
         methods::CLONE_DOUBLE_REF,
         methods::CLONE_ON_COPY,
         methods::CLONE_ON_REF_PTR,
         methods::EXPECT_FUN_CALL,
         methods::EXPECT_USED,
         methods::FILETYPE_IS_FILE,
-        methods::FILTER_MAP,
         methods::FILTER_MAP_IDENTITY,
         methods::FILTER_MAP_NEXT,
         methods::FILTER_NEXT,
         methods::FLAT_MAP_IDENTITY,
+        methods::FLAT_MAP_OPTION,
         methods::FROM_ITER_INSTEAD_OF_COLLECT,
         methods::GET_UNWRAP,
         methods::IMPLICIT_CLONE,
@@ -904,6 +885,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
         precedence::PRECEDENCE,
         ptr::CMP_NULL,
+        ptr::INVALID_NULL_PTR_USAGE,
         ptr::MUT_FROM_REF,
         ptr::PTR_ARG,
         ptr_eq::PTR_EQ,
@@ -988,6 +970,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         unit_types::UNIT_CMP,
         unnamed_address::FN_ADDRESS_COMPARISONS,
         unnamed_address::VTABLE_ADDRESS_COMPARISONS,
+        unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
         unnecessary_sort_by::UNNECESSARY_SORT_BY,
         unnecessary_wraps::UNNECESSARY_WRAPS,
         unnested_or_patterns::UNNESTED_OR_PATTERNS,
@@ -1073,6 +1056,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
     store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor);
     store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions);
+    store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports);
 
     let msrv = conf.msrv.as_ref().and_then(|s| {
         parse_msrv(s, None, None).or_else(|| {
@@ -1093,7 +1077,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv));
     store.register_late_pass(move || box use_self::UseSelf::new(msrv));
     store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
-    store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv));
+    store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark);
     store.register_late_pass(move || box casts::Casts::new(msrv));
     store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv));
 
@@ -1295,6 +1279,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
     store.register_late_pass(|| box manual_map::ManualMap);
     store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
+    store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(arithmetic::FLOAT_ARITHMETIC),
@@ -1345,6 +1330,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(strings::STRING_TO_STRING),
         LintId::of(strings::STR_TO_STRING),
         LintId::of(types::RC_BUFFER),
+        LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
         LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
         LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
         LintId::of(write::PRINT_STDERR),
@@ -1404,8 +1390,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
         LintId::of(matches::MATCH_WILD_ERR_ARM),
         LintId::of(matches::SINGLE_MATCH_ELSE),
-        LintId::of(methods::FILTER_MAP),
+        LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
         LintId::of(methods::FILTER_MAP_NEXT),
+        LintId::of(methods::FLAT_MAP_OPTION),
         LintId::of(methods::IMPLICIT_CLONE),
         LintId::of(methods::INEFFICIENT_TO_STRING),
         LintId::of(methods::MAP_FLATTEN),
@@ -1428,6 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(strings::STRING_ADD_ASSIGN),
         LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
         LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
+        LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
         LintId::of(types::LINKEDLIST),
         LintId::of(types::OPTION_OPTION),
         LintId::of(unicode::NON_ASCII_LITERAL),
@@ -1474,6 +1462,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
         LintId::of(blacklisted_name::BLACKLISTED_NAME),
         LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
+        LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
         LintId::of(booleans::LOGIC_BUG),
         LintId::of(booleans::NONMINIMAL_BOOL),
         LintId::of(casts::CAST_REF_TO_MUT),
@@ -1673,6 +1662,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
         LintId::of(precedence::PRECEDENCE),
         LintId::of(ptr::CMP_NULL),
+        LintId::of(ptr::INVALID_NULL_PTR_USAGE),
         LintId::of(ptr::MUT_FROM_REF),
         LintId::of(ptr::PTR_ARG),
         LintId::of(ptr_eq::PTR_EQ),
@@ -1715,7 +1705,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
         LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
         LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
-        LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
         LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
         LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
         LintId::of(transmute::WRONG_TRANSMUTE),
@@ -1759,6 +1748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
         LintId::of(blacklisted_name::BLACKLISTED_NAME),
         LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
+        LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
         LintId::of(casts::FN_TO_NUMERIC_CAST),
         LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
         LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
@@ -1949,7 +1939,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
         LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
         LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
-        LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
         LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
         LintId::of(types::BORROWED_BOX),
         LintId::of(types::TYPE_COMPLEXITY),
@@ -2012,6 +2001,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
         LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
         LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
+        LintId::of(ptr::INVALID_NULL_PTR_USAGE),
         LintId::of(ptr::MUT_FROM_REF),
         LintId::of(ranges::REVERSED_EMPTY_RANGES),
         LintId::of(regex::INVALID_REGEX),
@@ -2153,6 +2143,15 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
     ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
     ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
     ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
+
+    // uplifted lints
+    ls.register_renamed("clippy::invalid_ref", "invalid_value");
+    ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
+    ls.register_renamed("clippy::unused_label", "unused_labels");
+    ls.register_renamed("clippy::drop_bounds", "drop_bounds");
+    ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
+    ls.register_renamed("clippy::panic_params", "non_fmt_panic");
+    ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
 }
 
 // only exists to let the dogfood integration test works.
index 4871a03118739d15261d2a7bb557ea46f2a27447..c7a28f42ea19b65e24a0019be672549c4cb7a655 100644 (file)
@@ -1,24 +1,25 @@
 use super::EXPLICIT_INTO_ITER_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_trait_method, paths};
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::TyS;
 
-pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) {
-    let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
-    let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
-    if !TyS::same_type(receiver_ty, receiver_ty_adjusted) {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) {
+    let self_ty = cx.typeck_results().expr_ty(self_arg);
+    let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
+    if !(TyS::same_type(self_ty, self_ty_adjusted) && match_trait_method(cx, call_expr, &paths::INTO_ITERATOR)) {
         return;
     }
 
     let mut applicability = Applicability::MachineApplicable;
-    let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+    let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
     span_lint_and_sugg(
         cx,
         EXPLICIT_INTO_ITER_LOOP,
-        arg.span,
+        call_expr.span,
         "it is more concise to loop over containers instead of using explicit \
             iteration methods",
         "to write this more concisely, try",
index 92aa2beb66d455690c6aa05a44f2074849b7afbc..ce02ad013bef64df5463e0ed324905de543f87e5 100644 (file)
@@ -9,12 +9,12 @@
 use rustc_middle::ty::{self, Ty, TyS};
 use rustc_span::sym;
 
-pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
     let should_lint = match method_name {
-        "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
+        "iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
         "into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => {
-            let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
-            let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
+            let receiver_ty = cx.typeck_results().expr_ty(self_arg);
+            let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
             let ref_receiver_ty = cx.tcx.mk_ref(
                 cx.tcx.lifetimes.re_erased,
                 ty::TypeAndMut {
@@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, met
     }
 
     let mut applicability = Applicability::MachineApplicable;
-    let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+    let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
     let muta = if method_name == "iter_mut" { "mut " } else { "" };
     span_lint_and_sugg(
         cx,
index 94743cfcf4657705f708f7e5d94ff61592c900d5..64ff7574f86b7669d4efbf498e09527aa100abe4 100644 (file)
@@ -1,10 +1,11 @@
 use super::utils::make_iterator_snippet;
 use super::MANUAL_FLATTEN;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id};
+use clippy_utils::{is_lang_ctor, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
+use rustc_hir::LangItem::{OptionSome, ResultOk};
+use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::source_map::Span;
@@ -42,9 +43,9 @@ pub(super) fn check<'tcx>(
             if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
             if path_to_local_id(match_expr, pat_hir_id);
             // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
-            if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
-            let some_ctor = is_some_ctor(cx, path.res);
-            let ok_ctor = is_ok_ctor(cx, path.res);
+            if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind;
+            let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
+            let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
             if some_ctor || ok_ctor;
             then {
                 let if_let_type = if some_ctor { "Some" } else { "Ok" };
index 28acefd51fef74554630f7bea55ae5469c18b2cb..a4bc3e6bd100cd4c47c9b31febd8144209b183a2 100644 (file)
@@ -602,22 +602,19 @@ fn check_for_loop<'tcx>(
 fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
     let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
 
-    if let ExprKind::MethodCall(method, _, args, _) = arg.kind {
-        // just the receiver, no arguments
-        if args.len() == 1 {
-            let method_name = &*method.ident.as_str();
-            // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
-            match method_name {
-                "iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name),
-                "into_iter" => {
-                    explicit_iter_loop::check(cx, args, arg, method_name);
-                    explicit_into_iter_loop::check(cx, args, arg);
-                },
-                "next" => {
-                    next_loop_linted = iter_next_loop::check(cx, arg, expr);
-                },
-                _ => {},
-            }
+    if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind {
+        let method_name = &*method.ident.as_str();
+        // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
+        match method_name {
+            "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name),
+            "into_iter" => {
+                explicit_iter_loop::check(cx, self_arg, arg, method_name);
+                explicit_into_iter_loop::check(cx, self_arg, arg);
+            },
+            "next" => {
+                next_loop_linted = iter_next_loop::check(cx, arg, expr);
+            },
+            _ => {},
         }
     }
 
index 96720764e1658979b5563f811a19bfa09df7cb42..e97b7c9417033d9a9929f7a994fafb5f08f4b0e5 100644 (file)
@@ -100,7 +100,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
         ExprKind::Binary(_, e1, e2)
         | ExprKind::Assign(e1, e2, _)
         | ExprKind::AssignOp(_, e1, e2)
-        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id),
+        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
         ExprKind::Loop(b, _, _, _) => {
             // Break can come from the inner loop so remove them.
             absorb_break(&never_loop_block(b, main_loop_id))
index fc067e81bcafb7cd25b0817fc0f0e58106daae25..0fd09ff7197ae7e03e42966a528bb596b1b0233e 100644 (file)
@@ -14,8 +14,14 @@ pub(super) fn check<'tcx>(
     body: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
 ) {
+    let arg_expr = match arg.kind {
+        ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
+        ExprKind::MethodCall(method, _, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => {
+            &args[0]
+        },
+        _ => return,
+    };
     if_chain! {
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind;
         if let PatKind::Binding(.., target, _) = pat.kind;
         if let ExprKind::Array([arg_expression]) = arg_expr.kind;
         if let ExprKind::Path(ref list_item) = arg_expression.kind;
index c506d52e7462a09f5417bff79058ee9372b1581c..ec03daff87b075c2fd470a798a79ccc092bec5f5 100644 (file)
@@ -112,6 +112,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
             let attrs = cx.tcx.hir().attrs(item.hir_id());
             if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
             if let Res::Def(DefKind::Mod, id) = path.res;
+            if !id.is_local();
             then {
                 for kid in cx.tcx.item_children(id).iter() {
                     if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
index 8c9e3af62f482568fc5382df4e9b6878410ee94f..0b873534f2c8d4be30d6fc29ed6191edebd49f69 100644 (file)
@@ -1,15 +1,14 @@
 use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
-use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
-use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{
+    can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
+};
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::Applicability;
-use rustc_hir::{
-    def::Res,
-    intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
-    Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath,
-};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -103,12 +102,18 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 None => return,
             };
 
+            // These two lints will go back and forth with each other.
             if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
                 && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
             {
                 return;
             }
 
+            // `map` won't perform any adjustments.
+            if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+                return;
+            }
+
             if !can_move_expr_to_closure(cx, some_expr) {
                 return;
             }
@@ -192,51 +197,6 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     }
 }
 
-// Checks if the expression can be moved into a closure as is.
-fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    struct V<'cx, 'tcx> {
-        cx: &'cx LateContext<'tcx>,
-        make_closure: bool,
-    }
-    impl Visitor<'tcx> for V<'_, 'tcx> {
-        type Map = ErasedMap<'tcx>;
-        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-            NestedVisitorMap::None
-        }
-
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            match e.kind {
-                ExprKind::Break(..)
-                | ExprKind::Continue(_)
-                | ExprKind::Ret(_)
-                | ExprKind::Yield(..)
-                | ExprKind::InlineAsm(_)
-                | ExprKind::LlvmInlineAsm(_) => {
-                    self.make_closure = false;
-                },
-                // Accessing a field of a local value can only be done if the type isn't
-                // partially moved.
-                ExprKind::Field(base_expr, _)
-                    if matches!(
-                        base_expr.kind,
-                        ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
-                    ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) =>
-                {
-                    // TODO: check if the local has been partially moved. Assume it has for now.
-                    self.make_closure = false;
-                    return;
-                }
-                _ => (),
-            };
-            walk_expr(self, e);
-        }
-    }
-
-    let mut v = V { cx, make_closure: true };
-    v.visit_expr(expr);
-    v.make_closure
-}
-
 // Checks whether the expression could be passed as a function, or whether a closure is needed.
 // Returns the function to be passed to `map` if it exists.
 fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
@@ -269,20 +229,9 @@ fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxC
         match pat.kind {
             PatKind::Wild => Some(OptionPat::Wild),
             PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
-            PatKind::Path(QPath::Resolved(None, path))
-                if path
-                    .res
-                    .opt_def_id()
-                    .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) =>
-            {
-                Some(OptionPat::None)
-            },
-            PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _)
-                if path
-                    .res
-                    .opt_def_id()
-                    .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME))
-                    && pat.span.ctxt() == ctxt =>
+            PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
+            PatKind::TupleStruct(ref qpath, [pattern], _)
+                if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
             {
                 Some(OptionPat::Some { pattern, ref_count })
             },
@@ -298,17 +247,11 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
     match expr.kind {
         ExprKind::Call(
             Expr {
-                kind: ExprKind::Path(QPath::Resolved(None, path)),
+                kind: ExprKind::Path(ref qpath),
                 ..
             },
             [arg],
-        ) if ctxt == expr.span.ctxt() => {
-            if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) {
-                Some(arg)
-            } else {
-                None
-            }
-        },
+        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
         ExprKind::Block(
             Block {
                 stmts: [],
@@ -324,10 +267,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
 // Checks for the `None` value.
 fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
     match expr.kind {
-        ExprKind::Path(QPath::Resolved(None, path)) => path
-            .res
-            .opt_def_id()
-            .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)),
+        ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
         ExprKind::Block(
             Block {
                 stmts: [],
index dc19805b50abdbec8ae3e04ca9b5f1426c6b4505..54f714b54b657f6f6e2f25040d2e2e3afd4f1b4d 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::meets_msrv;
 use clippy_utils::source::snippet_opt;
+use clippy_utils::{meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
 use rustc_errors::Applicability;
@@ -10,8 +10,6 @@
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, Span};
 
-const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
     ///
@@ -76,7 +74,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl EarlyLintPass for ManualNonExhaustive {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
             return;
         }
 
index 9bfae602c407d26f6bf78f761f86f3eeccbfbbf7..847c8c648b00acb21dfd4f4d9b6b8e61ebc2968a 100644 (file)
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_lang_ctor, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::{ResultErr, ResultOk};
 use rustc_hir::{Expr, ExprKind, PatKind};
 use rustc_lint::LintContext;
 use rustc_lint::{LateContext, LateLintPass};
@@ -54,7 +55,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
             let or_expr = &args[1];
             if is_ok_wrapping(cx, &args[2]);
             if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
-            if match_qpath(err_path, &paths::RESULT_ERR);
+            if is_lang_ctor(cx, err_path, ResultErr);
             if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
             if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
             if let Some(indent) = indent_of(cx, scrutinee.span);
@@ -81,7 +82,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
 
 fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
     if let ExprKind::Path(ref qpath) = map_expr.kind {
-        if match_qpath(qpath, &paths::RESULT_OK) {
+        if is_lang_ctor(cx, qpath, ResultOk) {
             return true;
         }
     }
@@ -90,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
         let body = cx.tcx.hir().body(body_id);
         if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
         if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
-        if match_qpath(ok_path, &paths::RESULT_OK);
+        if is_lang_ctor(cx, ok_path, ResultOk);
         then { path_to_local_id(ok_arg, param_id) } else { false }
     }
 }
index dfa464ddb81ac4e1043cb17907b998092af7a5d5..23428524dee976f8a9994ba50fd4cdbe335023f0 100644 (file)
@@ -2,7 +2,7 @@
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths};
+use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_hir::def::Res;
@@ -17,8 +17,6 @@
 use rustc_span::source_map::Spanned;
 use rustc_span::Span;
 
-const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
-
 declare_clippy_lint! {
     /// **What it does:**
     /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
@@ -74,7 +72,7 @@ enum StripKind {
 
 impl<'tcx> LateLintPass<'tcx> for ManualStrip {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
             return;
         }
 
index f296d6a1a15f5f3af7f8b2ad688e14ec96fdee47..520162559e50f74690bcaa67c2f93135dea73f7f 100644 (file)
@@ -3,10 +3,11 @@
 use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::contains_return_break_continue_macro;
-use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg};
+use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind};
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::{Arm, Expr, ExprKind, PatKind};
 use rustc_lint::LintContext;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
@@ -68,23 +69,21 @@ fn unwrap_fn_path(&self) -> &str {
 }
 
 fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-    fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
+    fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
         if_chain! {
             if arms.len() == 2;
             if arms.iter().all(|arm| arm.guard.is_none());
-            if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
+            if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
                 match arm.pat.kind {
-                    PatKind::Path(ref some_qpath) =>
-                        match_qpath(some_qpath, &paths::OPTION_NONE),
-                    PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
-                        match_qpath(err_qpath, &paths::RESULT_ERR),
+                    PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
+                    PatKind::TupleStruct(ref qpath, &[pat], _) =>
+                        matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
                     _ => false,
                 }
-            );
+            });
             let unwrap_arm = &arms[1 - idx];
-            if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
-            if match_qpath(unwrap_qpath, &paths::OPTION_SOME)
-                || match_qpath(unwrap_qpath, &paths::RESULT_OK);
+            if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
+            if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
             if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
             if path_to_local_id(unwrap_arm.body, binding_hir_id);
             if !contains_return_break_continue_macro(or_arm.body);
@@ -106,13 +105,22 @@ fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
         } else {
             None
         };
-        if let Some(or_arm) = applicable_or_arm(match_arms);
+        if let Some(or_arm) = applicable_or_arm(cx, match_arms);
         if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
         if let Some(indent) = indent_of(cx, expr.span);
         if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
         then {
             let reindented_or_body =
                 reindent_multiline(or_body_snippet.into(), true, Some(indent));
+
+            let suggestion = if scrutinee.span.from_expansion() {
+                    // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)`
+                    sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
+                }
+                else {
+                    sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
+                };
+
             span_lint_and_sugg(
                 cx,
                 MANUAL_UNWRAP_OR, expr.span,
@@ -120,7 +128,7 @@ fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
                 "replace with",
                 format!(
                     "{}.unwrap_or({})",
-                    sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
+                    suggestion,
                     reindented_or_body,
                 ),
                 Applicability::MachineApplicable,
index e7719e7663d643737787a9846f2eb0bfade3296b..41cda23510ea2f9e19e694e534ebf0f98d5e8834 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks};
+use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
@@ -80,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a
 fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match expr.kind {
         ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
-        ExprKind::Path(QPath::Resolved(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY),
+        ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
         _ => false,
     }
 }
index c8ee0abd3dfefa81cf6db517adbaba50ea815628..13b2a834b0a962ac4db369573dab48ddfc9bda16 100644 (file)
@@ -7,14 +7,16 @@
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
 use clippy_utils::visitors::LocalUsedVisitor;
 use clippy_utils::{
-    get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local,
-    path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
+    get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs,
+    path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+    strip_pat_refs,
 };
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{
     self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
     Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
     /// **Why is this bad?** It's more concise and clear to just use the proper
     /// utility function
     ///
-    /// **Known problems:** None.
+    /// **Known problems:** This will change the drop order for the matched type. Both `if let` and
+    /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
+    /// value before entering the block. For most types this change will not matter, but for a few
+    /// types this will not be an acceptable change (e.g. locks). See the
+    /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
+    /// drop order.
     ///
     /// **Example:**
     ///
@@ -572,8 +579,6 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
     MATCH_SAME_ARMS,
 ]);
 
-const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0);
-
 impl<'tcx> LateLintPass<'tcx> for Matches {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
@@ -582,7 +587,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 
         redundant_pattern_match::check(cx, expr);
 
-        if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
             if !check_match_like_matches(cx, expr) {
                 lint_match_arms(cx, expr);
             }
@@ -737,8 +742,11 @@ fn report_single_match_single_pattern(
     let (msg, sugg) = if_chain! {
         if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
         let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
-        if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
-        if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
+        if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
+        if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
+        if ty.is_integral() || ty.is_char() || ty.is_str()
+            || (implements_trait(cx, ty, spe_trait_id, &[])
+                && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
         then {
             // scrutinee derives PartialEq and the pattern is a constant.
             let pat_ref_count = match pat.kind {
@@ -1120,7 +1128,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
             Applicability::MaybeIncorrect,
         ),
         variants => {
-            let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect();
+            let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
             let message = if adt_def.is_variant_list_non_exhaustive() {
                 suggestions.push("_".into());
                 "wildcard matches known variants and will also match future added variants"
@@ -1189,10 +1197,10 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e
 
 fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
-        let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
-            is_ref_some_arm(&arms[1])
-        } else if is_none_arm(&arms[1]) {
-            is_ref_some_arm(&arms[0])
+        let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
+            is_ref_some_arm(cx, &arms[1])
+        } else if is_none_arm(cx, &arms[1]) {
+            is_ref_some_arm(cx, &arms[0])
         } else {
             None
         };
@@ -1500,7 +1508,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'
 /// Gets all arms that are unbounded `PatRange`s.
 fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
     arms.iter()
-        .flat_map(|arm| {
+        .filter_map(|arm| {
             if let Arm { pat, guard: None, .. } = *arm {
                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
                     let lhs = match lhs {
@@ -1575,20 +1583,20 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool {
 }
 
 // Checks if arm has the form `None => None`
-fn is_none_arm(arm: &Arm<'_>) -> bool {
-    matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
+fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+    matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 }
 
 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
-fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
+fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
     if_chain! {
-        if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind;
-        if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
+        if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind;
+        if is_lang_ctor(cx, qpath, OptionSome);
         if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
         if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
         if let ExprKind::Path(ref some_path) = e.kind;
-        if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
+        if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
         if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
         then {
@@ -1699,54 +1707,206 @@ fn cmp(&self, other: &Self) -> Ordering {
 mod redundant_pattern_match {
     use super::REDUNDANT_PATTERN_MATCHING;
     use clippy_utils::diagnostics::span_lint_and_then;
-    use clippy_utils::source::snippet;
-    use clippy_utils::{is_trait_method, match_qpath, paths};
+    use clippy_utils::source::{snippet, snippet_with_applicability};
+    use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+    use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
     use if_chain::if_chain;
     use rustc_ast::ast::LitKind;
     use rustc_errors::Applicability;
-    use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
+    use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
+    use rustc_hir::{
+        intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath,
+    };
     use rustc_lint::LateContext;
+    use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
     use rustc_span::sym;
 
     pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
             match match_source {
                 MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
-                MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
-                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
+                MatchSource::IfLetDesugar { contains_else_clause } => {
+                    find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause)
+                },
+                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false),
                 _ => {},
             }
         }
     }
 
+    /// Checks if the drop order for a type matters. Some std types implement drop solely to
+    /// deallocate memory. For these types, and composites containing them, changing the drop order
+    /// won't result in any observable side effects.
+    fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+        if !ty.needs_drop(cx.tcx, cx.param_env) {
+            false
+        } else if !cx
+            .tcx
+            .lang_items()
+            .drop_trait()
+            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+        {
+            // This type doesn't implement drop, so no side effects here.
+            // Check if any component type has any.
+            match ty.kind() {
+                ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)),
+                ty::Array(ty, _) => type_needs_ordered_drop(cx, ty),
+                ty::Adt(adt, subs) => adt
+                    .all_fields()
+                    .map(|f| f.ty(cx.tcx, subs))
+                    .any(|ty| type_needs_ordered_drop(cx, ty)),
+                _ => true,
+            }
+        }
+        // Check for std types which implement drop, but only for memory allocation.
+        else if is_type_diagnostic_item(cx, ty, sym::vec_type)
+            || is_type_lang_item(cx, ty, LangItem::OwnedBox)
+            || is_type_diagnostic_item(cx, ty, sym::Rc)
+            || is_type_diagnostic_item(cx, ty, sym::Arc)
+            || is_type_diagnostic_item(cx, ty, sym::cstring_type)
+            || match_type(cx, ty, &paths::BTREEMAP)
+            || match_type(cx, ty, &paths::LINKED_LIST)
+            || match_type(cx, ty, &paths::WEAK_RC)
+            || match_type(cx, ty, &paths::WEAK_ARC)
+        {
+            // Check all of the generic arguments.
+            if let ty::Adt(_, subs) = ty.kind() {
+                subs.types().any(|ty| type_needs_ordered_drop(cx, ty))
+            } else {
+                true
+            }
+        } else {
+            true
+        }
+    }
+
+    // Extract the generic arguments out of a type
+    fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
+        if_chain! {
+            if let ty::Adt(_, subs) = ty.kind();
+            if let Some(sub) = subs.get(index);
+            if let GenericArgKind::Type(sub_ty) = sub.unpack();
+            then {
+                Some(sub_ty)
+            } else {
+                None
+            }
+        }
+    }
+
+    // Checks if there are any temporaries created in the given expression for which drop order
+    // matters.
+    fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+        struct V<'a, 'tcx> {
+            cx: &'a LateContext<'tcx>,
+            res: bool,
+        }
+        impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+            type Map = ErasedMap<'tcx>;
+            fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+                NestedVisitorMap::None
+            }
+
+            fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+                match expr.kind {
+                    // Taking the reference of a value leaves a temporary
+                    // e.g. In `&String::new()` the string is a temporary value.
+                    // Remaining fields are temporary values
+                    // e.g. In `(String::new(), 0).1` the string is a temporary value.
+                    ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
+                        if !matches!(expr.kind, ExprKind::Path(_)) {
+                            if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(expr);
+                            }
+                        }
+                    },
+                    // the base type is alway taken by reference.
+                    // e.g. In `(vec![0])[0]` the vector is a temporary value.
+                    ExprKind::Index(base, index) => {
+                        if !matches!(base.kind, ExprKind::Path(_)) {
+                            if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(base);
+                            }
+                        }
+                        self.visit_expr(index);
+                    },
+                    // Method calls can take self by reference.
+                    // e.g. In `String::new().len()` the string is a temporary value.
+                    ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
+                        if !matches!(self_arg.kind, ExprKind::Path(_)) {
+                            let self_by_ref = self
+                                .cx
+                                .typeck_results()
+                                .type_dependent_def_id(expr.hir_id)
+                                .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
+                            if self_by_ref
+                                && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
+                            {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(self_arg)
+                            }
+                        }
+                        args.iter().for_each(|arg| self.visit_expr(arg));
+                    },
+                    // Either explicitly drops values, or changes control flow.
+                    ExprKind::DropTemps(_)
+                    | ExprKind::Ret(_)
+                    | ExprKind::Break(..)
+                    | ExprKind::Yield(..)
+                    | ExprKind::Block(Block { expr: None, .. }, _)
+                    | ExprKind::Loop(..) => (),
+
+                    // Only consider the final expression.
+                    ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
+
+                    _ => walk_expr(self, expr),
+                }
+            }
+        }
+
+        let mut v = V { cx, res: false };
+        v.visit_expr(expr);
+        v.res
+    }
+
     fn find_sugg_for_if_let<'tcx>(
         cx: &LateContext<'tcx>,
         expr: &'tcx Expr<'_>,
-        op: &Expr<'_>,
-        arms: &[Arm<'_>],
+        op: &'tcx Expr<'tcx>,
+        arm: &Arm<'_>,
         keyword: &'static str,
+        has_else: bool,
     ) {
         // also look inside refs
-        let mut kind = &arms[0].pat.kind;
+        let mut kind = &arm.pat.kind;
         // if we have &None for example, peel it so we can detect "if let None = x"
         if let PatKind::Ref(inner, _mutability) = kind {
             kind = &inner.kind;
         }
-        let good_method = match kind {
-            PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => {
-                if let PatKind::Wild = patterns[0].kind {
-                    if match_qpath(path, &paths::RESULT_OK) {
-                        "is_ok()"
-                    } else if match_qpath(path, &paths::RESULT_ERR) {
-                        "is_err()"
-                    } else if match_qpath(path, &paths::OPTION_SOME) {
-                        "is_some()"
-                    } else if match_qpath(path, &paths::POLL_READY) {
-                        "is_ready()"
-                    } else if match_qpath(path, &paths::IPADDR_V4) {
-                        "is_ipv4()"
-                    } else if match_qpath(path, &paths::IPADDR_V6) {
-                        "is_ipv6()"
+        let op_ty = cx.typeck_results().expr_ty(op);
+        // Determine which function should be used, and the type contained by the corresponding
+        // variant.
+        let (good_method, inner_ty) = match kind {
+            PatKind::TupleStruct(ref path, [sub_pat], _) => {
+                if let PatKind::Wild = sub_pat.kind {
+                    if is_lang_ctor(cx, path, ResultOk) {
+                        ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
+                    } else if is_lang_ctor(cx, path, ResultErr) {
+                        ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
+                    } else if is_lang_ctor(cx, path, OptionSome) {
+                        ("is_some()", op_ty)
+                    } else if is_lang_ctor(cx, path, PollReady) {
+                        ("is_ready()", op_ty)
+                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
+                        ("is_ipv4()", op_ty)
+                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
+                        ("is_ipv6()", op_ty)
                     } else {
                         return;
                     }
@@ -1755,17 +1915,36 @@ fn find_sugg_for_if_let<'tcx>(
                 }
             },
             PatKind::Path(ref path) => {
-                if match_qpath(path, &paths::OPTION_NONE) {
+                let method = if is_lang_ctor(cx, path, OptionNone) {
                     "is_none()"
-                } else if match_qpath(path, &paths::POLL_PENDING) {
+                } else if is_lang_ctor(cx, path, PollPending) {
                     "is_pending()"
                 } else {
                     return;
-                }
+                };
+                // `None` and `Pending` don't have an inner type.
+                (method, cx.tcx.types.unit)
             },
             _ => return,
         };
 
+        // If this is the last expression in a block or there is an else clause then the whole
+        // type needs to be considered, not just the inner type of the branch being matched on.
+        // Note the last expression in a block is dropped after all local bindings.
+        let check_ty = if has_else
+            || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
+        {
+            op_ty
+        } else {
+            inner_ty
+        };
+
+        // All temporaries created in the scrutinee expression are dropped at the same time as the
+        // scrutinee would be, so they have to be considered as well.
+        // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
+        // for the duration if body.
+        let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op);
+
         // check that `while_let_on_iterator` lint does not trigger
         if_chain! {
             if keyword == "while";
@@ -1784,7 +1963,7 @@ fn find_sugg_for_if_let<'tcx>(
         span_lint_and_then(
             cx,
             REDUNDANT_PATTERN_MATCHING,
-            arms[0].pat.span,
+            arm.pat.span,
             &format!("redundant pattern matching, consider using `{}`", good_method),
             |diag| {
                 // while let ... = ... { ... }
@@ -1798,12 +1977,20 @@ fn find_sugg_for_if_let<'tcx>(
                 // while let ... = ... { ... }
                 // ^^^^^^^^^^^^^^^^^^^
                 let span = expr_span.until(op_span.shrink_to_hi());
-                diag.span_suggestion(
-                    span,
-                    "try this",
-                    format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
-                    Applicability::MachineApplicable, // snippet
-                );
+
+                let mut app = if needs_drop {
+                    Applicability::MaybeIncorrect
+                } else {
+                    Applicability::MachineApplicable
+                };
+                let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+                diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
+
+                if needs_drop {
+                    diag.note("this will change drop order of the result, as well as all temporaries");
+                    diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
+                }
             },
         );
     }
@@ -1819,6 +2006,7 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
                 ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
                     if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
                         find_good_method_for_match(
+                            cx,
                             arms,
                             path_left,
                             path_right,
@@ -1829,6 +2017,7 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
                         )
                         .or_else(|| {
                             find_good_method_for_match(
+                                cx,
                                 arms,
                                 path_left,
                                 path_right,
@@ -1848,6 +2037,7 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
                 {
                     if let PatKind::Wild = patterns[0].kind {
                         find_good_method_for_match(
+                            cx,
                             arms,
                             path_left,
                             path_right,
@@ -1858,6 +2048,7 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
                         )
                         .or_else(|| {
                             find_good_method_for_match(
+                                cx,
                                 arms,
                                 path_left,
                                 path_right,
@@ -1898,7 +2089,9 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
         }
     }
 
+    #[allow(clippy::too_many_arguments)]
     fn find_good_method_for_match<'a>(
+        cx: &LateContext<'_>,
         arms: &[Arm<'_>],
         path_left: &QPath<'_>,
         path_right: &QPath<'_>,
@@ -1907,9 +2100,13 @@ fn find_good_method_for_match<'a>(
         should_be_left: &'a str,
         should_be_right: &'a str,
     ) -> Option<&'a str> {
-        let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
+        let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
+            && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
+        {
             (&(*arms[0].body).kind, &(*arms[1].body).kind)
-        } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
+        } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
+            && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
+        {
             (&(*arms[1].body).kind, &(*arms[0].body).kind)
         } else {
             return None;
index e1d351aee45479426fa299d54ec73f022e6a05ee..183daee36177489843452fb7ce3350a2a6c742ff 100644 (file)
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::is_diagnostic_assoc_item;
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths};
+use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem::OptionNone;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
     if let ExprKind::Path(ref replacement_qpath) = src.kind {
         // Check that second argument is `Option::None`
-        if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
+        if is_lang_ctor(cx, replacement_qpath, OptionNone) {
             // Since this is a late pass (already type-checked),
             // and we already know that the second argument is an
             // `Option`, we do not need to check the first
@@ -210,17 +210,17 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
         sym::BinaryHeap,
     ];
 
-    if std_types_symbols
-        .iter()
-        .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol))
-    {
-        if let QPath::TypeRelative(_, method) = path {
-            if method.ident.name == sym::new {
-                return true;
+    if let QPath::TypeRelative(_, method) = path {
+        if method.ident.name == sym::new {
+            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+                    return std_types_symbols
+                        .iter()
+                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
+                }
             }
         }
     }
-
     false
 }
 
@@ -230,7 +230,7 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
         if !in_external_macro(cx.tcx.sess, expr_span);
         if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
         if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
-        if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default)
+        if is_diag_trait_item(cx, repl_def_id, sym::Default)
             || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
 
         then {
@@ -256,8 +256,6 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
     }
 }
 
-const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
 pub struct MemReplace {
     msrv: Option<RustcVersion>,
 }
@@ -281,7 +279,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             then {
                 check_replace_option_with_none(cx, src, dest, expr.span);
                 check_replace_with_uninit(cx, src, dest, expr.span);
-                if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) {
+                if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) {
                     check_replace_with_default(cx, src, dest, expr.span);
                 }
             }
diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
new file mode 100644 (file)
index 0000000..ecec6da
--- /dev/null
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::{get_iterator_item_ty, is_copy};
+use clippy_utils::{is_trait_method, meets_msrv, msrvs};
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_semver::RustcVersion;
+use rustc_span::{sym, Span};
+
+use super::CLONED_INSTEAD_OF_COPIED;
+
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) {
+    let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
+    let inner_ty = match recv_ty.kind() {
+        // `Option<T>` -> `T`
+        ty::Adt(adt, subst)
+            if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) && meets_msrv(msrv, &msrvs::OPTION_COPIED) =>
+        {
+            subst.type_at(0)
+        },
+        _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => {
+            match get_iterator_item_ty(cx, recv_ty) {
+                // <T as Iterator>::Item
+                Some(ty) => ty,
+                _ => return,
+            }
+        },
+        _ => return,
+    };
+    match inner_ty.kind() {
+        // &T where T: Copy
+        ty::Ref(_, ty, _) if is_copy(cx, ty) => {},
+        _ => return,
+    };
+    span_lint_and_sugg(
+        cx,
+        CLONED_INSTEAD_OF_COPIED,
+        span,
+        "used `cloned` where `copied` could be used instead",
+        "try",
+        "copied".into(),
+        Applicability::MachineApplicable,
+    )
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs
deleted file mode 100644 (file)
index 1588eec..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-    // lint if caller of `.filter().flat_map()` is an Iterator
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
-        let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
-                    and filtering by returning `iter::empty()`";
-        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs
deleted file mode 100644 (file)
index 741b1e7..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter_map().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-    // lint if caller of `.filter_map().flat_map()` is an Iterator
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
-        let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
-                    and filtering by returning `iter::empty()`";
-        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
-    }
-}
index 3a61f4ccad78930ccc5aca26b70de78a1727e1a1..403fe8d35468468db91a9c46a13dd2755b5c9fd7 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -33,14 +33,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg:
             }
         }
 
-        if_chain! {
-            if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind;
-
-            if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
-
-            then {
-                apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
-            }
+        if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
+            apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs
deleted file mode 100644 (file)
index 713bbf2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter_map().map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-    // lint if caller of `.filter_map().map()` is an Iterator
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let msg = "called `filter_map(..).map(..)` on an `Iterator`";
-        let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
-        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
-    }
-}
index 2b19e4ee8c055b16c9b962465dbc43769ebbce98..f0d69a1f42e7b911a6c39e34ce4cb1091eeedb95 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet;
-use clippy_utils::{is_trait_method, meets_msrv};
+use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -9,8 +9,6 @@
 
 use super::FILTER_MAP_NEXT;
 
-const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
-
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'_>,
@@ -19,7 +17,7 @@ pub(super) fn check<'tcx>(
     msrv: Option<&RustcVersion>,
 ) {
     if is_trait_method(cx, expr, sym::Iterator) {
-        if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
+        if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) {
             return;
         }
 
index dd613d0cd6384f827c311c580beab208091cb273..25f8434cb94428f458f36fd442fcf8260e542828 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_trait_method, match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -16,8 +16,6 @@ pub(super) fn check<'tcx>(
     flat_map_span: Span,
 ) {
     if is_trait_method(cx, expr, sym::Iterator) {
-        let arg_node = &flat_map_arg.kind;
-
         let apply_lint = |message: &str| {
             span_lint_and_sugg(
                 cx,
@@ -31,8 +29,8 @@ pub(super) fn check<'tcx>(
         };
 
         if_chain! {
-            if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
-            let body = cx.tcx.hir().body(*body_id);
+            if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind;
+            let body = cx.tcx.hir().body(body_id);
 
             if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
             if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
@@ -45,14 +43,8 @@ pub(super) fn check<'tcx>(
             }
         }
 
-        if_chain! {
-            if let hir::ExprKind::Path(ref qpath) = arg_node;
-
-            if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
-
-            then {
-                apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
-            }
+        if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
+            apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
new file mode 100644 (file)
index 0000000..12d5606
--- /dev/null
@@ -0,0 +1,34 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::{source_map::Span, sym};
+
+use super::FLAT_MAP_OPTION;
+use clippy_utils::ty::is_type_diagnostic_item;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
+    if !is_trait_method(cx, expr, sym::Iterator) {
+        return;
+    }
+    let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
+    let sig = match arg_ty.kind() {
+        ty::Closure(_, substs) => substs.as_closure().sig(),
+        _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx),
+        _ => return,
+    };
+    if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::option_type) {
+        return;
+    }
+    span_lint_and_sugg(
+        cx,
+        FLAT_MAP_OPTION,
+        span,
+        "used `flat_map` where `filter_map` could be used instead",
+        "try",
+        "filter_map".into(),
+        Applicability::MachineApplicable,
+    )
+}
index 707c54f7a3cad180a37af7f2c0175fef03b4eecb..28d0e8cd4ae9e33039c01e7b0c21c34d55166bb4 100644 (file)
@@ -1,26 +1,23 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg};
+use clippy_utils::{is_expr_path_def_path, paths, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::ExprKind;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::Ty;
 use rustc_span::sym;
 
 use super::FROM_ITER_INSTEAD_OF_COLLECT;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) {
     if_chain! {
-        if let hir::ExprKind::Path(path) = func_kind;
-        if match_qpath(path, &["from_iter"]);
+        if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD);
         let ty = cx.typeck_results().expr_ty(expr);
         let arg_ty = cx.typeck_results().expr_ty(&args[0]);
-        if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
         if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 
-        if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
+        if implements_trait(cx, arg_ty, iter_id, &[]);
         then {
             // `expr` implements `FromIterator` trait
             let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
index 1211e2f2bf7cb23105c687a05c208d3bdf8f33eb..81c42de145f6c704f7333c6a6618e8c73427e3c6 100644 (file)
@@ -1,28 +1,36 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_diag_item_method, is_diag_trait_item};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::ExprKind;
 use rustc_lint::LateContext;
 use rustc_middle::ty::TyS;
-use rustc_span::symbol::Symbol;
+use rustc_span::{sym, Span};
 
 use super::IMPLICIT_CLONE;
-use clippy_utils::is_diagnostic_assoc_item;
 
-pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) {
+pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) {
     if_chain! {
-        if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind;
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if match method_name {
+            "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
+            "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
+            "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
+            "to_vec" => cx.tcx.impl_of_method(method_def_id)
+                .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
+                == Some(true),
+            _ => false,
+        };
         let return_type = cx.typeck_results().expr_ty(expr);
-        let input_type = cx.typeck_results().expr_ty(arg).peel_refs();
-        if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        let input_type = cx.typeck_results().expr_ty(recv).peel_refs();
         if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did));
         if TyS::same_type(return_type, input_type);
-        if is_diagnostic_assoc_item(cx, expr_def_id, trait_diagnostic);
         then {
             span_lint_and_sugg(
-                cx,IMPLICIT_CLONE,method_path.ident.span,
-                &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_path.ident.name),
+                cx,
+                IMPLICIT_CLONE,
+                span,
+                &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name),
                 "consider using",
                 "clone".to_string(),
                 Applicability::MachineApplicable
index ecb8b72ef46105b28838b2893c5e41e0946e5157..2fddea7068d961f45d14a81bb3961c1377cce7f0 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::match_qpath;
+use clippy_utils::is_qpath_def_path;
 use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
 use rustc_ast::ast;
@@ -94,11 +94,11 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<M
 
     // `std::T::MAX` `std::T::MIN` constants
     if let hir::ExprKind::Path(path) = &expr.kind {
-        if match_qpath(path, &["core", &ty_str, "MAX"][..]) {
+        if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MAX"][..]) {
             return Some(MinMax::Max);
         }
 
-        if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
+        if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MIN"][..]) {
             return Some(MinMax::Min);
         }
     }
index 4330fea727b3aad373ace7e667c9d61f568b9e10..4d8365fcda12681f72a45fd48e961c2e54ab8b23 100644 (file)
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::meets_msrv;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::mutated_variables;
+use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -11,8 +11,6 @@
 
 use super::MAP_UNWRAP_OR;
 
-const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
-
 /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
 /// Return true if lint triggered
 pub(super) fn check<'tcx>(
@@ -23,13 +21,14 @@ pub(super) fn check<'tcx>(
     unwrap_arg: &'tcx hir::Expr<'_>,
     msrv: Option<&RustcVersion>,
 ) -> bool {
-    if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
-        return false;
-    }
     // lint if the caller of `map()` is an `Option`
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type);
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
 
+    if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) {
+        return false;
+    }
+
     if is_option || is_result {
         // Don't make a suggestion that may fail to compile due to mutably borrowing
         // the same variable twice.
index b1ade5addd6aa089e36707a7960d76860a9a09a3..e15dbb899b36aac49bd00dae5ebf6cc7058a4a5d 100644 (file)
@@ -8,17 +8,16 @@
 mod chars_next_cmp_with_unwrap;
 mod clone_on_copy;
 mod clone_on_ref_ptr;
+mod cloned_instead_of_copied;
 mod expect_fun_call;
 mod expect_used;
 mod filetype_is_file;
-mod filter_flat_map;
 mod filter_map;
-mod filter_map_flat_map;
 mod filter_map_identity;
-mod filter_map_map;
 mod filter_map_next;
 mod filter_next;
 mod flat_map_identity;
+mod flat_map_option;
 mod from_iter_instead_of_collect;
 mod get_unwrap;
 mod implicit_clone;
 use rustc_span::{sym, Span};
 use rustc_typeck::hir_ty_to_ty;
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for usages of `cloned()` on an `Iterator` or `Option` where
+    /// `copied()` could be used instead.
+    ///
+    /// **Why is this bad?** `copied()` is better because it guarantees that the type being cloned
+    /// implements `Copy`.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// [1, 2, 3].iter().cloned();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// [1, 2, 3].iter().copied();
+    /// ```
+    pub CLONED_INSTEAD_OF_COPIED,
+    pedantic,
+    "used `cloned` where `copied` could be used instead"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
+    /// used instead.
+    ///
+    /// **Why is this bad?** When applicable, `filter_map()` is more clear since it shows that
+    /// `Option` is used to produce 0 or 1 items.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
+    /// ```
+    pub FLAT_MAP_OPTION,
+    pedantic,
+    "used `flat_map` where `filter_map` could be used instead"
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
     ///
     "using combinations of `flatten` and `map` which can usually be written as a single method call"
 }
 
-declare_clippy_lint! {
-    /// **What it does:** Checks for usage of `_.filter(_).map(_)`,
-    /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
-    ///
-    /// **Why is this bad?** Readability, this can be written more concisely as
-    /// `_.filter_map(_)`.
-    ///
-    /// **Known problems:** Often requires a condition + Option/Iterator creation
-    /// inside the closure.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// let vec = vec![1];
-    ///
-    /// // Bad
-    /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2);
-    ///
-    /// // Good
-    /// vec.iter().filter_map(|x| if *x == 0 {
-    ///     Some(*x * 2)
-    /// } else {
-    ///     None
-    /// });
-    /// ```
-    pub FILTER_MAP,
-    pedantic,
-    "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
-}
-
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply
     /// as `filter_map(_)`.
@@ -1670,6 +1686,8 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
     CLONE_ON_COPY,
     CLONE_ON_REF_PTR,
     CLONE_DOUBLE_REF,
+    CLONED_INSTEAD_OF_COPIED,
+    FLAT_MAP_OPTION,
     INEFFICIENT_TO_STRING,
     NEW_RET_NO_SELF,
     SINGLE_CHAR_PATTERN,
@@ -1677,7 +1695,6 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
     SEARCH_IS_SOME,
     FILTER_NEXT,
     SKIP_WHILE_NEXT,
-    FILTER_MAP,
     FILTER_MAP_IDENTITY,
     MANUAL_FILTER_MAP,
     MANUAL_FIND_MAP,
@@ -1741,7 +1758,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 
         match expr.kind {
             hir::ExprKind::Call(func, args) => {
-                from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
+                from_iter_instead_of_collect::check(cx, expr, args, func);
             },
             hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
                 or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
@@ -1942,6 +1959,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
             ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
+            ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
             ("collect", []) => match method_call!(recv) {
                 Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
                 Some(("map", [m_recv, m_arg], _)) => {
@@ -1965,10 +1983,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 unnecessary_filter_map::check(cx, expr, arg);
                 filter_map_identity::check(cx, expr, arg, span);
             },
-            ("flat_map", [flm_arg]) => match method_call!(recv) {
-                Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr),
-                Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr),
-                _ => flat_map_identity::check(cx, expr, flm_arg, span),
+            ("flat_map", [arg]) => {
+                flat_map_identity::check(cx, expr, arg, span);
+                flat_map_option::check(cx, expr, arg, span);
             },
             ("flatten", []) => {
                 if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
@@ -1993,7 +2010,6 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                         ("filter", [f_arg]) => {
                             filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false)
                         },
-                        ("filter_map", [_]) => filter_map_map::check(cx, expr),
                         ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
                         _ => {},
                     }
@@ -2025,10 +2041,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 }
             },
             ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
-            ("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr),
-            ("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned),
-            ("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path),
-            ("to_vec", []) => implicit_clone::check(cx, expr, sym::slice),
+            ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
+                implicit_clone::check(cx, name, expr, recv, span);
+            },
             ("unwrap", []) => match method_call!(recv) {
                 Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
                 Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
index 7e9c8fa829decd77f9feb3ce6b1b5fe66ae30955..5a57135038fdc8beac4206c401c6e8167f176c3c 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks};
+use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -12,8 +12,6 @@
 
 use super::OPTION_AS_REF_DEREF;
 
-const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
 /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -23,7 +21,7 @@ pub(super) fn check<'tcx>(
     is_mut: bool,
     msrv: Option<&RustcVersion>,
 ) {
-    if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) {
+    if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) {
         return;
     }
 
index 013a6f90ac97b06bdfd443938f50e64ec7c22649..36a1c13d5be1e1918cab85e6cea28e34920a102c 100644 (file)
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_qpath, paths};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
@@ -32,7 +33,7 @@ pub(super) fn check<'tcx>(
 
     let (lint_name, msg, instead, hint) = {
         let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
-            match_qpath(qpath, &paths::OPTION_NONE)
+            is_lang_ctor(cx, qpath, OptionNone)
         } else {
             return;
         };
@@ -43,7 +44,7 @@ pub(super) fn check<'tcx>(
         }
 
         let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
-            match_qpath(qpath, &paths::OPTION_SOME)
+            is_lang_ctor(cx, qpath, OptionSome)
         } else {
             false
         };
index 0ae65c0c01dba0e06c429fc776362bc9be4e9db6..1a5894e48d14c5ac1c935dae2231e086f248c78c 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{match_def_path, match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -12,8 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
     if_chain! {
         if let hir::ExprKind::Call(callee, args) = recv.kind;
         if args.is_empty();
-        if let hir::ExprKind::Path(ref path) = callee.kind;
-        if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
+        if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT);
         if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr));
         then {
             span_lint(
index 0f28bfdf09e89c3be43a26fe34925201a966b03c..b61c4ffe9b3aebb5abd0a4d39ec94abf1d75e830 100644 (file)
@@ -1,8 +1,9 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 use rustc_span::sym;
@@ -54,14 +55,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
     match &expr.kind {
         hir::ExprKind::Call(func, args) => {
             if let hir::ExprKind::Path(ref path) = func.kind {
-                if match_qpath(path, &paths::OPTION_SOME) {
+                if is_lang_ctor(cx, path, OptionSome) {
                     if path_to_local_id(&args[0], arg_id) {
                         return (false, false);
                     }
                     return (true, false);
                 }
-                // We don't know. It might do anything.
-                return (true, true);
             }
             (true, true)
         },
@@ -85,7 +84,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
             let else_check = check_expression(cx, arg_id, else_arm);
             (if_check.0 | else_check.0, if_check.1 | else_check.1)
         },
-        hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
+        hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true),
         _ => (true, true),
     }
 }
index f6bf37e08b96614007217d910fe0778d3c8ffa6b..0daea47816a512137265160cc7773706380b4b44 100644 (file)
@@ -18,9 +18,7 @@ fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
             ty::Slice(_) => true,
             ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
             ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
-            ty::Array(_, size) => size
-                .try_eval_usize(cx.tcx, cx.param_env)
-                .map_or(false, |size| size < 32),
+            ty::Array(_, size) => size.try_eval_usize(cx.tcx, cx.param_env).is_some(),
             ty::Ref(_, inner, _) => may_slice(cx, inner),
             _ => false,
         }
index 1e0de249a91f1792c8f5c132ad9cd3f710f8a2ee..6e2bcb113c2c1baf6ba899e59a135db8482c7fa3 100644 (file)
@@ -102,6 +102,14 @@ pub(super) fn check<'tcx>(
             .iter()
             .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
     }) {
+        // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
+        if implements_trait
+            && !conventions
+                .iter()
+                .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
+        {
+            return;
+        }
         if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
             let suggestion = {
                 if conventions.len() > 1 {
index afced5a5ce58a246ad38591d9a74f3e326fc4867..0b0cd9be46cf41f02614a99d6a4839aac9c235e3 100644 (file)
@@ -20,8 +20,8 @@
 use crate::consts::{constant, Constant};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats,
-    last_path_segment, match_qpath, unsext, SpanlessEq,
+    expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const,
+    iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
 };
 
 declare_clippy_lint! {
@@ -555,8 +555,8 @@ fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'t
         ExprKind::MethodCall(.., args, _) if args.len() == 1 => {
             if_chain!(
                 if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-                if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString)
-                    || is_diagnostic_assoc_item(cx, expr_def_id, sym::ToOwned);
+                if is_diag_trait_item(cx, expr_def_id, sym::ToString)
+                    || is_diag_trait_item(cx, expr_def_id, sym::ToOwned);
                 then {
                     (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
                 } else {
@@ -564,13 +564,13 @@ fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'t
                 }
             )
         },
-        ExprKind::Call(path, v) if v.len() == 1 => {
-            if let ExprKind::Path(ref path) = path.kind {
-                if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
-                    (cx.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
-                } else {
-                    return;
-                }
+        ExprKind::Call(path, [arg]) => {
+            if expr_path_res(cx, path)
+                .opt_def_id()
+                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
+                .is_some()
+            {
+                (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, ".."))
             } else {
                 return;
             }
index 0dc02431ad5383141ebfbe54c140330f2d1f9c36..59cbc481ed42eccd73758fe2d314b09bde109bff 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::ty::has_drop;
-use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method};
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, msrvs, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
@@ -12,8 +12,6 @@
 use rustc_span::Span;
 use rustc_typeck::hir_ty_to_ty;
 
-const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
-
 declare_clippy_lint! {
     /// **What it does:**
     ///
@@ -97,7 +95,7 @@ fn check_fn(
         span: Span,
         hir_id: HirId,
     ) {
-        if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) {
             return;
         }
 
@@ -138,8 +136,8 @@ fn check_fn(
 
         let mir = cx.tcx.optimized_mir(def_id);
 
-        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) {
-            if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
+        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
+            if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
                 cx.tcx.sess.span_err(span, &err);
             }
         } else {
index 6a52de4f713644f965009ae3fd31bf77ad7232ee..64e9dc85466eb6e5a969b8d5d56a559e6bfe9a52 100644 (file)
@@ -4,7 +4,7 @@
 use if_chain::if_chain;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self};
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use std::fmt::Display;
 
index 96a58d1410f226b01058fd56dc9ecbbb6ca9bba2..dd4581986377f54b02e6034a3a1bf23e487af971 100644 (file)
@@ -5,7 +5,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_expn_of, parent_node_is_if_expr};
+use clippy_utils::{is_else_clause, is_expn_of};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
@@ -81,7 +81,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                     snip = snip.make_return();
                 }
 
-                if parent_node_is_if_expr(e, cx) {
+                if is_else_clause(cx.tcx, e) {
                     snip = snip.blockify()
                 }
 
index 780e224129347492b47447beaf512dba264affa5..e33a33e238633aeaf5b68930e536070d80b73cb5 100644 (file)
@@ -279,7 +279,7 @@ fn check_fn(
                             spans.extend(
                                 deref_span
                                     .iter()
-                                    .cloned()
+                                    .copied()
                                     .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                             );
                             spans.sort_by_key(|&(span, _)| span);
index 9852633b7342e48b8448d0c8228b2f9a3189581b..7b156a8c49dd9d93aec449e7898ce3acb7c09dba 100644 (file)
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv};
+use clippy_utils::{differing_macro_contexts, is_lang_ctor};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionSome, ResultOk};
 use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
 
 declare_clippy_lint! {
     "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
 }
 
-const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0);
-const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0);
-
-pub struct NeedlessQuestionMark {
-    msrv: Option<RustcVersion>,
-}
-
-impl NeedlessQuestionMark {
-    #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
-    }
-}
-
-impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
+declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
 
 #[derive(Debug)]
 enum SomeOkCall<'a> {
@@ -109,7 +95,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
             _ => return,
         };
 
-        if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) {
+        if let Some(ok_some_call) = is_some_or_ok_call(cx, e) {
             emit_lint(cx, &ok_some_call);
         }
     }
@@ -125,14 +111,12 @@ fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
 
         if_chain! {
             if let Some(expr) = expr_opt;
-            if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr);
+            if let Some(ok_some_call) = is_some_or_ok_call(cx, expr);
             then {
                 emit_lint(cx, &ok_some_call);
             }
         };
     }
-
-    extract_msrv_attr!(LateContext);
 }
 
 fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
@@ -151,16 +135,12 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
     );
 }
 
-fn is_some_or_ok_call<'a>(
-    nqml: &NeedlessQuestionMark,
-    cx: &'a LateContext<'_>,
-    expr: &'a Expr<'_>,
-) -> Option<SomeOkCall<'a>> {
+fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<SomeOkCall<'a>> {
     if_chain! {
         // Check outer expression matches CALL_IDENT(ARGUMENT) format
         if let ExprKind::Call(path, args) = &expr.kind;
-        if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
-        if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
+        if let ExprKind::Path(ref qpath) = &path.kind;
+        if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
 
         // Extract inner expression from ARGUMENT
         if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
@@ -186,8 +166,7 @@ fn is_some_or_ok_call<'a>(
             let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type);
 
             // Check for Option MSRV
-            let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
-            if outer_is_some && inner_is_some && meets_option_msrv {
+            if outer_is_some && inner_is_some {
                 return Some(SomeOkCall::SomeCall(expr, inner_expr));
             }
 
@@ -200,8 +179,7 @@ fn is_some_or_ok_call<'a>(
             let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
 
             // Must meet Result MSRV
-            let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
-            if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
+            if outer_is_result && inner_is_result && does_not_call_from {
                 return Some(SomeOkCall::OkCall(expr, inner_expr));
             }
         }
index 1b9120ae45f54c763dd72209e3b6295ec54b3d6a..e527adbb8929def838d0e8dc9ef6ffbfe6b3ad52 100644 (file)
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::paths;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::contains_return_break_continue_macro;
-use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath};
+use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::OptionSome;
 use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -164,7 +164,7 @@ fn detect_option_if_let_else<'tcx>(
         if arms.len() == 2;
         if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
         if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
-        if match_qpath(struct_qpath, &paths::OPTION_SOME);
+        if is_lang_ctor(cx, struct_qpath, OptionSome);
         if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
         if !contains_return_break_continue_macro(arms[0].body);
         if !contains_return_break_continue_macro(arms[1].body);
index d32b937b209c5324f4fbf1e4b518062387b04a75..cef74d87e7c016a8348dc5081fd6fddde0d01521 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{find_macro_calls, return_ty};
+use clippy_utils::{find_macro_calls, is_expn_of, return_ty};
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LateLintPass};
@@ -52,7 +52,7 @@ fn check_fn(
 }
 
 fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
-    let panics = find_macro_calls(
+    let mut panics = find_macro_calls(
         &[
             "unimplemented",
             "unreachable",
@@ -61,12 +61,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
             "assert",
             "assert_eq",
             "assert_ne",
-            "debug_assert",
-            "debug_assert_eq",
-            "debug_assert_ne",
         ],
         body,
     );
+    panics.retain(|span| is_expn_of(*span, "debug_assert").is_none());
     if !panics.is_empty() {
         span_lint_and_then(
             cx,
index 1e9468589472326ad23098a935f3de9ff052a747..1a680e7607e0b21e0116df6e0fa4363af52081b9 100644 (file)
@@ -74,7 +74,7 @@
 
 impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if match_panic_call(cx, expr).is_some() {
+        if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() {
             let span = get_outer_span(expr);
             if is_expn_of(expr.span, "unimplemented").is_some() {
                 span_lint(
index 6f80e447c7ddd71fd99a21e8d69a4f2890d860af..6b64846c24d101026756f0d222d318b935bb6724 100644 (file)
     /// false positives in cases involving multiple lifetimes that are bounded by
     /// each other.
     ///
+    /// Also, it does not take account of other similar cases where getting memory addresses
+    /// matters; namely, returning the pointer to the argument in question,
+    /// and passing the argument, as both references and pointers,
+    /// to a function that needs the memory address. For further details, refer to
+    /// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953)
+    /// that explains a real case in which this false positive
+    /// led to an **undefined behaviour** introduced with unsafe code.
+    ///
     /// **Example:**
     ///
     /// ```rust
index 09fcdb5faf84272c37e10a7de8a4a7922be48768..b0674f90678362d0248ac4def6f5fa738e50a83d 100644 (file)
@@ -4,7 +4,7 @@
 use clippy_utils::ptr::get_spans;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
-use clippy_utils::{is_allowed, match_qpath, paths};
+use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
@@ -15,6 +15,7 @@
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
+use rustc_span::symbol::Symbol;
 use rustc_span::{sym, MultiSpan};
 use std::borrow::Cow;
 
@@ -94,7 +95,7 @@
     /// ```
     pub CMP_NULL,
     style,
-    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
+    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
 }
 
 declare_clippy_lint! {
     "fns that create mutable refs from immutable ref args"
 }
 
-declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]);
+declare_clippy_lint! {
+    /// **What it does:** This lint checks for invalid usages of `ptr::null`.
+    ///
+    /// **Why is this bad?** This causes undefined behavior.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```ignore
+    /// // Bad. Undefined behavior
+    /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
+    /// ```
+    ///
+    /// // Good
+    /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
+    /// ```
+    pub INVALID_NULL_PTR_USAGE,
+    correctness,
+    "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
+}
+
+declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
 
 impl<'tcx> LateLintPass<'tcx> for Ptr {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -153,7 +175,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Binary(ref op, l, r) = expr.kind {
-            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) {
+            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
                 span_lint(
                     cx,
                     CMP_NULL,
@@ -161,6 +183,55 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     "comparing with null is better expressed by the `.is_null()` method",
                 );
             }
+        } else {
+            check_invalid_ptr_usage(cx, expr);
+        }
+    }
+}
+
+fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
+    const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
+        (&paths::SLICE_FROM_RAW_PARTS, &[0]),
+        (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
+        (&paths::PTR_COPY, &[0, 1]),
+        (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
+        (&paths::PTR_READ, &[0]),
+        (&paths::PTR_READ_UNALIGNED, &[0]),
+        (&paths::PTR_READ_VOLATILE, &[0]),
+        (&paths::PTR_REPLACE, &[0]),
+        (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
+        (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
+        (&paths::PTR_SWAP, &[0, 1]),
+        (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
+        (&paths::PTR_WRITE, &[0]),
+        (&paths::PTR_WRITE_UNALIGNED, &[0]),
+        (&paths::PTR_WRITE_VOLATILE, &[0]),
+        (&paths::PTR_WRITE_BYTES, &[0]),
+    ];
+
+    if_chain! {
+        if let ExprKind::Call(ref fun, ref args) = expr.kind;
+        if let ExprKind::Path(ref qpath) = fun.kind;
+        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+        let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
+        if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
+            .iter()
+            .find(|&&(fn_path, _)| fn_path == fun_def_path);
+        then {
+            for &arg_idx in arg_indices {
+                if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
+                    span_lint_and_sugg(
+                        cx,
+                        INVALID_NULL_PTR_USAGE,
+                        arg.span,
+                        "pointer must be non-null",
+                        "change this to",
+                        "core::ptr::NonNull::dangling().as_ptr()".to_string(),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
         }
     }
 }
@@ -345,13 +416,12 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability,
     }
 }
 
-fn is_null_path(expr: &Expr<'_>) -> bool {
-    if let ExprKind::Call(pathexp, args) = expr.kind {
-        if args.is_empty() {
-            if let ExprKind::Path(ref path) = pathexp.kind {
-                return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT);
-            }
-        }
+fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let ExprKind::Call(pathexp, []) = expr.kind {
+        expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| {
+            match_any_def_paths(cx, id, &[&paths::PTR_NULL, &paths::PTR_NULL_MUT]).is_some()
+        })
+    } else {
+        false
     }
-    false
 }
index 6d720f43851a0d56402553fc4d404610571f2947..30bee21390068eadf73ac702b08a9565beafcb36 100644 (file)
@@ -1,12 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths};
+use clippy_utils::{eq_expr_value, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -100,15 +101,14 @@ fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>
             if Self::is_option(cx, subject);
 
             if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind;
-            if match_qpath(path1, &["Some"]);
-            if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind;
+            if is_lang_ctor(cx, path1, OptionSome);
+            if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
             let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
 
             if let ExprKind::Block(block, None) = &arms[0].body.kind;
             if block.stmts.is_empty();
             if let Some(trailing_expr) = &block.expr;
-            if let ExprKind::Path(path) = &trailing_expr.kind;
-            if match_qpath(path, &[&bind.as_str()]);
+            if path_to_local_id(trailing_expr, bind_id);
 
             if let PatKind::Wild = arms[1].pat.kind;
             if Self::expression_returns_none(cx, arms[1].body);
@@ -156,15 +156,7 @@ fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool
                 false
             },
             ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
-            ExprKind::Path(ref qp) => {
-                if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) =
-                    cx.qpath_res(qp, expression.hir_id)
-                {
-                    return match_def_path(cx, def_id, &paths::OPTION_NONE);
-                }
-
-                false
-            },
+            ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
             _ => false,
         }
     }
index 1c3c125e579132a7ef28745d57e65dcb81fb1034..7169f96eaf1f3c9160b3e87a5188bfdde3040bec 100644 (file)
@@ -2,7 +2,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path};
 use clippy_utils::{higher, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
     "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
 }
 
-const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
-
 pub struct Ranges {
     msrv: Option<RustcVersion>,
 }
@@ -187,7 +185,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 check_range_zip_with_len(cx, path, args, expr.span);
             },
             ExprKind::Binary(ref op, l, r) => {
-                if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) {
+                if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) {
                     check_possible_range_contains(cx, op.node, l, r, expr);
                 }
             },
index abebd4227975ef3425c2edb2ffefc5ebc7613761..d5ee8d3468deb84aca1ad7017d66765a1d181b15 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::meets_msrv;
+use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -7,8 +7,6 @@
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for fields in struct literals where shorthands
     /// could be used.
@@ -52,7 +50,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl EarlyLintPass for RedundantFieldNames {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) {
             return;
         }
 
index 32b57698ec54dc6481c0daa7468ec3b919860d25..48107d9c037db3abdae9caa08930c1cbbb2d9321 100644 (file)
@@ -1,14 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::meets_msrv;
 use clippy_utils::source::snippet;
+use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
     ///
@@ -100,7 +98,7 @@ fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
 
 impl EarlyLintPass for RedundantStaticLifetimes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) {
             return;
         }
 
index af772cf4a145d30bb67aa50d648bc29a6c7d895d..b565c77aaecff808131af09b3c81dd23ca69197c 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{fn_def_id, in_macro, match_qpath};
+use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
@@ -84,9 +84,8 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
             if local.ty.is_none();
             if cx.tcx.hir().attrs(local.hir_id).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 let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
+            if path_to_local_id(retexpr, local_id);
             if !last_statement_borrows(cx, initexpr);
             if !in_external_macro(cx.sess(), initexpr.span);
             if !in_external_macro(cx.sess(), retexpr.span);
@@ -223,6 +222,7 @@ fn check_final_expr<'tcx>(
             },
             _ => (),
         },
+        ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
         _ => (),
     }
 }
index c9d72aabb6a3cb68d6d7dc14717e88fa103f6387..a45bb1023899dacd3f04d8883ef1b04d3f7e4497 100644 (file)
@@ -1,11 +1,10 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::in_macro;
-use if_chain::if_chain;
-use rustc_ast::{Item, ItemKind, UseTreeKind};
+use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::edition::Edition;
+use rustc_span::{edition::Edition, symbol::kw, Span, Symbol};
 
 declare_clippy_lint! {
     /// **What it does:** Checking for imports with single component use path.
 declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
 
 impl EarlyLintPass for SingleComponentPathImports {
-    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if_chain! {
-            if !in_macro(item.span);
-            if cx.sess.opts.edition >= Edition::Edition2018;
-            if !item.vis.kind.is_pub();
-            if let ItemKind::Use(use_tree) = &item.kind;
-            if let segments = &use_tree.prefix.segments;
-            if segments.len() == 1;
-            if let UseTreeKind::Simple(None, _, _) = use_tree.kind;
-            then {
+    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
+        if cx.sess.opts.edition < Edition::Edition2018 {
+            return;
+        }
+        check_mod(cx, &krate.items);
+    }
+}
+
+fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
+    // keep track of imports reused with `self` keyword,
+    // such as `self::crypto_hash` in the example below
+    // ```rust,ignore
+    // use self::crypto_hash::{Algorithm, Hasher};
+    // ```
+    let mut imports_reused_with_self = Vec::new();
+
+    // keep track of single use statements
+    // such as `crypto_hash` in the example below
+    // ```rust,ignore
+    // use crypto_hash;
+    // ```
+    let mut single_use_usages = Vec::new();
+
+    // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
+    // ```rust,ignore
+    // macro_rules! foo { () => {} };
+    // pub(crate) use foo;
+    // ```
+    let mut macros = Vec::new();
+
+    for item in items {
+        track_uses(
+            cx,
+            &item,
+            &mut imports_reused_with_self,
+            &mut single_use_usages,
+            &mut macros,
+        );
+    }
+
+    for single_use in &single_use_usages {
+        if !imports_reused_with_self.contains(&single_use.0) {
+            let can_suggest = single_use.2;
+            if can_suggest {
                 span_lint_and_sugg(
                     cx,
                     SINGLE_COMPONENT_PATH_IMPORTS,
-                    item.span,
+                    single_use.1,
                     "this import is redundant",
                     "remove it entirely",
                     String::new(),
-                    Applicability::MachineApplicable
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                span_lint_and_help(
+                    cx,
+                    SINGLE_COMPONENT_PATH_IMPORTS,
+                    single_use.1,
+                    "this import is redundant",
+                    None,
+                    "remove this import",
                 );
             }
         }
     }
 }
+
+fn track_uses(
+    cx: &EarlyContext<'_>,
+    item: &Item,
+    imports_reused_with_self: &mut Vec<Symbol>,
+    single_use_usages: &mut Vec<(Symbol, Span, bool)>,
+    macros: &mut Vec<Symbol>,
+) {
+    if in_macro(item.span) || item.vis.kind.is_pub() {
+        return;
+    }
+
+    match &item.kind {
+        ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
+            check_mod(cx, &items);
+        },
+        ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
+            macros.push(item.ident.name);
+        },
+        ItemKind::Use(use_tree) => {
+            let segments = &use_tree.prefix.segments;
+
+            let should_report =
+                |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited);
+
+            // keep track of `use some_module;` usages
+            if segments.len() == 1 {
+                if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
+                    let name = segments[0].ident.name;
+                    if should_report(&name) {
+                        single_use_usages.push((name, item.span, true));
+                    }
+                }
+                return;
+            }
+
+            if segments.is_empty() {
+                // keep track of `use {some_module, some_other_module};` usages
+                if let UseTreeKind::Nested(trees) = &use_tree.kind {
+                    for tree in trees {
+                        let segments = &tree.0.prefix.segments;
+                        if segments.len() == 1 {
+                            if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
+                                let name = segments[0].ident.name;
+                                if should_report(&name) {
+                                    single_use_usages.push((name, tree.0.span, false));
+                                }
+                            }
+                        }
+                    }
+                }
+            } else {
+                // keep track of `use self::some_module` usages
+                if segments[0].ident.name == kw::SelfLower {
+                    // simple case such as `use self::module::SomeStruct`
+                    if segments.len() > 1 {
+                        imports_reused_with_self.push(segments[1].ident.name);
+                        return;
+                    }
+
+                    // nested case such as `use self::{module1::Struct1, module2::Struct2}`
+                    if let UseTreeKind::Nested(trees) = &use_tree.kind {
+                        for tree in trees {
+                            let segments = &tree.0.prefix.segments;
+                            if !segments.is_empty() {
+                                imports_reused_with_self.push(segments[0].ident.name);
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        _ => {},
+    }
+}
index 09e00866815568a8478b4097b55b0efde6683e2d..cd2bdec1707b31d2f75647a0e0e4cd35f098d105 100644 (file)
@@ -65,8 +65,8 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool)
 
 fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
     const FUNCTIONS: [&[&str]; 8] = [
-        &paths::COPY_NONOVERLAPPING,
-        &paths::COPY,
+        &paths::PTR_COPY_NONOVERLAPPING,
+        &paths::PTR_COPY,
         &paths::WRITE_BYTES,
         &paths::PTR_SWAP_NONOVERLAPPING,
         &paths::PTR_SLICE_FROM_RAW_PARTS,
index 8cf89ae456ee8a3e6ae9da78966749df566b00b6..191781be000cf8177919236a38389dafac24c3df 100644 (file)
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -9,7 +10,7 @@
 use rustc_lint::{LateContext, LateLintPass, Lint};
 use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// **What it does:** Checks slow zero-filled vector initialization
@@ -46,8 +47,8 @@
 /// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
 /// `vec = Vec::with_capacity(0)`
 struct VecAllocation<'tcx> {
-    /// Symbol of the local variable name
-    variable_name: Symbol,
+    /// HirId of the variable
+    local_id: HirId,
 
     /// Reference to the expression which allocates the vector
     allocation_expr: &'tcx Expr<'tcx>,
@@ -72,16 +73,15 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             if let ExprKind::Assign(left, right, _) = expr.kind;
 
-            // Extract variable name
-            if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind;
-            if let Some(variable_name) = path.segments.get(0);
+            // Extract variable
+            if let Some(local_id) = path_to_local(left);
 
             // Extract len argument
-            if let Some(len_arg) = Self::is_vec_with_capacity(right);
+            if let Some(len_arg) = Self::is_vec_with_capacity(cx, right);
 
             then {
                 let vi = VecAllocation {
-                    variable_name: variable_name.ident.name,
+                    local_id,
                     allocation_expr: right,
                     len_expr: len_arg,
                 };
@@ -95,13 +95,13 @@ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
         // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
         if_chain! {
             if let StmtKind::Local(local) = stmt.kind;
-            if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind;
+            if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind;
             if let Some(init) = local.init;
-            if let Some(len_arg) = Self::is_vec_with_capacity(init);
+            if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
 
             then {
                 let vi = VecAllocation {
-                    variable_name: variable_name.name,
+                    local_id,
                     allocation_expr: init,
                     len_expr: len_arg,
                 };
@@ -115,19 +115,18 @@ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 impl SlowVectorInit {
     /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
     /// of the first argument of `with_capacity` call if it matches or `None` if it does not.
-    fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+    fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
         if_chain! {
-            if let ExprKind::Call(func, args) = expr.kind;
-            if let ExprKind::Path(ref path) = func.kind;
-            if match_qpath(path, &["Vec", "with_capacity"]);
-            if args.len() == 1;
-
+            if let ExprKind::Call(func, [arg]) = expr.kind;
+            if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind;
+            if name.ident.as_str() == "with_capacity";
+            if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type);
             then {
-                return Some(&args[0]);
+                Some(arg)
+            } else {
+                None
             }
         }
-
-        None
     }
 
     /// Search initialization for the given vector
@@ -208,11 +207,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
     fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
         if_chain! {
             if self.initialization_found;
-            if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
-            if let ExprKind::Path(ref qpath_subj) = args[0].kind;
-            if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
+            if let ExprKind::MethodCall(path, _, [self_arg, extend_arg], _) = expr.kind;
+            if path_to_local_id(self_arg, self.vec_alloc.local_id);
             if path.ident.name == sym!(extend);
-            if let Some(extend_arg) = args.get(1);
             if self.is_repeat_take(extend_arg);
 
             then {
@@ -225,11 +222,9 @@ fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
     fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
         if_chain! {
             if self.initialization_found;
-            if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
-            if let ExprKind::Path(ref qpath_subj) = args[0].kind;
-            if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
+            if let ExprKind::MethodCall(path, _, [self_arg, len_arg, fill_arg], _) = expr.kind;
+            if path_to_local_id(self_arg, self.vec_alloc.local_id);
             if path.ident.name == sym!(resize);
-            if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2));
 
             // Check that is filled with 0
             if let ExprKind::Lit(ref lit) = fill_arg.kind;
@@ -252,7 +247,7 @@ fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
 
             // Check that take is applied to `repeat(0)`
             if let Some(repeat_expr) = take_args.get(0);
-            if Self::is_repeat_zero(repeat_expr);
+            if self.is_repeat_zero(repeat_expr);
 
             // Check that len expression is equals to `with_capacity` expression
             if let Some(len_arg) = take_args.get(1);
@@ -267,21 +262,19 @@ fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
     }
 
     /// Returns `true` if given expression is `repeat(0)`
-    fn is_repeat_zero(expr: &Expr<'_>) -> bool {
+    fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool {
         if_chain! {
-            if let ExprKind::Call(fn_expr, repeat_args) = expr.kind;
-            if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind;
-            if match_qpath(qpath_repeat, &["repeat"]);
-            if let Some(repeat_arg) = repeat_args.get(0);
+            if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind;
+            if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT);
             if let ExprKind::Lit(ref lit) = repeat_arg.kind;
             if let LitKind::Int(0, _) = lit.node;
 
             then {
-                return true
+                true
+            } else {
+                false
             }
         }
-
-        false
     }
 }
 
index cb2237e531262fb95d2975e7515782749b383f51..4272935bc310e93bfd13bf2dec59c6e6d5ef6559 100644 (file)
@@ -195,7 +195,7 @@ fn attempt_to_emit_no_difference_lint(
     i: usize,
     expected_loc: IdentLocation,
 ) {
-    if let Some(binop) = binops.get(i).cloned() {
+    if let Some(binop) = binops.get(i).copied() {
         // We need to try and figure out which identifier we should
         // suggest using instead. Since there could be multiple
         // replacement candidates in a given expression, and we're
index a0492a88f912ad9229560c38fd1a0796b431702c..e2c144709f5b78b5420eca8a8828186d8fa57b2a 100644 (file)
@@ -104,30 +104,32 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
     // tracker to decide if the last group of tabs is not closed by a non-tab character
     let mut is_active = false;
 
-    let chars_array: Vec<_> = the_str.chars().collect();
+    // Note that we specifically need the char _byte_ indices here, not the positional indexes
+    // within the char array to deal with multi-byte characters properly. `char_indices` does
+    // exactly that. It provides an iterator over tuples of the form `(byte position, char)`.
+    let char_indices: Vec<_> = the_str.char_indices().collect();
 
-    if chars_array == vec!['\t'] {
+    if let [(_, '\t')] = char_indices.as_slice() {
         return vec![(0, 1)];
     }
 
-    for (index, arr) in chars_array.windows(2).enumerate() {
-        let index = u32::try_from(index).expect(line_length_way_to_long);
-        match arr {
-            ['\t', '\t'] => {
+    for entry in char_indices.windows(2) {
+        match entry {
+            [(_, '\t'), (_, '\t')] => {
                 // either string starts with double tab, then we have to set it active,
                 // otherwise is_active is true anyway
                 is_active = true;
             },
-            [_, '\t'] => {
+            [(_, _), (index_b, '\t')] => {
                 // as ['\t', '\t'] is excluded, this has to be a start of a tab group,
                 // set indices accordingly
                 is_active = true;
-                current_start = index + 1;
+                current_start = u32::try_from(*index_b).unwrap();
             },
-            ['\t', _] => {
+            [(_, '\t'), (index_b, _)] => {
                 // this now has to be an end of the group, hence we have to push a new tuple
                 is_active = false;
-                spans.push((current_start, index + 1));
+                spans.push((current_start, u32::try_from(*index_b).unwrap()));
             },
             _ => {},
         }
@@ -137,7 +139,7 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
     if is_active {
         spans.push((
             current_start,
-            u32::try_from(the_str.chars().count()).expect(line_length_way_to_long),
+            u32::try_from(char_indices.last().unwrap().0 + 1).expect(line_length_way_to_long),
         ));
     }
 
@@ -148,6 +150,13 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
 mod tests_for_get_chunks_of_tabs {
     use super::get_chunks_of_tabs;
 
+    #[test]
+    fn test_unicode_han_string() {
+        let res = get_chunks_of_tabs(" \u{4f4d}\t");
+
+        assert_eq!(res, vec![(4, 5)]);
+    }
+
     #[test]
     fn test_empty_string() {
         let res = get_chunks_of_tabs("");
index ae05a8da37bc93fab9c2e6030ead26bf225340b5..4fb297ac6c6990771cc9ef935f76b2225901ed60 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths};
+use clippy_utils::{is_diag_trait_item, match_def_path, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -95,7 +95,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
             if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
             if path.ident.name == sym!(to_string);
             if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-            if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString);
+            if is_diag_trait_item(cx, expr_def_id, sym::ToString);
             if path_to_local_id(&args[0], self_hir_id);
             then {
                 span_lint(
index 86ac916df6cba743262ab8b90298e02e8c858233..569113910c98226cb75acfda62fdd78c83c2c6a2 100644 (file)
     /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
     /// ```
     pub TRANSMUTE_PTR_TO_PTR,
-    complexity,
+    pedantic,
     "transmutes from a pointer to a pointer / a reference to a reference"
 }
 
index 0be05d3e0cf3f2a1304581516a7026e08259a1bb..888ecab10461ac936317191e6fa41927e655c504 100644 (file)
@@ -1,6 +1,6 @@
 use crate::consts::{constant_context, Constant};
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
@@ -37,18 +37,15 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         }
 
         if_chain! {
-            if let ExprKind::Call(func, args) = expr.kind;
-            if let ExprKind::Path(ref path) = func.kind;
-            if match_qpath(path, &paths::STD_MEM_TRANSMUTE);
-            if args.len() == 1;
+            if let ExprKind::Call(func, [arg]) = expr.kind;
+            if is_expr_path_def_path(cx, func, &paths::TRANSMUTE);
 
             then {
-
                 // Catching transmute over constants that resolve to `null`.
                 let mut const_eval_context = constant_context(cx, cx.typeck_results());
                 if_chain! {
-                    if let ExprKind::Path(ref _qpath) = args[0].kind;
-                    let x = const_eval_context.expr(&args[0]);
+                    if let ExprKind::Path(ref _qpath) = arg.kind;
+                    let x = const_eval_context.expr(arg);
                     if let Some(Constant::RawPtr(0)) = x;
                     then {
                         span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
@@ -58,7 +55,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 // Catching:
                 // `std::mem::transmute(0 as *const i32)`
                 if_chain! {
-                    if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind;
+                    if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
                     if let ExprKind::Lit(ref lit) = inner_expr.kind;
                     if let LitKind::Int(0, _) = lit.node;
                     then {
@@ -69,10 +66,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 // Catching:
                 // `std::mem::transmute(std::ptr::null::<i32>())`
                 if_chain! {
-                    if let ExprKind::Call(func1, args1) = args[0].kind;
-                    if let ExprKind::Path(ref path1) = func1.kind;
-                    if match_qpath(path1, &paths::STD_PTR_NULL);
-                    if args1.is_empty();
+                    if let ExprKind::Call(func1, []) = arg.kind;
+                    if is_expr_path_def_path(cx, func1, &paths::PTR_NULL);
                     then {
                         span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
                     }
index 23a1953ffaceb8715200c0ec8d2c2c400e295df6..ebb39ea4877dee0268807ccca366b5ae5e9c974d 100644 (file)
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{snippet, snippet_with_macro_callsite};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths};
+use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::ResultErr;
 use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
@@ -68,7 +69,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if let ExprKind::Call(err_fun, err_args) = try_arg.kind;
             if let Some(err_arg) = err_args.get(0);
             if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
-            if match_qpath(err_fun_path, &paths::RESULT_ERR);
+            if is_lang_ctor(cx, err_fun_path, ResultErr);
             if let Some(return_ty) = find_return_type(cx, &expr.kind);
             then {
                 let prefix;
@@ -101,10 +102,15 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 } else {
                     snippet(cx, err_arg.span, "_")
                 };
+                let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
+                    "" // already returns
+                } else {
+                    "return "
+                };
                 let suggestion = if err_ty == expr_err_ty {
-                    format!("return {}{}{}", prefix, origin_snippet, suffix)
+                    format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix)
                 } else {
-                    format!("return {}{}.into(){}", prefix, origin_snippet, suffix)
+                    format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix)
                 };
 
                 span_lint_and_sugg(
index 1425d8f3f37edf1e9a62f66ece489bfbbc8f06fe..bdeff035e5ec99ab30dd2e68a90c06a2eb4033a7 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::{match_path, paths};
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
@@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
                     _ => None,
                 });
                 then {
-                    if is_any_trait(inner) {
+                    if is_any_trait(cx, inner) {
                         // Ignore `Box<Any>` types; see issue #1884 for details.
                         return false;
                     }
@@ -84,13 +84,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
 }
 
 // Returns true if given type is `Any` trait.
-fn is_any_trait(t: &hir::Ty<'_>) -> bool {
+fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
     if_chain! {
         if let TyKind::TraitObject(traits, ..) = t.kind;
         if !traits.is_empty();
+        if let Some(trait_did) = traits[0].trait_ref.trait_def_id();
         // Only Send/Sync can be used as additional traits, so it is enough to
         // check only the first trait.
-        if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT);
+        if match_def_path(cx, trait_did, &paths::ANY_TRAIT);
         then {
             return true;
         }
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
new file mode 100644 (file)
index 0000000..48c54d7
--- /dev/null
@@ -0,0 +1,67 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use if_chain::if_chain;
+use rustc_ast::{Item, ItemKind, UseTreeKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::kw;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for imports ending in `::{self}`.
+    ///
+    /// **Why is this bad?** In most cases, this can be written much more cleanly by omitting `::{self}`.
+    ///
+    /// **Known problems:** Removing `::{self}` will cause any non-module items at the same path to also be imported.
+    /// This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt
+    /// to detect this scenario and that is why it is a restriction lint.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// use std::io::{self};
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::io;
+    /// ```
+    pub UNNECESSARY_SELF_IMPORTS,
+    restriction,
+    "imports ending in `::{self}`, which can be omitted"
+}
+
+declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]);
+
+impl EarlyLintPass for UnnecessarySelfImports {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        if_chain! {
+            if let ItemKind::Use(use_tree) = &item.kind;
+            if let UseTreeKind::Nested(nodes) = &use_tree.kind;
+            if let [(self_tree, _)] = &**nodes;
+            if let [self_seg] = &*self_tree.prefix.segments;
+            if self_seg.ident.name == kw::SelfLower;
+            if let Some(last_segment) = use_tree.prefix.segments.last();
+
+            then {
+                span_lint_and_then(
+                    cx,
+                    UNNECESSARY_SELF_IMPORTS,
+                    item.span,
+                    "import ending with `::{self}`",
+                    |diag| {
+                        diag.span_suggestion(
+                            last_segment.span().with_hi(item.span.hi()),
+                            "consider omitting `::{self}`",
+                            format!(
+                                "{}{};",
+                                last_segment.ident,
+                                if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() },
+                            ),
+                            Applicability::MaybeIncorrect,
+                        );
+                        diag.note("this will slightly change semantics; any non-module items at the same path will also be imported");
+                    },
+                );
+            }
+        }
+    }
+}
index 5bb417cb1be4b4d40a2cc04a3ca58516c442a193..f2f1410aed7421343bd812172c02f40d25167ca4 100644 (file)
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
-use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions};
+use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
+use rustc_hir::LangItem::{OptionSome, ResultOk};
 use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
@@ -85,11 +86,11 @@ fn check_fn(
         }
 
         // Get the wrapper and inner types, if can't, abort.
-        let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
+        let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
             if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) {
-                ("Option", &paths::OPTION_SOME, subst.type_at(0))
+                ("Option", OptionSome, subst.type_at(0))
             } else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) {
-                ("Result", &paths::RESULT_OK, subst.type_at(0))
+                ("Result", ResultOk, subst.type_at(0))
             } else {
                 return;
             }
@@ -103,14 +104,12 @@ fn check_fn(
             if_chain! {
                 if !in_macro(ret_expr.span);
                 // Check if a function call.
-                if let ExprKind::Call(func, args) = ret_expr.kind;
-                // Get the Path of the function call.
-                if let ExprKind::Path(ref qpath) = func.kind;
+                if let ExprKind::Call(func, [arg]) = ret_expr.kind;
                 // Check if OPTION_SOME or RESULT_OK, depending on return type.
-                if match_qpath(qpath, path);
-                if args.len() == 1;
+                if let ExprKind::Path(qpath) = &func.kind;
+                if is_lang_ctor(cx, qpath, lang_item);
                 // Make sure the function argument does not contain a return expression.
-                if !contains_return(&args[0]);
+                if !contains_return(arg);
                 then {
                     suggs.push(
                         (
@@ -118,7 +117,7 @@ fn check_fn(
                             if inner_type.is_unit() {
                                 "".to_string()
                             } else {
-                                snippet(cx, args[0].span.source_callsite(), "..").to_string()
+                                snippet(cx, arg.span.source_callsite(), "..").to_string()
                             }
                         )
                     );
index 9376a2cf66a90b1bd497498906e2e7111ccf2671..3e985fa72b8fe77583ed58223778bf464a2eb04c 100644 (file)
@@ -1,11 +1,8 @@
 #![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
 
+use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::over;
-use clippy_utils::{
-    ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path},
-    meets_msrv,
-};
+use clippy_utils::{meets_msrv, msrvs, over};
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
@@ -54,8 +51,6 @@
     "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
 }
 
-const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0);
-
 #[derive(Clone, Copy)]
 pub struct UnnestedOrPatterns {
     msrv: Option<RustcVersion>,
@@ -72,13 +67,13 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl EarlyLintPass for UnnestedOrPatterns {
     fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
-        if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &a.pat);
         }
     }
 
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
-        if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
             if let ast::ExprKind::Let(pat, _) = &e.kind {
                 lint_unnested_or_patterns(cx, pat);
             }
@@ -86,13 +81,13 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
     }
 
     fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
-        if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &p.pat);
         }
     }
 
     fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
-        if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &l.pat);
         }
     }
index 024ab03fd418ed02bcbfb3d4201898122ddad8f7..3387f35bac3d4fea903d4145fbb247dc5e8b5f49 100644 (file)
@@ -41,31 +41,41 @@ fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
         };
 
         match expr.kind {
-            hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => {
+            hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
                 if let hir::ExprKind::Call(func, args) = res.kind {
                     if matches!(
                         func.kind,
                         hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _))
                     ) {
-                        check_method_call(cx, &args[0], expr);
+                        check_map_error(cx, &args[0], expr);
                     }
                 } else {
-                    check_method_call(cx, res, expr);
+                    check_map_error(cx, res, expr);
                 }
             },
-
             hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() {
                 "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
-                    check_method_call(cx, &args[0], expr);
+                    check_map_error(cx, &args[0], expr);
                 },
                 _ => (),
             },
-
             _ => (),
         }
     }
 }
 
+fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
+    let mut call = call;
+    while let hir::ExprKind::MethodCall(ref path, _, ref args, _) = call.kind {
+        if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") {
+            call = &args[0];
+        } else {
+            break;
+        }
+    }
+    check_method_call(cx, call, expr);
+}
+
 fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
     if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind {
         let symbol = &*path.ident.as_str();
index c6a3c58a9a2a45c391f7191e93c1287e55779965..aa4d16633ff80f3937f576017e3ad2d3ec689a19 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{in_macro, meets_msrv};
+use clippy_utils::{in_macro, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -62,8 +62,6 @@ pub struct UseSelf {
     stack: Vec<StackItem>,
 }
 
-const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
-
 impl UseSelf {
     #[must_use]
     pub fn new(msrv: Option<RustcVersion>) -> Self {
@@ -236,7 +234,10 @@ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) {
     }
 
     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
-        if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+        if in_macro(hir_ty.span)
+            || in_impl(cx, hir_ty)
+            || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS)
+        {
             return;
         }
 
@@ -288,7 +289,7 @@ fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bo
             }
         }
 
-        if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+        if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) {
             return;
         }
 
index 147f823491d6dab7973d8eac8fad2283a1d4b7a8..d56855a71c159b2e1cdc351d7708e9f8371b3e46 100644 (file)
@@ -106,7 +106,7 @@ fn $config() -> $Ty {
 
 pub use self::helpers::Conf;
 define_Conf! {
-    /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports
+    /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports
     (msrv, "msrv": Option<String>, None),
     /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
     (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
index cf8039d6059b64a287483aedf4c2a2034fc37fc4..3d3d0e19d26224190bdd8e0546ae7e31599fb9bf 100644 (file)
@@ -3,7 +3,8 @@
 use clippy_utils::source::snippet;
 use clippy_utils::ty::match_type;
 use clippy_utils::{
-    is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq,
+    is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints,
+    SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
@@ -578,8 +579,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 
         if_chain! {
             if let ExprKind::Call(func, and_then_args) = expr.kind;
-            if let ExprKind::Path(ref path) = func.kind;
-            if match_qpath(path, &["span_lint_and_then"]);
+            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
             if and_then_args.len() == 5;
             if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
             let body = cx.tcx.hir().body(*body_id);
@@ -761,8 +761,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
             // Check if this is a call to utils::match_type()
             if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
-            if let ExprKind::Path(fn_qpath) = &fn_path.kind;
-            if match_qpath(fn_qpath, &["utils", "match_type"]);
+            if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
             // Extract the path to the matched type
             if let Some(segments) = path_to_matched_type(cx, ty_path);
             let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
@@ -771,6 +770,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
             if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
             then {
+                // TODO: check paths constants from external crates.
                 let cx_snippet = snippet(cx, context.span, "_");
                 let ty_snippet = snippet(cx, ty.span, "_");
 
@@ -778,9 +778,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                     cx,
                     MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
                     expr.span,
-                    "usage of `utils::match_type()` on a type diagnostic item",
+                    "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
                     "try",
-                    format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
+                    format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
                     Applicability::MaybeIncorrect,
                 );
             }
index 12a47a6b7036d9a3e1e707b05066f7200f360979..7e962472c07f5b4c994027c5303262dbb7c4470b 100644 (file)
@@ -573,7 +573,7 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool)
                         diag.multipart_suggestion(
                             "try this",
                             iter::once((comma_span.to(token_expr.span), String::new()))
-                                .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement)))
+                                .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
                                 .collect(),
                             Applicability::MachineApplicable,
                         );
index eaea3e636f9c39bcc25b5e5260077d74e589b1fd..93e10c836cc7ffbb785f963694246c26f24313fe 100644 (file)
@@ -5,6 +5,7 @@
 #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
 
 use crate::{both, over};
+use if_chain::if_chain;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, *};
 use rustc_span::symbol::Ident;
@@ -571,3 +572,34 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
         _ => false,
     }
 }
+
+/// Extract args from an assert-like macro.
+///
+/// Currently working with:
+/// - `assert_eq!` and `assert_ne!`
+/// - `debug_assert_eq!` and `debug_assert_ne!`
+///
+/// For example:
+///
+/// `debug_assert_eq!(a, b)` will return Some([a, b])
+pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> {
+    if_chain! {
+        if let ExprKind::If(_, ref block, _) = expr.kind;
+        if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind;
+        then {
+            expr = e;
+        }
+    }
+    if_chain! {
+        if let ExprKind::Block(ref block, _) = expr.kind;
+        if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind;
+        if let ExprKind::Match(ref match_expr, _) = expr.kind;
+        if let ExprKind::Tup(ref tup) = match_expr.kind;
+        if let [a, b, ..] = tup.as_slice();
+        if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind);
+        then {
+            return Some([&*a, &*b]);
+        }
+    }
+    None
+}
index 7ec8452bf4c67a5bee04738155ff324bdb08231b..c0584e1e2269410bd0387197965995ed5f4cd754 100644 (file)
@@ -151,10 +151,9 @@ pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
 
 /// Return true if the attributes contain `#[doc(hidden)]`
 pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
-    #[allow(clippy::filter_map)]
     attrs
         .iter()
         .filter(|attr| attr.has_name(sym::doc))
-        .flat_map(ast::Attribute::meta_item_list)
+        .filter_map(ast::Attribute::meta_item_list)
         .any(|l| attr::list_contains_name(&l, sym::hidden))
 }
index f695f1a61e716c19ef909b9d86b09018e845783c..07ae6e924e28b7daee63fe73710532859c40b5e5 100644 (file)
@@ -96,6 +96,16 @@ impl HirEqInterExpr<'_, '_, '_> {
     pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
         match (&left.kind, &right.kind) {
             (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
+                // This additional check ensures that the type of the locals are equivalent even if the init
+                // expression or type have some inferred parts.
+                if let Some(typeck) = self.inner.maybe_typeck_results {
+                    let l_ty = typeck.pat_ty(&l.pat);
+                    let r_ty = typeck.pat_ty(&r.pat);
+                    if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) {
+                        return false;
+                    }
+                }
+
                 // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
                 // these only get added if the init and type is equal.
                 both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
@@ -424,7 +434,7 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
                                 TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
                             )
                         })
-                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) =>
+                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
                 {
                     kind
                 },
index c847712ec2e9e854353c509acc46749df82fc740..cd85c487798d911a1e64d477937d92212c4ad663 100644 (file)
@@ -10,6 +10,7 @@
 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
+extern crate rustc_attr;
 extern crate rustc_data_structures;
 extern crate rustc_errors;
 extern crate rustc_hir;
@@ -37,6 +38,7 @@
 pub mod eager_or_lazy;
 pub mod higher;
 mod hir_utils;
+pub mod msrvs;
 pub mod numeric_literal;
 pub mod paths;
 pub mod ptr;
 use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::LangItem::{ResultErr, ResultOk};
 use rustc_hir::{
-    def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem,
-    ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath,
-    TraitItem, TraitItemKind, TraitRef, TyKind,
+    def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
+    ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment,
+    QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
 };
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::exports::Export;
@@ -80,7 +83,7 @@
 use rustc_target::abi::Integer;
 
 use crate::consts::{constant, Constant};
-use crate::ty::is_recursively_primitive_type;
+use crate::ty::{can_partially_move_ty, is_recursively_primitive_type};
 
 pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
     if let Ok(version) = RustcVersion::parse(msrv) {
@@ -222,6 +225,19 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
     }
 }
 
+/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
+/// For example, use this to check whether a function call or a pattern is `Some(..)`.
+pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
+    if let QPath::Resolved(_, path) = qpath {
+        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
+            if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
+                return cx.tcx.parent(ctor_id) == Some(item_id);
+            }
+        }
+    }
+    false
+}
+
 /// Returns `true` if this `span` was expanded by any macro.
 #[must_use]
 pub fn in_macro(span: Span) -> bool {
@@ -279,27 +295,29 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str])
     trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 }
 
-/// Checks if the method call given in `def_id` belongs to a trait or other container with a given
-/// diagnostic item
-pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
-    cx.tcx
-        .opt_associated_item(def_id)
-        .and_then(|associated_item| match associated_item.container {
-            rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
-            rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
-                rustc_ty::Adt(adt, _) => Some(adt.did),
-                rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
-                _ => None,
-            },
-        })
-        .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id))
+/// Checks if a method is defined in an impl of a diagnostic item
+pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+    if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+        if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+            return cx.tcx.is_diagnostic_item(diag_item, adt.did);
+        }
+    }
+    false
+}
+
+/// Checks if a method is in a diagnostic item trait
+pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
+        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
+    }
+    false
 }
 
 /// Checks if the method call given in `expr` belongs to the given trait.
 pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
     cx.typeck_results()
         .type_dependent_def_id(expr.hir_id)
-        .map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item))
+        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
 }
 
 /// Checks if an expression references a variable of the given name.
@@ -380,6 +398,29 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
     }
 }
 
+/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
+pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
+    if let ExprKind::Path(p) = &expr.kind {
+        cx.qpath_res(p, expr.hir_id)
+    } else {
+        Res::Err
+    }
+}
+
+/// Resolves the path to a `DefId` and checks if it matches the given path.
+pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
+    cx.qpath_res(path, hir_id)
+        .opt_def_id()
+        .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
+/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
+pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
+    expr_path_res(cx, expr)
+        .opt_def_id()
+        .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 /// `QPath::Resolved.1.res.opt_def_id()`.
@@ -408,20 +449,6 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
         .all(|(a, b)| a.ident.name.as_str() == *b)
 }
 
-/// Matches a `Path` against a slice of segment string literals, e.g.
-///
-/// # Examples
-/// ```rust,ignore
-/// match_path_ast(path, &["std", "rt", "begin_unwind"])
-/// ```
-pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
-    path.segments
-        .iter()
-        .rev()
-        .zip(segments.iter().rev())
-        .all(|(a, b)| a.ident.name.as_str() == *b)
-}
-
 /// If the expression is a path to a local, returns the canonical `HirId` of the local.
 pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
     if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
@@ -522,6 +549,73 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
     None
 }
 
+/// Checks if the top level expression can be moved into a closure as is.
+pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool {
+    match expr.kind {
+        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
+        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
+            if jump_targets.contains(&id) =>
+        {
+            true
+        },
+        ExprKind::Break(..)
+        | ExprKind::Continue(_)
+        | ExprKind::Ret(_)
+        | ExprKind::Yield(..)
+        | ExprKind::InlineAsm(_)
+        | ExprKind::LlvmInlineAsm(_) => false,
+        // Accessing a field of a local value can only be done if the type isn't
+        // partially moved.
+        ExprKind::Field(base_expr, _)
+            if matches!(
+                base_expr.kind,
+                ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
+            ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) =>
+        {
+            // TODO: check if the local has been partially moved. Assume it has for now.
+            false
+        }
+        _ => true,
+    }
+}
+
+/// Checks if the expression can be moved into a closure as is.
+pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    struct V<'cx, 'tcx> {
+        cx: &'cx LateContext<'tcx>,
+        loops: Vec<HirId>,
+        allow_closure: bool,
+    }
+    impl Visitor<'tcx> for V<'_, 'tcx> {
+        type Map = ErasedMap<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            if !self.allow_closure {
+                return;
+            }
+            if let ExprKind::Loop(b, ..) = e.kind {
+                self.loops.push(e.hir_id);
+                self.visit_block(b);
+                self.loops.pop();
+            } else {
+                self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops);
+                walk_expr(self, e);
+            }
+        }
+    }
+
+    let mut v = V {
+        cx,
+        allow_closure: true,
+        loops: Vec::new(),
+    };
+    v.visit_expr(expr);
+    v.allow_closure
+}
+
 /// Returns the method names and argument list of nested method call expressions that make up
 /// `expr`. method/span lists are sorted with the most recent call first.
 pub fn method_calls<'tcx>(
@@ -960,7 +1054,7 @@ fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut
 /// the function once on the given pattern.
 pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
     if let PatKind::Or(pats) = pat.kind {
-        pats.iter().cloned().for_each(f)
+        pats.iter().copied().for_each(f)
     } else {
         f(pat)
     }
@@ -1011,11 +1105,11 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It
 
 /// Checks if a given expression is a match expression expanded from the `?`
 /// operator or the `try` macro.
-pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    fn is_ok(arm: &Arm<'_>) -> bool {
+pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+    fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
         if_chain! {
             if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
-            if match_qpath(path, &paths::RESULT_OK[1..]);
+            if is_lang_ctor(cx, path, ResultOk);
             if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
             if path_to_local_id(arm.body, hir_id);
             then {
@@ -1025,9 +1119,9 @@ fn is_ok(arm: &Arm<'_>) -> bool {
         false
     }
 
-    fn is_err(arm: &Arm<'_>) -> bool {
+    fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
         if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
-            match_qpath(path, &paths::RESULT_ERR[1..])
+            is_lang_ctor(cx, path, ResultErr)
         } else {
             false
         }
@@ -1043,8 +1137,8 @@ fn is_err(arm: &Arm<'_>) -> bool {
             if arms.len() == 2;
             if arms[0].guard.is_none();
             if arms[1].guard.is_none();
-            if (is_ok(&arms[0]) && is_err(&arms[1])) ||
-                (is_ok(&arms[1]) && is_err(&arms[0]));
+            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
+                (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
             then {
                 return Some(expr);
             }
@@ -1131,29 +1225,47 @@ pub fn match_function_call<'tcx>(
     None
 }
 
+/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
+/// any.
+pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
+    let search_path = cx.get_def_path(did);
+    paths
+        .iter()
+        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
+}
+
+/// Checks if the given `DefId` matches the path.
 pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
-    // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
-    // accepts only that. We should probably move to Symbols in Clippy as well.
-    let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
-    cx.match_def_path(did, &syms)
+    // We should probably move to Symbols in Clippy as well rather than interning every time.
+    let path = cx.get_def_path(did);
+    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 }
 
-pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> {
-    match_function_call(cx, expr, &paths::BEGIN_PANIC)
-        .or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT))
-        .or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR))
+pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Call(func, [arg]) = expr.kind {
+        expr_path_res(cx, func)
+            .opt_def_id()
+            .map_or(false, |id| match_panic_def_id(cx, id))
+            .then(|| arg)
+    } else {
+        None
+    }
 }
 
 pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
-    match_def_path(cx, did, &paths::BEGIN_PANIC)
-        || match_def_path(cx, did, &paths::BEGIN_PANIC_FMT)
-        || match_def_path(cx, did, &paths::PANIC_ANY)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC_STR)
+    match_any_def_paths(
+        cx,
+        did,
+        &[
+            &paths::BEGIN_PANIC,
+            &paths::BEGIN_PANIC_FMT,
+            &paths::PANIC_ANY,
+            &paths::PANICKING_PANIC,
+            &paths::PANICKING_PANIC_FMT,
+            &paths::PANICKING_PANIC_STR,
+        ],
+    )
+    .is_some()
 }
 
 /// Returns the list of condition expressions and the list of blocks in a
@@ -1189,21 +1301,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
     (conds, blocks)
 }
 
-/// This function returns true if the given expression is the `else` or `if else` part of an if
-/// statement
-pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
-    let map = cx.tcx.hir();
-    let parent_id = map.get_parent_node(expr.hir_id);
-    let parent_node = map.get(parent_id);
-    matches!(
-        parent_node,
-        Node::Expr(Expr {
-            kind: ExprKind::If(_, _, _),
-            ..
-        })
-    )
-}
-
 // Finds the `#[must_use]` attribute, if any
 pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
     attrs.iter().find(|a| a.has_name(sym::must_use))
@@ -1228,6 +1325,51 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
 }
 
+/// Gets the node where an expression is either used, or it's type is unified with another branch.
+pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
+    let map = tcx.hir();
+    let mut child_id = expr.hir_id;
+    let mut iter = map.parent_iter(child_id);
+    loop {
+        match iter.next() {
+            None => break None,
+            Some((id, Node::Block(_))) => child_id = id,
+            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
+            Some((_, Node::Expr(expr))) => match expr.kind {
+                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
+                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
+                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
+                _ => break Some(Node::Expr(expr)),
+            },
+            Some((_, node)) => break Some(node),
+        }
+    }
+}
+
+/// Checks if the result of an expression is used, or it's type is unified with another branch.
+pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+    !matches!(
+        get_expr_use_or_unification_node(tcx, expr),
+        None | Some(Node::Stmt(Stmt {
+            kind: StmtKind::Expr(_)
+                | StmtKind::Semi(_)
+                | StmtKind::Local(Local {
+                    pat: Pat {
+                        kind: PatKind::Wild,
+                        ..
+                    },
+                    ..
+                }),
+            ..
+        }))
+    )
+}
+
+/// Checks if the expression is the final expression returned from a block.
+pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+    matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
+}
+
 pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
     cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
         if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
@@ -1397,28 +1539,43 @@ fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
     peel(pat, 0)
 }
 
+/// Peels of expressions while the given closure returns `Some`.
+pub fn peel_hir_expr_while<'tcx>(
+    mut expr: &'tcx Expr<'tcx>,
+    mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
+) -> &'tcx Expr<'tcx> {
+    while let Some(e) = f(expr) {
+        expr = e;
+    }
+    expr
+}
+
 /// Peels off up to the given number of references on the expression. Returns the underlying
 /// expression and the number of references removed.
 pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
-    fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
-        match expr.kind {
-            ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
-            _ => (expr, count),
-        }
-    }
-    f(expr, 0, count)
+    let mut remaining = count;
+    let e = peel_hir_expr_while(expr, |e| match e.kind {
+        ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => {
+            remaining -= 1;
+            Some(e)
+        },
+        _ => None,
+    });
+    (e, count - remaining)
 }
 
 /// Peels off all references on the expression. Returns the underlying expression and the number of
 /// references removed.
 pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
-    fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
-        match expr.kind {
-            ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1),
-            _ => (expr, count),
-        }
-    }
-    f(expr, 0)
+    let mut count = 0;
+    let e = peel_hir_expr_while(expr, |e| match e.kind {
+        ExprKind::AddrOf(BorrowKind::Ref, _, e) => {
+            count += 1;
+            Some(e)
+        },
+        _ => None,
+    });
+    (e, count)
 }
 
 #[macro_export]
@@ -1450,27 +1607,3 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
         }
     }
 }
-
-/// Check if the resolution of a given path is an `Ok` variant of `Result`.
-pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
-    if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
-            if let Some(variant_id) = cx.tcx.parent(id) {
-                return variant_id == ok_id;
-            }
-        }
-    }
-    false
-}
-
-/// Check if the resolution of a given path is a `Some` variant of `Option`.
-pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
-    if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
-            if let Some(variant_id) = cx.tcx.parent(id) {
-                return variant_id == some_id;
-            }
-        }
-    }
-    false
-}
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
new file mode 100644 (file)
index 0000000..00df04c
--- /dev/null
@@ -0,0 +1,29 @@
+use rustc_semver::RustcVersion;
+
+macro_rules! msrv_aliases {
+    ($($major:literal,$minor:literal,$patch:literal {
+        $($name:ident),* $(,)?
+    })*) => {
+        $($(
+        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
+        )*)*
+    };
+}
+
+// names may refer to stabilized feature flags or library items
+msrv_aliases! {
+    1,53,0 { OR_PATTERNS }
+    1,50,0 { BOOL_THEN }
+    1,46,0 { CONST_IF_MATCH }
+    1,45,0 { STR_STRIP_PREFIX }
+    1,42,0 { MATCHES_MACRO }
+    1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
+    1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
+    1,38,0 { POINTER_CAST }
+    1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
+    1,36,0 { ITERATOR_COPIED }
+    1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
+    1,34,0 { TRY_FROM }
+    1,30,0 { ITERATOR_FIND_MAP }
+    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
+}
index 3b4c4070c0ed38999580d460db3e22cb1b3aba69..5e6733a300f2ca49cae21a62b5deeddc472685a2 100644 (file)
@@ -4,7 +4,7 @@
 //! Whenever possible, please consider diagnostic items over hardcoded paths.
 //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 
-pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
+pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
 pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
 pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
 pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
+pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
+pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
 pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
 pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
-pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
-pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"];
 pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
 pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
 pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
 pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
+pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
+pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
 pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
+pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
+pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
 #[cfg(feature = "internal-lints")]
 pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
@@ -60,8 +64,9 @@
 pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
 pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
 pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
-pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
-pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
+pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
+pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
+pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 #[cfg(feature = "internal-lints")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
 pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
 pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
+pub const PTR_COPY: [&str; 4] = ["core", "intrinsics", "", "copy"];
+pub const PTR_COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
 pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
 pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
 pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
 pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
 pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
+pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
+pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
+pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
+pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
+pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
+pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
+pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
+pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
+pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
 pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
 pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
-pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
 pub const RESULT: [&str; 3] = ["core", "result", "Result"];
 pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
 pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
 pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
-pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
+pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
-pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
-pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
 pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
 pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
index b52cbf31e35aad71c4f8a31c704633e6b5dab059..a08dcf19e5b51481398f933bd35f1bf4401f2ae4 100644 (file)
@@ -1,3 +1,8 @@
+// This code used to be a part of `rustc` but moved to Clippy as a result of
+// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
+// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
+// differ from the time of `rustc` even if the name stays the same.
+
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{
@@ -6,6 +11,7 @@
 };
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_semver::RustcVersion;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi::RustIntrinsic;
@@ -13,7 +19,7 @@
 
 type McfResult = Result<(), (Span, Cow<'static, str>)>;
 
-pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
+pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
     let def_id = body.source.def_id();
     let mut current = def_id;
     loop {
@@ -70,7 +76,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
     )?;
 
     for bb in body.basic_blocks() {
-        check_terminator(tcx, body, bb.terminator())?;
+        check_terminator(tcx, body, bb.terminator(), msrv)?;
         for stmt in &bb.statements {
             check_statement(tcx, body, def_id, stmt)?;
         }
@@ -268,7 +274,12 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t
     Ok(())
 }
 
-fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
+fn check_terminator(
+    tcx: TyCtxt<'tcx>,
+    body: &'a Body<'tcx>,
+    terminator: &Terminator<'tcx>,
+    msrv: Option<&RustcVersion>,
+) -> McfResult {
     let span = terminator.source_info.span;
     match &terminator.kind {
         TerminatorKind::FalseEdge { .. }
@@ -305,7 +316,7 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
         } => {
             let fn_ty = func.ty(body, tcx);
             if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
-                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
+                if !is_const_fn(tcx, fn_def_id, msrv) {
                     return Err((
                         span,
                         format!(
@@ -350,3 +361,22 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
         TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
     }
 }
+
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
+    rustc_mir::const_eval::is_const_fn(tcx, def_id)
+        && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
+            if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
+                // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
+                // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
+                // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
+                crate::meets_msrv(
+                    msrv,
+                    &RustcVersion::parse(&since.as_str())
+                        .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
+                )
+            } else {
+                // Unstable const fn with the feature enabled.
+                msrv.is_none()
+            }
+        })
+}
index 2d794d48dc5ff63dba4f0a5ba7901f7e5f7b06de..53180d1f9f54f699014b50c35f0d1e1eff33b9cc 100644 (file)
@@ -66,6 +66,15 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
     snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 }
 
+/// Gets a snippet of the indentation of the line of a span
+pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+    snippet_opt(cx, line_span(cx, span)).map(|mut s| {
+        let len = s.len() - s.trim_start().len();
+        s.truncate(len);
+        s
+    })
+}
+
 // If the snippet is empty, it's an attribute that was inserted during macro
 // expansion and we want to ignore those, because they could come from external
 // sources that the user has no control over.
index 807cfbc4c7f1f420ed25025e62f560bd53a9f732..64a80f2554fa4c0e413cd2c51be74fe3f20f6cb6 100644 (file)
@@ -13,7 +13,7 @@
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
 use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
 use rustc_span::sym;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::DUMMY_SP;
 use rustc_trait_selection::traits::query::normalize::AtExt;
 
@@ -52,6 +52,25 @@ pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool {
     })
 }
 
+/// Resolves `<T as Iterator>::Item` for `T`
+/// Do not invoke without first verifying that the type implements `Iterator`
+pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+    cx.tcx
+        .get_diagnostic_item(sym::Iterator)
+        .and_then(|iter_did| {
+            cx.tcx.associated_items(iter_did).find_by_name_and_kind(
+                cx.tcx,
+                Ident::from_str("Item"),
+                ty::AssocKind::Type,
+                iter_did,
+            )
+        })
+        .map(|assoc| {
+            let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
+            cx.tcx.normalize_erasing_regions(cx.param_env, proj)
+        })
+}
+
 /// Returns true if ty has `iter` or `iter_mut` methods
 pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
     // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
index 99b86953d51a6287ceccb8bcb9207d881b0f368e..d6cc6d0c2c76394842dd80d085e811bf96b78725 100644 (file)
@@ -390,17 +390,23 @@ pass.
 
 ## Specifying the lint's minimum supported Rust version (MSRV)
 
-Projects supporting older versions of Rust would need to disable a lint if it
-targets features present in later versions. Support for this can be added by
-specifying an MSRV in your lint like so,
+Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
+using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
+ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
+required, just use the one with a lower MSRV.
+
+First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
+accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
 
 ```rust
-const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
+msrv_aliases! {
+    ..
+    1,45,0 { STR_STRIP_PREFIX }
+}
 ```
 
-The project's MSRV will also have to be an attribute in the lint so you'll have
-to add a struct and constructor for your lint. The project's MSRV needs to be
-passed when the lint is registered in `lib.rs`
+In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
+constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
 
 ```rust
 pub struct ManualStrip {
@@ -415,11 +421,11 @@ impl ManualStrip {
 }
 ```
 
-The project's MSRV can then be matched against the lint's `msrv` in the LintPass
+The project's MSRV can then be matched against the feature MSRV in the LintPass
 using the `meets_msrv` utility function.
 
 ``` rust
-if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
+if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
     return;
 }
 ```
@@ -625,7 +631,7 @@ in the following steps:
 Here are some pointers to things you are likely going to need for every lint:
 
 * [Clippy utils][utils] - Various helper functions. Maybe the function you need
-  is already in here (`implements_trait`, `match_path`, `snippet`, etc)
+  is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
 * [Clippy diagnostics][diagnostics]
 * [The `if_chain` macro][if_chain]
 * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
index 2041aed2b97c7f4ee88e573e73e0766586f46389..f6a75595c986b1294e21bf8236eafd90cd97237e 100644 (file)
@@ -5,7 +5,7 @@
 // When a new lint is introduced, we can search the results for new warnings and check for false
 // positives.
 
-#![allow(clippy::filter_map, clippy::collapsible_else_if)]
+#![allow(clippy::collapsible_else_if)]
 
 use std::ffi::OsStr;
 use std::process::Command;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 
+#[cfg(not(windows))]
 const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
+#[cfg(not(windows))]
 const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy";
 
+#[cfg(windows)]
+const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe";
+#[cfg(windows)]
+const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe";
+
 const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
 const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
 
index 7e4d12b8632be48d274287e40f5e14a853687759..cd398451783d6bc53b03596e5b7a55956c9a19d3 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-04-08"
+channel = "nightly-2021-04-22"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index e588c23345e2fdd8ad010e05a0db259589f7bade..7764cc8da786115eb69d7de82ea2216decd6a1d1 100644 (file)
@@ -2,58 +2,18 @@
 #![deny(clippy::internal)]
 #![feature(rustc_private)]
 
+extern crate clippy_utils;
 extern crate rustc_ast;
 extern crate rustc_errors;
 extern crate rustc_lint;
 extern crate rustc_session;
 extern crate rustc_span;
 
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 use rustc_ast::ast::Expr;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-#[allow(unused_variables)]
-pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
-where
-    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
-{
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_help<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    option_span: Option<Span>,
-    help: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_note<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    note_span: Option<Span>,
-    note: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_sugg<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    sp: Span,
-    msg: &str,
-    help: &str,
-    sugg: String,
-    applicability: Applicability,
-) {
-}
 
 declare_tool_lint! {
     pub clippy::TEST_LINT,
index d5dd3bb562b429d9265f3ba1d5c80a4c977a4dc2..bdd296db8320bac83ebab1971a9c0363c194dac5 100644 (file)
@@ -2,58 +2,18 @@
 #![deny(clippy::internal)]
 #![feature(rustc_private)]
 
+extern crate clippy_utils;
 extern crate rustc_ast;
 extern crate rustc_errors;
 extern crate rustc_lint;
 extern crate rustc_session;
 extern crate rustc_span;
 
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 use rustc_ast::ast::Expr;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-#[allow(unused_variables)]
-pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
-where
-    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
-{
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_help<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    option_span: Option<Span>,
-    help: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_note<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    note_span: Option<Span>,
-    note: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_sugg<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    sp: Span,
-    msg: &str,
-    help: &str,
-    sugg: String,
-    applicability: Applicability,
-) {
-}
 
 declare_tool_lint! {
     pub clippy::TEST_LINT,
index 874d4a9f255c27dcaa542325be407d65d25cfabd..0632b038577375f3a1c2ada471554bd5cb7303b8 100644 (file)
@@ -1,5 +1,5 @@
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:75:9
+  --> $DIR/collapsible_span_lint_calls.rs:35:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
@@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:78:9
+  --> $DIR/collapsible_span_lint_calls.rs:38:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_help(expr.span, help_msg);
@@ -22,7 +22,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:81:9
+  --> $DIR/collapsible_span_lint_calls.rs:41:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.help(help_msg);
@@ -30,7 +30,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:84:9
+  --> $DIR/collapsible_span_lint_calls.rs:44:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_note(expr.span, note_msg);
@@ -38,7 +38,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:87:9
+  --> $DIR/collapsible_span_lint_calls.rs:47:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.note(note_msg);
index fe950b0aa7c7004c729c33cc7b6dab3392d1260a..063f0c6460c5ee92053ba824d69e06b67d62bc23 100644 (file)
@@ -1,29 +1,18 @@
 #![deny(clippy::internal)]
 #![feature(rustc_private)]
 
+extern crate clippy_utils;
 extern crate rustc_hir;
 extern crate rustc_lint;
 extern crate rustc_middle;
+
 #[macro_use]
 extern crate rustc_session;
+use clippy_utils::{paths, ty::match_type};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 
-mod paths {
-    pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
-}
-
-mod utils {
-    use super::*;
-
-    pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool {
-        false
-    }
-}
-
-use utils::match_type;
-
 declare_lint! {
     pub TEST_LINT,
     Warn,
@@ -38,12 +27,12 @@ impl<'tcx> LateLintPass<'tcx> for Pass {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) {
         let ty = cx.typeck_results().expr_ty(expr);
 
-        let _ = match_type(cx, ty, &paths::VEC);
+        let _ = match_type(cx, ty, &paths::VEC); // FIXME: Doesn't lint external paths
         let _ = match_type(cx, ty, &OPTION);
         let _ = match_type(cx, ty, &["core", "result", "Result"]);
 
         let rc_path = &["alloc", "rc", "Rc"];
-        let _ = utils::match_type(cx, ty, rc_path);
+        let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
     }
 }
 
index 82465dbaf6ecc9d9e61b5f99e25f62948da7ebc7..714729605658cca3aa0110c9ee311b7beb637861 100644 (file)
@@ -1,8 +1,8 @@
-error: usage of `utils::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:41:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:31:17
    |
-LL |         let _ = match_type(cx, ty, &paths::VEC);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)`
+LL |         let _ = match_type(cx, ty, &OPTION);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::option_type)`
    |
 note: the lint level is defined here
   --> $DIR/match_type_on_diag_item.rs:1:9
@@ -11,23 +11,17 @@ LL | #![deny(clippy::internal)]
    |         ^^^^^^^^^^^^^^^^
    = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
 
-error: usage of `utils::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:42:17
-   |
-LL |         let _ = match_type(cx, ty, &OPTION);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)`
-
-error: usage of `utils::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:43:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:32:17
    |
 LL |         let _ = match_type(cx, ty, &["core", "result", "Result"]);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::result_type)`
 
-error: usage of `utils::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:46:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:35:17
    |
-LL |         let _ = utils::match_type(cx, ty, rc_path);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)`
+LL |         let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
index 658cae397e1494b3f5ab0595a21d501229e5d9e7..4a62f6f2909f3b01af9663bbc3cb96c47a901821 100644 (file)
@@ -1,5 +1,7 @@
-#![feature(asm)]
 // only-x86_64
+// ignore-aarch64
+
+#![feature(asm)]
 
 #[warn(clippy::inline_asm_x86_intel_syntax)]
 mod warn_intel {
@@ -23,6 +25,7 @@ pub(super) unsafe fn use_asm() {
     }
 }
 
+#[cfg(target_arch = "x86_64")]
 fn main() {
     unsafe {
         warn_att::use_asm();
index 27b51166eacb8321c52938fed00fddbbe14602a1..e3abbe086586ea18c4af09fa93efe389e2cb1e75 100644 (file)
@@ -1,5 +1,5 @@
 error: Intel x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:7:9
+  --> $DIR/asm_syntax.rs:9:9
    |
 LL |         asm!("");
    |         ^^^^^^^^^
@@ -8,7 +8,7 @@ LL |         asm!("");
    = help: use AT&T x86 assembly syntax
 
 error: Intel x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:8:9
+  --> $DIR/asm_syntax.rs:10:9
    |
 LL |         asm!("", options());
    |         ^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |         asm!("", options());
    = help: use AT&T x86 assembly syntax
 
 error: Intel x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:9:9
+  --> $DIR/asm_syntax.rs:11:9
    |
 LL |         asm!("", options(nostack));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |         asm!("", options(nostack));
    = help: use AT&T x86 assembly syntax
 
 error: AT&T x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:21:9
+  --> $DIR/asm_syntax.rs:23:9
    |
 LL |         asm!("", options(att_syntax));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -33,7 +33,7 @@ LL |         asm!("", options(att_syntax));
    = help: use Intel x86 assembly syntax
 
 error: AT&T x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:22:9
+  --> $DIR/asm_syntax.rs:24:9
    |
 LL |         asm!("", options(nostack, att_syntax));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
new file mode 100644 (file)
index 0000000..2de402f
--- /dev/null
@@ -0,0 +1,59 @@
+#![warn(clippy::bool_assert_comparison)]
+
+macro_rules! a {
+    () => {
+        true
+    };
+}
+macro_rules! b {
+    () => {
+        true
+    };
+}
+
+fn main() {
+    assert_eq!("a".len(), 1);
+    assert_eq!("a".is_empty(), false);
+    assert_eq!("".is_empty(), true);
+    assert_eq!(true, "".is_empty());
+    assert_eq!(a!(), b!());
+    assert_eq!(a!(), "".is_empty());
+    assert_eq!("".is_empty(), b!());
+
+    assert_ne!("a".len(), 1);
+    assert_ne!("a".is_empty(), false);
+    assert_ne!("".is_empty(), true);
+    assert_ne!(true, "".is_empty());
+    assert_ne!(a!(), b!());
+    assert_ne!(a!(), "".is_empty());
+    assert_ne!("".is_empty(), b!());
+
+    debug_assert_eq!("a".len(), 1);
+    debug_assert_eq!("a".is_empty(), false);
+    debug_assert_eq!("".is_empty(), true);
+    debug_assert_eq!(true, "".is_empty());
+    debug_assert_eq!(a!(), b!());
+    debug_assert_eq!(a!(), "".is_empty());
+    debug_assert_eq!("".is_empty(), b!());
+
+    debug_assert_ne!("a".len(), 1);
+    debug_assert_ne!("a".is_empty(), false);
+    debug_assert_ne!("".is_empty(), true);
+    debug_assert_ne!(true, "".is_empty());
+    debug_assert_ne!(a!(), b!());
+    debug_assert_ne!(a!(), "".is_empty());
+    debug_assert_ne!("".is_empty(), b!());
+
+    // assert with error messages
+    assert_eq!("a".len(), 1, "tadam {}", 1);
+    assert_eq!("a".len(), 1, "tadam {}", true);
+    assert_eq!("a".is_empty(), false, "tadam {}", 1);
+    assert_eq!("a".is_empty(), false, "tadam {}", true);
+    assert_eq!(false, "a".is_empty(), "tadam {}", true);
+
+    debug_assert_eq!("a".len(), 1, "tadam {}", 1);
+    debug_assert_eq!("a".len(), 1, "tadam {}", true);
+    debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+    debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+    debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+}
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
new file mode 100644 (file)
index 0000000..f57acf5
--- /dev/null
@@ -0,0 +1,112 @@
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:16:5
+   |
+LL |     assert_eq!("a".is_empty(), false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+   |
+   = note: `-D clippy::bool-assert-comparison` implied by `-D warnings`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:17:5
+   |
+LL |     assert_eq!("".is_empty(), true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:18:5
+   |
+LL |     assert_eq!(true, "".is_empty());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:24:5
+   |
+LL |     assert_ne!("a".is_empty(), false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:25:5
+   |
+LL |     assert_ne!("".is_empty(), true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:26:5
+   |
+LL |     assert_ne!(true, "".is_empty());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:32:5
+   |
+LL |     debug_assert_eq!("a".is_empty(), false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:33:5
+   |
+LL |     debug_assert_eq!("".is_empty(), true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:34:5
+   |
+LL |     debug_assert_eq!(true, "".is_empty());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:40:5
+   |
+LL |     debug_assert_ne!("a".is_empty(), false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:41:5
+   |
+LL |     debug_assert_ne!("".is_empty(), true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:42:5
+   |
+LL |     debug_assert_ne!(true, "".is_empty());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:50:5
+   |
+LL |     assert_eq!("a".is_empty(), false, "tadam {}", 1);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:51:5
+   |
+LL |     assert_eq!("a".is_empty(), false, "tadam {}", true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:52:5
+   |
+LL |     assert_eq!(false, "a".is_empty(), "tadam {}", true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:56:5
+   |
+LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:57:5
+   |
+LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:58:5
+   |
+LL |     debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: aborting due to 18 previous errors
+
index c389c243d447e45adaf8e20707d8d1013ee6736e..ce2040bdeb82d934e2bbe8c7fbd7bbb63d959835 100644 (file)
@@ -206,4 +206,18 @@ fn fp_test() {
     }
 }
 
+fn fp_if_let_issue7054() {
+    // This shouldn't trigger the lint
+    let string;
+    let _x = if let true = true {
+        ""
+    } else if true {
+        string = "x".to_owned();
+        &string
+    } else {
+        string = "y".to_owned();
+        &string
+    };
+}
+
 fn main() {}
index e65bcfd78737f329e673fbce03cc79343ce40e03..51a46481399b4e6a5b6af13ec2384fbe46eb263e 100644 (file)
@@ -100,4 +100,15 @@ fn check_if_same_than_else_mask() {
     }
 }
 
+#[allow(clippy::vec_init_then_push)]
+fn pf_local_with_inferred_type_issue7053() {
+    if true {
+        let mut v = Vec::new();
+        v.push(0);
+    } else {
+        let mut v = Vec::new();
+        v.push("");
+    };
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
new file mode 100644 (file)
index 0000000..4eb999e
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+#![warn(clippy::cloned_instead_of_copied)]
+
+fn main() {
+    // yay
+    let _ = [1].iter().copied();
+    let _ = vec!["hi"].iter().copied();
+    let _ = Some(&1).copied();
+    let _ = Box::new([1].iter()).copied();
+    let _ = Box::new(Some(&1)).copied();
+
+    // nay
+    let _ = [String::new()].iter().cloned();
+    let _ = Some(&String::new()).cloned();
+}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
new file mode 100644 (file)
index 0000000..894496c
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+#![warn(clippy::cloned_instead_of_copied)]
+
+fn main() {
+    // yay
+    let _ = [1].iter().cloned();
+    let _ = vec!["hi"].iter().cloned();
+    let _ = Some(&1).cloned();
+    let _ = Box::new([1].iter()).cloned();
+    let _ = Box::new(Some(&1)).cloned();
+
+    // nay
+    let _ = [String::new()].iter().cloned();
+    let _ = Some(&String::new()).cloned();
+}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
new file mode 100644 (file)
index 0000000..e0707d3
--- /dev/null
@@ -0,0 +1,34 @@
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:6:24
+   |
+LL |     let _ = [1].iter().cloned();
+   |                        ^^^^^^ help: try: `copied`
+   |
+   = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
+
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:7:31
+   |
+LL |     let _ = vec!["hi"].iter().cloned();
+   |                               ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:8:22
+   |
+LL |     let _ = Some(&1).cloned();
+   |                      ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:9:34
+   |
+LL |     let _ = Box::new([1].iter()).cloned();
+   |                                  ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:10:32
+   |
+LL |     let _ = Box::new(Some(&1)).cloned();
+   |                                ^^^^^^ help: try: `copied`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5835.rs b/src/tools/clippy/tests/ui/crashes/ice-5835.rs
new file mode 100644 (file)
index 0000000..5e99cb4
--- /dev/null
@@ -0,0 +1,9 @@
+#[rustfmt::skip]
+pub struct Foo {
+    /// 位    
+    ///   ^ Do not remove this tab character.
+    ///   It was required to trigger the ICE.
+    pub bar: u8,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5835.stderr b/src/tools/clippy/tests/ui/crashes/ice-5835.stderr
new file mode 100644 (file)
index 0000000..c972bcb
--- /dev/null
@@ -0,0 +1,10 @@
+error: using tabs in doc comments is not recommended
+  --> $DIR/ice-5835.rs:3:10
+   |
+LL |     /// 位    
+   |           ^^^^ help: consider using four spaces per tab
+   |
+   = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
+
+error: aborting due to previous error
+
index 9a7cf4b0919f63bf7ca195bb240852dafd8a6880..8498c0407808d1b60950894cb8495fca0a30f787 100644 (file)
@@ -16,8 +16,8 @@ LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
    = help: unsized fn params are gated as an unstable feature
 help: function arguments must have a statically known size, borrowed types always have a known size
    |
-LL | fn bug<T>() -> impl Iterator<Item = [(); { |&x: [u8]| x }]> {
-   |                                             ^
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: &[u8]| x }]> {
+   |                                                ^
 
 error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
   --> $DIR/ice-6251.rs:4:54
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7126.rs b/src/tools/clippy/tests/ui/crashes/ice-7126.rs
new file mode 100644 (file)
index 0000000..ca563ba
--- /dev/null
@@ -0,0 +1,14 @@
+// This test requires a feature gated const fn and will stop working in the future.
+
+#![feature(const_btree_new)]
+
+use std::collections::BTreeMap;
+
+struct Foo(BTreeMap<i32, i32>);
+impl Foo {
+    fn new() -> Self {
+        Self(BTreeMap::new())
+    }
+}
+
+fn main() {}
index 995787c5336688d9283fc8e6ca5ff806faf54133..7ff5a16ed86ca078e2fec2362f93a7a83213b2f4 100644 (file)
@@ -1,11 +1,11 @@
 // ignore-macos
 // ignore-windows
 
-#![feature(main)]
+#![feature(rustc_attrs)]
 
 #[warn(clippy::main_recursion)]
 #[allow(unconditional_recursion)]
-#[main]
+#[rustc_main]
 fn a() {
     println!("Hello, World!");
     a();
index fc444c0bea7203a2c90084a68b3025f52ac7f771..dbf0b03af769ccc5ec9b36c18a27bf5e174573cb 100644 (file)
@@ -11,5 +11,6 @@
 #[warn(clippy::panic_params)]
 #[warn(clippy::unknown_clippy_lints)]
 #[warn(clippy::find_map)]
+#[warn(clippy::filter_map)]
 
 fn main() {}
index 64efcd18f8891a344ada11c0be14d67a00533f81..3e125c1dab568a7f137b12a7521cb41cc27b9ce0 100644 (file)
@@ -24,23 +24,23 @@ error: lint `clippy::unused_collect` has been removed: `collect` has been marked
 LL | #[warn(clippy::unused_collect)]
    |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::invalid_ref` has been removed: superseded by rustc lint `invalid_value`
+error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
   --> $DIR/deprecated.rs:5:8
    |
 LL | #[warn(clippy::invalid_ref)]
-   |        ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
-error: lint `clippy::into_iter_on_array` has been removed: this lint has been uplifted to rustc and is now called `array_into_iter`
+error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
   --> $DIR/deprecated.rs:6:8
    |
 LL | #[warn(clippy::into_iter_on_array)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
-error: lint `clippy::unused_label` has been removed: this lint has been uplifted to rustc and is now called `unused_labels`
+error: lint `clippy::unused_label` has been renamed to `unused_labels`
   --> $DIR/deprecated.rs:7:8
    |
 LL | #[warn(clippy::unused_label)]
-   |        ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
   --> $DIR/deprecated.rs:8:8
@@ -48,29 +48,29 @@ error: lint `clippy::regex_macro` has been removed: the regex! macro has been re
 LL | #[warn(clippy::regex_macro)]
    |        ^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::drop_bounds` has been removed: this lint has been uplifted to rustc and is now called `drop_bounds`
+error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
   --> $DIR/deprecated.rs:9:8
    |
 LL | #[warn(clippy::drop_bounds)]
-   |        ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
-error: lint `clippy::temporary_cstring_as_ptr` has been removed: this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`
+error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
   --> $DIR/deprecated.rs:10:8
    |
 LL | #[warn(clippy::temporary_cstring_as_ptr)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
-error: lint `clippy::panic_params` has been removed: this lint has been uplifted to rustc and is now called `panic_fmt`
+error: lint `clippy::panic_params` has been renamed to `non_fmt_panic`
   --> $DIR/deprecated.rs:11:8
    |
 LL | #[warn(clippy::panic_params)]
-   |        ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic`
 
-error: lint `clippy::unknown_clippy_lints` has been removed: this lint has been integrated into the `unknown_lints` rustc lint
+error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
   --> $DIR/deprecated.rs:12:8
    |
 LL | #[warn(clippy::unknown_clippy_lints)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
   --> $DIR/deprecated.rs:13:8
@@ -78,11 +78,17 @@ error: lint `clippy::find_map` has been removed: this lint has been replaced by
 LL | #[warn(clippy::find_map)]
    |        ^^^^^^^^^^^^^^^^
 
+error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
+  --> $DIR/deprecated.rs:14:8
+   |
+LL | #[warn(clippy::filter_map)]
+   |        ^^^^^^^^^^^^^^^^^^
+
 error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
   --> $DIR/deprecated.rs:1:8
    |
 LL | #[warn(clippy::unstable_as_slice)]
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/entry.fixed b/src/tools/clippy/tests/ui/entry.fixed
new file mode 100644 (file)
index 0000000..cfad309
--- /dev/null
@@ -0,0 +1,155 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+#![feature(asm)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+    ($e:expr) => {{ $e }};
+}
+
+macro_rules! insert {
+    ($map:expr, $key:expr, $val:expr) => {
+        $map.insert($key, $val)
+    };
+}
+
+fn foo() {}
+
+fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
+    // or_insert(v)
+    m.entry(k).or_insert(v);
+
+    // semicolon on insert, use or_insert_with(..)
+    m.entry(k).or_insert_with(|| {
+        if true {
+            v
+        } else {
+            v2
+        }
+    });
+
+    // semicolon on if, use or_insert_with(..)
+    m.entry(k).or_insert_with(|| {
+        if true {
+            v
+        } else {
+            v2
+        }
+    });
+
+    // early return, use if let
+    if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+        if true {
+            e.insert(v);
+        } else {
+            e.insert(v2);
+            return;
+        }
+    }
+
+    // use or_insert_with(..)
+    m.entry(k).or_insert_with(|| {
+        foo();
+        v
+    });
+
+    // semicolon on insert and match, use or_insert_with(..)
+    m.entry(k).or_insert_with(|| {
+        match 0 {
+            1 if true => {
+                v
+            },
+            _ => {
+                v2
+            },
+        }
+    });
+
+    // one branch doesn't insert, use if let
+    if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+        match 0 {
+            0 => foo(),
+            _ => {
+                e.insert(v2);
+            },
+        };
+    }
+
+    // use or_insert_with
+    m.entry(k).or_insert_with(|| {
+        foo();
+        match 0 {
+            0 if false => {
+                v
+            },
+            1 => {
+                foo();
+                v
+            },
+            2 | 3 => {
+                for _ in 0..2 {
+                    foo();
+                }
+                if true {
+                    v
+                } else {
+                    v2
+                }
+            },
+            _ => {
+                v2
+            },
+        }
+    });
+
+    // ok, insert in loop
+    if !m.contains_key(&k) {
+        for _ in 0..2 {
+            m.insert(k, v);
+        }
+    }
+
+    // macro_expansion test, use or_insert(..)
+    m.entry(m!(k)).or_insert_with(|| m!(v));
+
+    // ok, map used before insertion
+    if !m.contains_key(&k) {
+        let _ = m.len();
+        m.insert(k, v);
+    }
+
+    // ok, inline asm
+    if !m.contains_key(&k) {
+        unsafe { asm!("nop") }
+        m.insert(k, v);
+    }
+
+    // ok, different keys.
+    if !m.contains_key(&k) {
+        m.insert(k2, v);
+    }
+
+    // ok, different maps
+    if !m.contains_key(&k) {
+        m2.insert(k, v);
+    }
+
+    // ok, insert in macro
+    if !m.contains_key(&k) {
+        insert!(m, k, v);
+    }
+}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
+    // insert then do something, use if let
+    if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+        e.insert(v);
+        foo();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry.rs b/src/tools/clippy/tests/ui/entry.rs
new file mode 100644 (file)
index 0000000..fa9280b
--- /dev/null
@@ -0,0 +1,159 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+#![feature(asm)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+    ($e:expr) => {{ $e }};
+}
+
+macro_rules! insert {
+    ($map:expr, $key:expr, $val:expr) => {
+        $map.insert($key, $val)
+    };
+}
+
+fn foo() {}
+
+fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
+    // or_insert(v)
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+    }
+
+    // semicolon on insert, use or_insert_with(..)
+    if !m.contains_key(&k) {
+        if true {
+            m.insert(k, v);
+        } else {
+            m.insert(k, v2);
+        }
+    }
+
+    // semicolon on if, use or_insert_with(..)
+    if !m.contains_key(&k) {
+        if true {
+            m.insert(k, v)
+        } else {
+            m.insert(k, v2)
+        };
+    }
+
+    // early return, use if let
+    if !m.contains_key(&k) {
+        if true {
+            m.insert(k, v);
+        } else {
+            m.insert(k, v2);
+            return;
+        }
+    }
+
+    // use or_insert_with(..)
+    if !m.contains_key(&k) {
+        foo();
+        m.insert(k, v);
+    }
+
+    // semicolon on insert and match, use or_insert_with(..)
+    if !m.contains_key(&k) {
+        match 0 {
+            1 if true => {
+                m.insert(k, v);
+            },
+            _ => {
+                m.insert(k, v2);
+            },
+        };
+    }
+
+    // one branch doesn't insert, use if let
+    if !m.contains_key(&k) {
+        match 0 {
+            0 => foo(),
+            _ => {
+                m.insert(k, v2);
+            },
+        };
+    }
+
+    // use or_insert_with
+    if !m.contains_key(&k) {
+        foo();
+        match 0 {
+            0 if false => {
+                m.insert(k, v);
+            },
+            1 => {
+                foo();
+                m.insert(k, v);
+            },
+            2 | 3 => {
+                for _ in 0..2 {
+                    foo();
+                }
+                if true {
+                    m.insert(k, v);
+                } else {
+                    m.insert(k, v2);
+                };
+            },
+            _ => {
+                m.insert(k, v2);
+            },
+        }
+    }
+
+    // ok, insert in loop
+    if !m.contains_key(&k) {
+        for _ in 0..2 {
+            m.insert(k, v);
+        }
+    }
+
+    // macro_expansion test, use or_insert(..)
+    if !m.contains_key(&m!(k)) {
+        m.insert(m!(k), m!(v));
+    }
+
+    // ok, map used before insertion
+    if !m.contains_key(&k) {
+        let _ = m.len();
+        m.insert(k, v);
+    }
+
+    // ok, inline asm
+    if !m.contains_key(&k) {
+        unsafe { asm!("nop") }
+        m.insert(k, v);
+    }
+
+    // ok, different keys.
+    if !m.contains_key(&k) {
+        m.insert(k2, v);
+    }
+
+    // ok, different maps
+    if !m.contains_key(&k) {
+        m2.insert(k, v);
+    }
+
+    // ok, insert in macro
+    if !m.contains_key(&k) {
+        insert!(m, k, v);
+    }
+}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
+    // insert then do something, use if let
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+        foo();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry.stderr b/src/tools/clippy/tests/ui/entry.stderr
new file mode 100644 (file)
index 0000000..2f075a9
--- /dev/null
@@ -0,0 +1,186 @@
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:24:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     }
+   | |_____^ help: try this: `m.entry(k).or_insert(v);`
+   |
+   = note: `-D clippy::map-entry` implied by `-D warnings`
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:29:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         if true {
+LL | |             m.insert(k, v);
+LL | |         } else {
+LL | |             m.insert(k, v2);
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         if true {
+LL |             v
+LL |         } else {
+LL |             v2
+LL |         }
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:38:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         if true {
+LL | |             m.insert(k, v)
+LL | |         } else {
+LL | |             m.insert(k, v2)
+LL | |         };
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         if true {
+LL |             v
+LL |         } else {
+LL |             v2
+LL |         }
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:47:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         if true {
+LL | |             m.insert(k, v);
+LL | |         } else {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL |         if true {
+LL |             e.insert(v);
+LL |         } else {
+LL |             e.insert(v2);
+LL |             return;
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:57:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         foo();
+LL | |         m.insert(k, v);
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         foo();
+LL |         v
+LL |     });
+   |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:63:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         match 0 {
+LL | |             1 if true => {
+LL | |                 m.insert(k, v);
+...  |
+LL | |         };
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         match 0 {
+LL |             1 if true => {
+LL |                 v
+LL |             },
+LL |             _ => {
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:75:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         match 0 {
+LL | |             0 => foo(),
+LL | |             _ => {
+...  |
+LL | |         };
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL |         match 0 {
+LL |             0 => foo(),
+LL |             _ => {
+LL |                 e.insert(v2);
+LL |             },
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:85:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         foo();
+LL | |         match 0 {
+LL | |             0 if false => {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         foo();
+LL |         match 0 {
+LL |             0 if false => {
+LL |                 v
+LL |             },
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:119:5
+   |
+LL | /     if !m.contains_key(&m!(k)) {
+LL | |         m.insert(m!(k), m!(v));
+LL | |     }
+   | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));`
+
+error: usage of `contains_key` followed by `insert` on a `BTreeMap`
+  --> $DIR/entry.rs:153:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |         foo();
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+LL |         e.insert(v);
+LL |         foo();
+LL |     }
+   |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/entry_fixable.fixed b/src/tools/clippy/tests/ui/entry_fixable.fixed
deleted file mode 100644 (file)
index dcdaae7..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-// run-rustfix
-
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    m.entry(k).or_insert(v);
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_fixable.rs b/src/tools/clippy/tests/ui/entry_fixable.rs
deleted file mode 100644 (file)
index 55d5b21..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-// run-rustfix
-
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        m.insert(k, v);
-    }
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_fixable.stderr b/src/tools/clippy/tests/ui/entry_fixable.stderr
deleted file mode 100644 (file)
index 8740320..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_fixable.rs:12:5
-   |
-LL | /     if !m.contains_key(&k) {
-LL | |         m.insert(k, v);
-LL | |     }
-   | |_____^ help: consider using: `m.entry(k).or_insert(v);`
-   |
-   = note: `-D clippy::map-entry` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui/entry_unfixable.rs b/src/tools/clippy/tests/ui/entry_unfixable.rs
deleted file mode 100644 (file)
index f530fc0..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        m.insert(k, v)
-    } else {
-        None
-    };
-}
-
-fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if m.contains_key(&k) {
-        None
-    } else {
-        m.insert(k, v)
-    };
-}
-
-fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        foo();
-        m.insert(k, v)
-    } else {
-        None
-    };
-}
-
-fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if m.contains_key(&k) {
-        None
-    } else {
-        foo();
-        m.insert(k, v)
-    };
-}
-
-fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        foo();
-        m.insert(k, v)
-    } else {
-        None
-    };
-}
-
-// should not trigger
-fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) {
-    if !m.contains_key(&k) {
-        m.insert(o, v);
-    }
-}
-
-// should not trigger, because the one uses different HashMap from another one
-fn insert_from_different_map<K: Eq + Hash, V>(m: HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        n.insert(k, v);
-    }
-}
-
-// should not trigger, because the one uses different HashMap from another one
-fn insert_from_different_map2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        n.insert(k, v);
-    }
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_unfixable.stderr b/src/tools/clippy/tests/ui/entry_unfixable.stderr
deleted file mode 100644 (file)
index e58c8d2..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_unfixable.rs:10:5
-   |
-LL | /     if !m.contains_key(&k) {
-LL | |         m.insert(k, v)
-LL | |     } else {
-LL | |         None
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-   |
-   = note: `-D clippy::map-entry` implied by `-D warnings`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_unfixable.rs:18:5
-   |
-LL | /     if m.contains_key(&k) {
-LL | |         None
-LL | |     } else {
-LL | |         m.insert(k, v)
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_unfixable.rs:26:5
-   |
-LL | /     if !m.contains_key(&k) {
-LL | |         foo();
-LL | |         m.insert(k, v)
-LL | |     } else {
-LL | |         None
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_unfixable.rs:35:5
-   |
-LL | /     if m.contains_key(&k) {
-LL | |         None
-LL | |     } else {
-LL | |         foo();
-LL | |         m.insert(k, v)
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `BTreeMap`
-  --> $DIR/entry_unfixable.rs:44:5
-   |
-LL | /     if !m.contains_key(&k) {
-LL | |         foo();
-LL | |         m.insert(k, v)
-LL | |     } else {
-LL | |         None
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-
-error: aborting due to 5 previous errors
-
diff --git a/src/tools/clippy/tests/ui/entry_with_else.fixed b/src/tools/clippy/tests/ui/entry_with_else.fixed
new file mode 100644 (file)
index 0000000..2332fa6
--- /dev/null
@@ -0,0 +1,73 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+    ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+    match m.entry(k) {
+        std::collections::hash_map::Entry::Vacant(e) => {
+            e.insert(v);
+        }
+        std::collections::hash_map::Entry::Occupied(mut e) => {
+            e.insert(v2);
+        }
+    }
+
+    match m.entry(k) {
+        std::collections::hash_map::Entry::Occupied(mut e) => {
+            e.insert(v);
+        }
+        std::collections::hash_map::Entry::Vacant(e) => {
+            e.insert(v2);
+        }
+    }
+
+    if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+        e.insert(v);
+    } else {
+        foo();
+    }
+
+    if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+        e.insert(v);
+    } else {
+        foo();
+    }
+
+    match m.entry(k) {
+        std::collections::hash_map::Entry::Vacant(e) => {
+            e.insert(v);
+        }
+        std::collections::hash_map::Entry::Occupied(mut e) => {
+            e.insert(v2);
+        }
+    }
+
+    match m.entry(k) {
+        std::collections::hash_map::Entry::Occupied(mut e) => {
+            if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+        }
+        std::collections::hash_map::Entry::Vacant(e) => {
+            e.insert(v);
+            None
+        }
+    };
+
+    if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+        foo();
+        Some(e.insert(v))
+    } else {
+        None
+    };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_with_else.rs b/src/tools/clippy/tests/ui/entry_with_else.rs
new file mode 100644 (file)
index 0000000..2ff0c03
--- /dev/null
@@ -0,0 +1,60 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+    ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+    } else {
+        m.insert(k, v2);
+    }
+
+    if m.contains_key(&k) {
+        m.insert(k, v);
+    } else {
+        m.insert(k, v2);
+    }
+
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+    } else {
+        foo();
+    }
+
+    if !m.contains_key(&k) {
+        foo();
+    } else {
+        m.insert(k, v);
+    }
+
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+    } else {
+        m.insert(k, v2);
+    }
+
+    if m.contains_key(&k) {
+        if true { m.insert(k, v) } else { m.insert(k, v2) }
+    } else {
+        m.insert(k, v)
+    };
+
+    if m.contains_key(&k) {
+        foo();
+        m.insert(k, v)
+    } else {
+        None
+    };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_with_else.stderr b/src/tools/clippy/tests/ui/entry_with_else.stderr
new file mode 100644 (file)
index 0000000..6f62ff8
--- /dev/null
@@ -0,0 +1,142 @@
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:16:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     } else {
+LL | |         m.insert(k, v2);
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::map-entry` implied by `-D warnings`
+help: try this
+   |
+LL |     match m.entry(k) {
+LL |         std::collections::hash_map::Entry::Vacant(e) => {
+LL |             e.insert(v);
+LL |         }
+LL |         std::collections::hash_map::Entry::Occupied(mut e) => {
+LL |             e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:22:5
+   |
+LL | /     if m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     } else {
+LL | |         m.insert(k, v2);
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     match m.entry(k) {
+LL |         std::collections::hash_map::Entry::Occupied(mut e) => {
+LL |             e.insert(v);
+LL |         }
+LL |         std::collections::hash_map::Entry::Vacant(e) => {
+LL |             e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:28:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     } else {
+LL | |         foo();
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL |         e.insert(v);
+LL |     } else {
+LL |         foo();
+LL |     }
+   |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:34:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         foo();
+LL | |     } else {
+LL | |         m.insert(k, v);
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL |         e.insert(v);
+LL |     } else {
+LL |         foo();
+LL |     }
+   |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:40:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     } else {
+LL | |         m.insert(k, v2);
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     match m.entry(k) {
+LL |         std::collections::hash_map::Entry::Vacant(e) => {
+LL |             e.insert(v);
+LL |         }
+LL |         std::collections::hash_map::Entry::Occupied(mut e) => {
+LL |             e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:46:5
+   |
+LL | /     if m.contains_key(&k) {
+LL | |         if true { m.insert(k, v) } else { m.insert(k, v2) }
+LL | |     } else {
+LL | |         m.insert(k, v)
+LL | |     };
+   | |_____^
+   |
+help: try this
+   |
+LL |     match m.entry(k) {
+LL |         std::collections::hash_map::Entry::Occupied(mut e) => {
+LL |             if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+LL |         }
+LL |         std::collections::hash_map::Entry::Vacant(e) => {
+LL |             e.insert(v);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:52:5
+   |
+LL | /     if m.contains_key(&k) {
+LL | |         foo();
+LL | |         m.insert(k, v)
+LL | |     } else {
+LL | |         None
+LL | |     };
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL |         foo();
+LL |         Some(e.insert(v))
+LL |     } else {
+LL |         None
+LL |     };
+   |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/filter_methods.rs b/src/tools/clippy/tests/ui/filter_methods.rs
deleted file mode 100644 (file)
index 5145024..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#![warn(clippy::all, clippy::pedantic)]
-#![allow(clippy::clippy::let_underscore_drop)]
-#![allow(clippy::missing_docs_in_private_items)]
-
-fn main() {
-    let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
-
-    let _: Vec<_> = vec![5_i8; 6]
-        .into_iter()
-        .filter(|&x| x == 0)
-        .flat_map(|x| x.checked_mul(2))
-        .collect();
-
-    let _: Vec<_> = vec![5_i8; 6]
-        .into_iter()
-        .filter_map(|x| x.checked_mul(2))
-        .flat_map(|x| x.checked_mul(2))
-        .collect();
-
-    let _: Vec<_> = vec![5_i8; 6]
-        .into_iter()
-        .filter_map(|x| x.checked_mul(2))
-        .map(|x| x.checked_mul(2))
-        .collect();
-}
diff --git a/src/tools/clippy/tests/ui/filter_methods.stderr b/src/tools/clippy/tests/ui/filter_methods.stderr
deleted file mode 100644 (file)
index c7b4f28..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-error: called `filter(..).flat_map(..)` on an `Iterator`
-  --> $DIR/filter_methods.rs:8:21
-   |
-LL |       let _: Vec<_> = vec![5_i8; 6]
-   |  _____________________^
-LL | |         .into_iter()
-LL | |         .filter(|&x| x == 0)
-LL | |         .flat_map(|x| x.checked_mul(2))
-   | |_______________________________________^
-   |
-   = note: `-D clippy::filter-map` implied by `-D warnings`
-   = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
-
-error: called `filter_map(..).flat_map(..)` on an `Iterator`
-  --> $DIR/filter_methods.rs:14:21
-   |
-LL |       let _: Vec<_> = vec![5_i8; 6]
-   |  _____________________^
-LL | |         .into_iter()
-LL | |         .filter_map(|x| x.checked_mul(2))
-LL | |         .flat_map(|x| x.checked_mul(2))
-   | |_______________________________________^
-   |
-   = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
-
-error: called `filter_map(..).map(..)` on an `Iterator`
-  --> $DIR/filter_methods.rs:20:21
-   |
-LL |       let _: Vec<_> = vec![5_i8; 6]
-   |  _____________________^
-LL | |         .into_iter()
-LL | |         .filter_map(|x| x.checked_mul(2))
-LL | |         .map(|x| x.checked_mul(2))
-   | |__________________________________^
-   |
-   = help: this is more succinctly expressed by only calling `.filter_map(..)` instead
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui/flat_map_option.fixed b/src/tools/clippy/tests/ui/flat_map_option.fixed
new file mode 100644 (file)
index 0000000..6a34f00
--- /dev/null
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::flat_map_option)]
+#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
+
+fn main() {
+    // yay
+    let c = |x| Some(x);
+    let _ = [1].iter().filter_map(c);
+    let _ = [1].iter().filter_map(Some);
+
+    // nay
+    let _ = [1].iter().flat_map(|_| &Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_option.rs b/src/tools/clippy/tests/ui/flat_map_option.rs
new file mode 100644 (file)
index 0000000..2479abd
--- /dev/null
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::flat_map_option)]
+#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
+
+fn main() {
+    // yay
+    let c = |x| Some(x);
+    let _ = [1].iter().flat_map(c);
+    let _ = [1].iter().flat_map(Some);
+
+    // nay
+    let _ = [1].iter().flat_map(|_| &Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_option.stderr b/src/tools/clippy/tests/ui/flat_map_option.stderr
new file mode 100644 (file)
index 0000000..a9d8056
--- /dev/null
@@ -0,0 +1,16 @@
+error: used `flat_map` where `filter_map` could be used instead
+  --> $DIR/flat_map_option.rs:8:24
+   |
+LL |     let _ = [1].iter().flat_map(c);
+   |                        ^^^^^^^^ help: try: `filter_map`
+   |
+   = note: `-D clippy::flat-map-option` implied by `-D warnings`
+
+error: used `flat_map` where `filter_map` could be used instead
+  --> $DIR/flat_map_option.rs:9:24
+   |
+LL |     let _ = [1].iter().flat_map(Some);
+   |                        ^^^^^^^^ help: try: `filter_map`
+
+error: aborting due to 2 previous errors
+
index 249a88a0b3982cdccb57fa64418d5831d54c9c33..f44928d4083868968a448727c64cce4ba20ecb8a 100644 (file)
@@ -281,3 +281,29 @@ mod issue_4958 {
         for _ in rr.into_iter() {}
     }
 }
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+    struct S;
+    impl S {
+        #[allow(clippy::should_implement_trait)]
+        pub fn into_iter<T>(self) -> I<T> {
+            unimplemented!()
+        }
+    }
+
+    struct I<T>(T);
+    impl<T> Iterator for I<T> {
+        type Item = T;
+        fn next(&mut self) -> Option<Self::Item> {
+            unimplemented!()
+        }
+    }
+
+    fn f() {
+        for _ in S.into_iter::<u32>() {
+            unimplemented!()
+        }
+    }
+}
index 306d85a6351e107f6a7e6bbb0b8c70d9843a4a93..5b1eb3ee4dcd2421440fe4332b17c8a00af23c0f 100644 (file)
@@ -281,3 +281,29 @@ fn more_tests() {
         for _ in rr.into_iter() {}
     }
 }
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+    struct S;
+    impl S {
+        #[allow(clippy::should_implement_trait)]
+        pub fn into_iter<T>(self) -> I<T> {
+            unimplemented!()
+        }
+    }
+
+    struct I<T>(T);
+    impl<T> Iterator for I<T> {
+        type Item = T;
+        fn next(&mut self) -> Option<Self::Item> {
+            unimplemented!()
+        }
+    }
+
+    fn f() {
+        for _ in S.into_iter::<u32>() {
+            unimplemented!()
+        }
+    }
+}
index 740a22a07d747c5dc409fea811cf27afb96e5468..e4cfb005fd1d0d8e8a0ac4bd42392dddb56df35c 100644 (file)
@@ -65,4 +65,8 @@ fn main() {
     // False positive
     let a = "foo".to_string();
     let _ = Some(a + "bar");
+
+    // Wrap it with braces
+    let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+    let _s: String = (&*v.join("\n")).to_string();
 }
index b604d79cca373119935232370d9cee658c540f5c..683957f0ff0f79cb452811b66b0db83d90f55c8b 100644 (file)
@@ -67,4 +67,8 @@ fn main() {
     // False positive
     let a = "foo".to_string();
     let _ = Some(format!("{}", a + "bar"));
+
+    // Wrap it with braces
+    let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+    let _s: String = format!("{}", &*v.join("\n"));
 }
index 96df7f37f779232dd03fc305b86845381317ef69..2017eb2b3838fc46208b13ec268f6248bdb21d54 100644 (file)
@@ -87,5 +87,11 @@ error: useless use of `format!`
 LL |     let _ = Some(format!("{}", a + "bar"));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
 
-error: aborting due to 13 previous errors
+error: useless use of `format!`
+  --> $DIR/format.rs:73:22
+   |
+LL |     let _s: String = format!("{}", &*v.join("/n"));
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
+
+error: aborting due to 14 previous errors
 
index b101d2704fbda52692767a9e2a2a67679bea3711..2951e6bdac430bde13ea1a58bdfad95fb14a0a2f 100644 (file)
@@ -5,7 +5,7 @@ LL | impl Into<StringWrapper> for String {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::from-over-into` implied by `-D warnings`
-   = help: consider to implement `From` instead
+   = help: consider to implement `From<std::string::String>` instead
 
 error: aborting due to previous error
 
index 8d9c311003508173c227ebaef88e3d090a9e6ed7..d1025743790a9747bfb95c6bb746d0b1e15b917c 100644 (file)
@@ -13,6 +13,15 @@ struct Foo {
     z: i32,
 }
 
+macro_rules! new_foo {
+    () => {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+        Foo { y, x, z }
+    };
+}
+
 mod without_base {
     use super::Foo;
 
@@ -24,6 +33,10 @@ mod without_base {
         // Should lint.
         Foo { x, y, z };
 
+        // Should NOT lint.
+        // issue #7069.
+        new_foo!();
+
         // Shoule NOT lint because the order is the same as in the definition.
         Foo { x, y, z };
 
index 63fac9105015d3756b5be6edc83c52777c5d5179..b095aa64a2174371e94d9f2c240ed910c9c893ca 100644 (file)
@@ -13,6 +13,15 @@ struct Foo {
     z: i32,
 }
 
+macro_rules! new_foo {
+    () => {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+        Foo { y, x, z }
+    };
+}
+
 mod without_base {
     use super::Foo;
 
@@ -24,6 +33,10 @@ fn test() {
         // Should lint.
         Foo { y, x, z };
 
+        // Should NOT lint.
+        // issue #7069.
+        new_foo!();
+
         // Shoule NOT lint because the order is the same as in the definition.
         Foo { x, y, z };
 
index d021bb19579f4baa6c3d4cd5fa8fd9e58c4f7c1c..ef308dedb1661c331703d68d9ab973477f473d51 100644 (file)
@@ -1,5 +1,5 @@
 error: struct constructor field order is inconsistent with struct definition field order
-  --> $DIR/inconsistent_struct_constructor.rs:25:9
+  --> $DIR/inconsistent_struct_constructor.rs:34:9
    |
 LL |         Foo { y, x, z };
    |         ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
@@ -7,7 +7,7 @@ LL |         Foo { y, x, z };
    = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
 
 error: struct constructor field order is inconsistent with struct definition field order
-  --> $DIR/inconsistent_struct_constructor.rs:43:9
+  --> $DIR/inconsistent_struct_constructor.rs:56:9
    |
 LL | /         Foo {
 LL | |             z,
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed
new file mode 100644 (file)
index 0000000..4f5322e
--- /dev/null
@@ -0,0 +1,49 @@
+// run-rustfix
+
+fn main() {
+    unsafe {
+        let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+        let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        std::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        std::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        struct A; // zero sized struct
+        assert_eq!(std::mem::size_of::<A>(), 0);
+
+        let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        std::ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A);
+        std::ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr());
+
+        std::ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0);
+        std::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        std::ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs
new file mode 100644 (file)
index 0000000..ae51c52
--- /dev/null
@@ -0,0 +1,49 @@
+// run-rustfix
+
+fn main() {
+    unsafe {
+        let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
+        let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
+
+        let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+
+        std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+
+        std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+
+        struct A; // zero sized struct
+        assert_eq!(std::mem::size_of::<A>(), 0);
+
+        let _a: A = std::ptr::read(std::ptr::null());
+        let _a: A = std::ptr::read(std::ptr::null_mut());
+
+        let _a: A = std::ptr::read_unaligned(std::ptr::null());
+        let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
+
+        let _a: A = std::ptr::read_volatile(std::ptr::null());
+        let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
+
+        let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
+
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0);
+
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
+
+        std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
+        std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
+
+        std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
+        std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
+
+        std::ptr::write(std::ptr::null_mut(), A);
+
+        std::ptr::write_unaligned(std::ptr::null_mut(), A);
+
+        std::ptr::write_volatile(std::ptr::null_mut(), A);
+
+        std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr
new file mode 100644 (file)
index 0000000..532c36a
--- /dev/null
@@ -0,0 +1,154 @@
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:5:59
+   |
+LL |         let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
+   |                                                           ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |
+   = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:6:59
+   |
+LL |         let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
+   |                                                           ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:8:63
+   |
+LL |         let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+   |                                                               ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:10:33
+   |
+LL |         std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+   |                                 ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:11:73
+   |
+LL |         std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+   |                                                                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:13:48
+   |
+LL |         std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+   |                                                ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:14:88
+   |
+LL |         std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+   |                                                                                        ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:19:36
+   |
+LL |         let _a: A = std::ptr::read(std::ptr::null());
+   |                                    ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:20:36
+   |
+LL |         let _a: A = std::ptr::read(std::ptr::null_mut());
+   |                                    ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:22:46
+   |
+LL |         let _a: A = std::ptr::read_unaligned(std::ptr::null());
+   |                                              ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:23:46
+   |
+LL |         let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
+   |                                              ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:25:45
+   |
+LL |         let _a: A = std::ptr::read_volatile(std::ptr::null());
+   |                                             ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:26:45
+   |
+LL |         let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
+   |                                             ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:28:39
+   |
+LL |         let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
+   |                                       ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:30:69
+   |
+LL |         let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
+   |                                                                     ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:31:69
+   |
+LL |         let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0);
+   |                                                                     ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:33:73
+   |
+LL |         let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
+   |                                                                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:35:29
+   |
+LL |         std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
+   |                             ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:36:37
+   |
+LL |         std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
+   |                                     ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:38:44
+   |
+LL |         std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
+   |                                            ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:39:52
+   |
+LL |         std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
+   |                                                    ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:41:25
+   |
+LL |         std::ptr::write(std::ptr::null_mut(), A);
+   |                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:43:35
+   |
+LL |         std::ptr::write_unaligned(std::ptr::null_mut(), A);
+   |                                   ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:45:34
+   |
+LL |         std::ptr::write_volatile(std::ptr::null_mut(), A);
+   |                                  ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:47:40
+   |
+LL |         std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
+   |                                        ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: aborting due to 25 previous errors
+
index 2773227e26bcaa0be55b82eb4abbedcdb8816829..39cc58cd298439d0c06756b5f68090234b9414b1 100644 (file)
@@ -19,4 +19,8 @@ fn main() {
         let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
             .to_bytes().to_vec();
     }
+
+    // Issue #6808
+    let arr: [u8; 64] = [0; 64];
+    let _: Vec<_> = arr.to_vec();
 }
index 60a4eac23c79f61650593f77063d69d40a3605c3..c2a036ec09f1e80cea582f0b49c746497e95e3ce 100644 (file)
@@ -22,4 +22,8 @@ fn main() {
             .cloned()
             .collect();
     }
+
+    // Issue #6808
+    let arr: [u8; 64] = [0; 64];
+    let _: Vec<_> = arr.iter().cloned().collect();
 }
index b90a1e6c9196733db6f63a23f2e48a2e0dfe136e..e1df61794cecee7273dfac211f425fec0f924680 100644 (file)
@@ -22,5 +22,11 @@ LL | |             .cloned()
 LL | |             .collect();
    | |______________________^ help: try: `.to_vec()`
 
-error: aborting due to 3 previous errors
+error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+  --> $DIR/iter_cloned_collect.rs:28:24
+   |
+LL |     let _: Vec<_> = arr.iter().cloned().collect();
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: aborting due to 4 previous errors
 
index 91e34c62160a129672ce3c9f08da34e3943ffd04..51c66a46368dba6010f92775aecfca6ac22e556e 100644 (file)
@@ -4,7 +4,7 @@
 // run-rustfix
 // ignore-32bit
 
-#![allow(unused_imports, unreachable_code, unused_variables, dead_code)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
 #![warn(clippy::macro_use_imports)]
 
@@ -40,4 +40,8 @@ mod a {
     }
 }
 
+// issue #7015, ICE due to calling `item_children` with local `DefId`
+#[macro_use]
+use a as b;
+
 fn main() {}
index 9c3c50c5d49f29b0a03eea1ca77c583a71686597..2011129bc944d86a997e2d83df19fc3de344f5db 100644 (file)
@@ -4,7 +4,7 @@
 // run-rustfix
 // ignore-32bit
 
-#![allow(unused_imports, unreachable_code, unused_variables, dead_code)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
 #![warn(clippy::macro_use_imports)]
 
@@ -40,4 +40,8 @@ fn test() {
     }
 }
 
+// issue #7015, ICE due to calling `item_children` with local `DefId`
+#[macro_use]
+use a as b;
+
 fn main() {}
index ee0158457778673751f097c518d285560900de7a..40d01df6379a696496028454027d8acaba84f1c8 100644 (file)
@@ -146,4 +146,11 @@ fn main() {
             None => None,
         };
     }
+
+    // #7077
+    let s = &String::new();
+    let _: Option<&str> = match Some(s) {
+        Some(s) => Some(s),
+        None => None,
+    };
 }
index 29509bddfd94d260a10dc919a24d01273d521577..cfef0c5cc4ec66207834005b7b151d666737c0fa 100644 (file)
@@ -212,4 +212,11 @@ const fn f4() {
             None => None,
         };
     }
+
+    // #7077
+    let s = &String::new();
+    let _: Option<&str> = match Some(s) {
+        Some(s) => Some(s),
+        None => None,
+    };
 }
index f1d3252230bc2b7a23c57fa0037c7daa05df0040..e7a29596b73ac50f8fdb1723c089e9425dbdf3bb 100644 (file)
@@ -151,4 +151,16 @@ const fn const_fn_result_unwrap_or() {
     };
 }
 
+mod issue6965 {
+    macro_rules! some_macro {
+        () => {
+            if 1 > 2 { Some(1) } else { None }
+        };
+    }
+
+    fn test() {
+        let _ = some_macro!().unwrap_or(0);
+    }
+}
+
 fn main() {}
index c9eee25a5b1538d96a38e36d8922f44ea1bae6ff..66006b6c616f08f9e1858e53ef451b57b26c130f 100644 (file)
@@ -190,4 +190,19 @@ const fn const_fn_result_unwrap_or() {
     };
 }
 
+mod issue6965 {
+    macro_rules! some_macro {
+        () => {
+            if 1 > 2 { Some(1) } else { None }
+        };
+    }
+
+    fn test() {
+        let _ = match some_macro!() {
+            Some(val) => val,
+            None => 0,
+        };
+    }
+}
+
 fn main() {}
index fc174c4c2705dc3c6d7ea4b6df6b889b7b881c8c..99625b789b6a4d7d0728ca89eb19dd98e8d73125 100644 (file)
@@ -141,5 +141,15 @@ LL | |         Err(_) => "Alice",
 LL | |     };
    | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")`
 
-error: aborting due to 13 previous errors
+error: this pattern reimplements `Option::unwrap_or`
+  --> $DIR/manual_unwrap_or.rs:201:17
+   |
+LL |           let _ = match some_macro!() {
+   |  _________________^
+LL | |             Some(val) => val,
+LL | |             None => 0,
+LL | |         };
+   | |_________^ help: replace with: `some_macro!().unwrap_or(0)`
+
+error: aborting due to 14 previous errors
 
index 49ace1ca128c121c25e9f02664f63c29f5128ea9..7f9f7ddc53575ca57a21cc0840df551218571da1 100644 (file)
@@ -4,6 +4,10 @@
 
 use std::ops::{Deref, RangeFrom};
 
+fn cloned_instead_of_copied() {
+    let _ = [1].iter().cloned();
+}
+
 fn option_as_ref_deref() {
     let mut opt = Some(String::from("123"));
 
index 8d3575c2da83b4e322029080e8fb694ded856b93..ddb1e1f37240909738d742fcd917b6c0b4d83d02 100644 (file)
@@ -1,12 +1,12 @@
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:156:24
+  --> $DIR/min_rust_version_attr.rs:160:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::manual-strip` implied by `-D warnings`
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:155:9
+  --> $DIR/min_rust_version_attr.rs:159:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL |             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
    |
 
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:168:24
+  --> $DIR/min_rust_version_attr.rs:172:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:167:9
+  --> $DIR/min_rust_version_attr.rs:171:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs
new file mode 100644 (file)
index 0000000..7b9dc76
--- /dev/null
@@ -0,0 +1,8 @@
+// This file provides a const function that is unstably const forever.
+
+#![feature(staged_api)]
+#![stable(feature = "1", since = "1.0.0")]
+
+#[stable(feature = "1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "foo", issue = "none")]
+pub const fn unstably_const_fn() {}
index ba352ef9ee932a64064409e83f6c721e23286617..7cda1aaa3c22894def353f9e6223546a930ab2f5 100644 (file)
@@ -2,9 +2,14 @@
 //! compilation error.
 //! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
 
+// aux-build:helper.rs
+
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features)]
 #![feature(start, const_generics)]
+#![feature(custom_inner_attributes)]
+
+extern crate helper;
 
 struct Game;
 
@@ -101,3 +106,17 @@ pub fn a(self, a: A) -> B {
 
     unsafe { &*p }
 }
+
+// Do not lint this because it calls a function whose constness is unstable.
+fn unstably_const_fn() {
+    helper::unstably_const_fn()
+}
+
+mod const_fn_stabilized_after_msrv {
+    #![clippy::msrv = "1.46.0"]
+
+    // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
+    fn const_fn_stabilized_after_msrv(byte: u8) {
+        byte.is_ascii_digit();
+    }
+}
index c6f44b7daa3421b13a6c9da0b28bc564577af9c6..0accb516f5f6b4a145e161c708cc726a72b221a8 100644 (file)
@@ -1,6 +1,7 @@
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features, clippy::let_and_return)]
 #![feature(const_generics)]
+#![feature(custom_inner_attributes)]
 
 use std::mem::transmute;
 
@@ -70,5 +71,14 @@ pub fn b(self, a: &A) -> B {
     }
 }
 
+mod const_fn_stabilized_before_msrv {
+    #![clippy::msrv = "1.47.0"]
+
+    // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
+    fn const_fn_stabilized_before_msrv(byte: u8) {
+        byte.is_ascii_digit();
+    }
+}
+
 // Should not be const
 fn main() {}
index 74d32b8a1aa933a0960b5751b503103b2735c384..63c211f39fa1e3530a1be0d041f69d6bae65ee63 100644 (file)
@@ -1,5 +1,5 @@
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:13:5
+  --> $DIR/could_be_const.rs:14:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         Self { guess: 42 }
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:17:5
+  --> $DIR/could_be_const.rs:18:5
    |
 LL | /     fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
 LL | |         b
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:23:1
+  --> $DIR/could_be_const.rs:24:1
    |
 LL | / fn one() -> i32 {
 LL | |     1
@@ -25,7 +25,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:28:1
+  --> $DIR/could_be_const.rs:29:1
    |
 LL | / fn two() -> i32 {
 LL | |     let abc = 2;
@@ -34,7 +34,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:34:1
+  --> $DIR/could_be_const.rs:35:1
    |
 LL | / fn string() -> String {
 LL | |     String::new()
@@ -42,7 +42,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:39:1
+  --> $DIR/could_be_const.rs:40:1
    |
 LL | / unsafe fn four() -> i32 {
 LL | |     4
@@ -50,7 +50,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:44:1
+  --> $DIR/could_be_const.rs:45:1
    |
 LL | / fn generic<T>(t: T) -> T {
 LL | |     t
@@ -58,12 +58,20 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:67:9
+  --> $DIR/could_be_const.rs:68:9
    |
 LL | /         pub fn b(self, a: &A) -> B {
 LL | |             B
 LL | |         }
    | |_________^
 
-error: aborting due to 8 previous errors
+error: this could be a `const fn`
+  --> $DIR/could_be_const.rs:78:5
+   |
+LL | /     fn const_fn_stabilized_before_msrv(byte: u8) {
+LL | |         byte.is_ascii_digit();
+LL | |     }
+   | |_____^
+
+error: aborting due to 9 previous errors
 
index fd8433870bb1b8e6bd07e4e45e4ef06b48d6f14f..52ddd9d2dc8266ac128e0c9bcc17f9d7394ecd21 100644 (file)
@@ -96,78 +96,6 @@ where
 
 fn main() {}
 
-mod question_mark_none {
-    #![clippy::msrv = "1.12.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should not be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        Ok(to.magic?) // should not be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
-mod question_mark_result {
-    #![clippy::msrv = "1.21.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should not be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        to.magic // should be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
-mod question_mark_both {
-    #![clippy::msrv = "1.22.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        to.magic // should be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        to.magic // should be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
 // #6921 if a macro wraps an expr in Some(  ) and the ? is in the macro use,
 // the suggestion fails to apply; do not lint
 macro_rules! some_in_macro {
index 36d45ac7e03e29789c7a43cde1150c57ce2a928e..1ea4ba0d83fd76da23c3ac13bb4dcb3caf9cc801 100644 (file)
@@ -96,78 +96,6 @@ fn false_positive_test<U, T>(x: Result<(), U>) -> Result<(), T>
 
 fn main() {}
 
-mod question_mark_none {
-    #![clippy::msrv = "1.12.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should not be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        Ok(to.magic?) // should not be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
-mod question_mark_result {
-    #![clippy::msrv = "1.21.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should not be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        Ok(to.magic?) // should be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
-mod question_mark_both {
-    #![clippy::msrv = "1.22.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        Ok(to.magic?) // should be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
 // #6921 if a macro wraps an expr in Some(  ) and the ? is in the macro use,
 // the suggestion fails to apply; do not lint
 macro_rules! some_in_macro {
index 7cbf1e505adf63dff27f884fb4315c971bb1a1a3..afd68d91e51fe54c022487f30388a72ea434511e 100644 (file)
@@ -67,25 +67,7 @@ LL |         return Ok(t.magic?);
    |                ^^^^^^^^^^^^ help: try: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:138:9
-   |
-LL |         Ok(to.magic?) // should be triggered
-   |         ^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:154:9
-   |
-LL |         Some(to.magic?) // should be triggered
-   |         ^^^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:162:9
-   |
-LL |         Ok(to.magic?) // should be triggered
-   |         ^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:187:27
+  --> $DIR/needless_question_mark.rs:115:27
    |
 LL |         || -> Option<_> { Some(Some($expr)?) }()
    |                           ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
@@ -95,5 +77,5 @@ LL |     let _x = some_and_qmark_in_macro!(x?);
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 15 previous errors
+error: aborting due to 12 previous errors
 
index 82d95cc041fb35941a47c4862625db40d34e40bd..5c4fd466c04187df5abf52be6a504374c59c919b 100644 (file)
@@ -1,4 +1,5 @@
 // run-rustfix
+// edition:2018
 
 #![allow(unused)]
 #![allow(
@@ -125,10 +126,85 @@ mod issue6501 {
     }
 }
 
-fn main() {
-    let _ = test_end_of_fn();
-    let _ = test_no_semicolon();
-    let _ = test_if_block();
-    let _ = test_match(true);
-    test_closure();
+async fn async_test_end_of_fn() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    true
+}
+
+async fn async_test_no_semicolon() -> bool {
+    true
+}
+
+async fn async_test_if_block() -> bool {
+    if true {
+        true
+    } else {
+        false
+    }
+}
+
+async fn async_test_match(x: bool) -> bool {
+    match x {
+        true => false,
+        false => {
+            true
+        },
+    }
+}
+
+async fn async_test_closure() {
+    let _ = || {
+        true
+    };
+    let _ = || true;
+}
+
+async fn async_test_macro_call() -> i32 {
+    return the_answer!();
+}
+
+async fn async_test_void_fun() {
+    
+}
+
+async fn async_test_void_if_fun(b: bool) {
+    if b {
+        
+    } else {
+        
+    }
+}
+
+async fn async_test_void_match(x: u32) {
+    match x {
+        0 => (),
+        _ => {},
+    }
+}
+
+async fn async_read_line() -> String {
+    use std::io::BufRead;
+    let stdin = ::std::io::stdin();
+    return stdin.lock().lines().next().unwrap().unwrap();
 }
+
+async fn async_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()
+    }
+}
+
+async fn async_test_return_in_macro() {
+    needed_return!(10);
+    needed_return!(0);
+}
+
+fn main() {}
index 8a471f802e111dbf62fa4d07afc1cb23582f2138..34811db7413a3ac5d90254dd7591c02b53b6556b 100644 (file)
@@ -1,4 +1,5 @@
 // run-rustfix
+// edition:2018
 
 #![allow(unused)]
 #![allow(
@@ -125,10 +126,85 @@ fn bar(res: Result<Foo, u8>) -> Foo {
     }
 }
 
-fn main() {
-    let _ = test_end_of_fn();
-    let _ = test_no_semicolon();
-    let _ = test_if_block();
-    let _ = test_match(true);
-    test_closure();
+async fn async_test_end_of_fn() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    return true;
+}
+
+async fn async_test_no_semicolon() -> bool {
+    return true;
+}
+
+async fn async_test_if_block() -> bool {
+    if true {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+async fn async_test_match(x: bool) -> bool {
+    match x {
+        true => return false,
+        false => {
+            return true;
+        },
+    }
+}
+
+async fn async_test_closure() {
+    let _ = || {
+        return true;
+    };
+    let _ = || return true;
+}
+
+async fn async_test_macro_call() -> i32 {
+    return the_answer!();
+}
+
+async fn async_test_void_fun() {
+    return;
+}
+
+async fn async_test_void_if_fun(b: bool) {
+    if b {
+        return;
+    } else {
+        return;
+    }
+}
+
+async fn async_test_void_match(x: u32) {
+    match x {
+        0 => (),
+        _ => return,
+    }
+}
+
+async fn async_read_line() -> String {
+    use std::io::BufRead;
+    let stdin = ::std::io::stdin();
+    return stdin.lock().lines().next().unwrap().unwrap();
 }
+
+async fn async_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();
+    }
+}
+
+async fn async_test_return_in_macro() {
+    needed_return!(10);
+    needed_return!(0);
+}
+
+fn main() {}
index 075db22456f736b0ad268830dd5687af4aa1f702..74dda971fdabb632633f4a125ba15d6e2d5ad1a3 100644 (file)
@@ -1,5 +1,5 @@
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:23:5
+  --> $DIR/needless_return.rs:24:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
@@ -7,106 +7,190 @@ LL |     return true;
    = note: `-D clippy::needless-return` implied by `-D warnings`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:27:5
+  --> $DIR/needless_return.rs:28:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:32:9
+  --> $DIR/needless_return.rs:33:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:34:9
+  --> $DIR/needless_return.rs:35:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:40:17
+  --> $DIR/needless_return.rs:41:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:42:13
+  --> $DIR/needless_return.rs:43:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:49:9
+  --> $DIR/needless_return.rs:50:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:51:16
+  --> $DIR/needless_return.rs:52:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:59:5
+  --> $DIR/needless_return.rs:60:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:64:9
+  --> $DIR/needless_return.rs:65:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:66:9
+  --> $DIR/needless_return.rs:67:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:73:14
+  --> $DIR/needless_return.rs:74:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:88:9
+  --> $DIR/needless_return.rs:89:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:90:9
+  --> $DIR/needless_return.rs:91:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:111:32
+  --> $DIR/needless_return.rs:112:32
    |
 LL |         bar.unwrap_or_else(|_| return)
    |                                ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:116:13
+  --> $DIR/needless_return.rs:117:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:118:20
+  --> $DIR/needless_return.rs:119:20
    |
 LL |         let _ = || return;
    |                    ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:124:32
+  --> $DIR/needless_return.rs:125:32
    |
 LL |         res.unwrap_or_else(|_| return Foo)
    |                                ^^^^^^^^^^ help: remove `return`: `Foo`
 
-error: aborting due to 18 previous errors
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:134:5
+   |
+LL |     return true;
+   |     ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:138:5
+   |
+LL |     return true;
+   |     ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:143:9
+   |
+LL |         return true;
+   |         ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:145:9
+   |
+LL |         return false;
+   |         ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:151:17
+   |
+LL |         true => return false,
+   |                 ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:153:13
+   |
+LL |             return true;
+   |             ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:160:9
+   |
+LL |         return true;
+   |         ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:162:16
+   |
+LL |     let _ = || return true;
+   |                ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:170:5
+   |
+LL |     return;
+   |     ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:175:9
+   |
+LL |         return;
+   |         ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:177:9
+   |
+LL |         return;
+   |         ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:184:14
+   |
+LL |         _ => return,
+   |              ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:199:9
+   |
+LL |         return String::from("test");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:201:9
+   |
+LL |         return String::new();
+   |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: aborting due to 32 previous errors
 
index b60c79f97c865935a72e954ad765a6680e5eb4d2..c4fcd7e70944c5ebc974916f88b81877a6c83102 100644 (file)
@@ -1,44 +1,39 @@
 #![warn(clippy::panic_in_result_fn)]
 #![allow(clippy::unnecessary_wraps)]
 
+// debug_assert should never trigger the `panic_in_result_fn` lint
+
 struct A;
 
 impl A {
-    fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
-    {
+    fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> {
         debug_assert!(x == 5, "wrong argument");
         Ok(true)
     }
 
-    fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> // should emit lint
-    {
+    fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> {
         debug_assert_eq!(x, 5);
         Ok(true)
     }
 
-    fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> // should emit lint
-    {
+    fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> {
         debug_assert_ne!(x, 1);
         Ok(true)
     }
 
-    fn other_with_debug_assert_with_message(x: i32) // should not emit lint
-    {
+    fn other_with_debug_assert_with_message(x: i32) {
         debug_assert!(x == 5, "wrong argument");
     }
 
-    fn other_with_debug_assert_eq(x: i32) // should not emit lint
-    {
+    fn other_with_debug_assert_eq(x: i32) {
         debug_assert_eq!(x, 5);
     }
 
-    fn other_with_debug_assert_ne(x: i32) // should not emit lint
-    {
+    fn other_with_debug_assert_ne(x: i32) {
         debug_assert_ne!(x, 1);
     }
 
-    fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
-    {
+    fn result_without_banned_functions() -> Result<bool, String> {
         let debug_assert = "debug_assert!";
         println!("No {}", debug_assert);
         Ok(true)
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr
deleted file mode 100644 (file)
index ec18e89..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5
-   |
-LL | /     fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
-LL | |     {
-LL | |         debug_assert!(x == 5, "wrong argument");
-LL | |         Ok(true)
-LL | |     }
-   | |_____^
-   |
-   = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9
-   |
-LL |         debug_assert!(x == 5, "wrong argument");
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5
-   |
-LL | /     fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> // should emit lint
-LL | |     {
-LL | |         debug_assert_eq!(x, 5);
-LL | |         Ok(true)
-LL | |     }
-   | |_____^
-   |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9
-   |
-LL |         debug_assert_eq!(x, 5);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5
-   |
-LL | /     fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> // should emit lint
-LL | |     {
-LL | |         debug_assert_ne!(x, 1);
-LL | |         Ok(true)
-LL | |     }
-   | |_____^
-   |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9
-   |
-LL |         debug_assert_ne!(x, 1);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 3 previous errors
-
index 77fcb8dfd02fd7460fd097e3c06570cb3cb41cc8..93b236f7473d5f2d3d823ae63ce8edcd37d00515 100644 (file)
@@ -1,5 +1,5 @@
 #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
-#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::assertions_on_constants, clippy::eq_op)]
 
 extern crate core;
 
@@ -43,6 +43,18 @@ fn core_versions() {
     unreachable!();
 }
 
+fn debug_assert() {
+    debug_assert!(true);
+    debug_assert_eq!(true, true);
+    debug_assert_ne!(true, false);
+}
+
+fn debug_assert_msg() {
+    debug_assert!(true, "test");
+    debug_assert_eq!(true, true, "test");
+    debug_assert_ne!(true, false, "test");
+}
+
 fn main() {
     panic();
     todo();
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed
new file mode 100644 (file)
index 0000000..794ed54
--- /dev/null
@@ -0,0 +1,58 @@
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+    let m = std::sync::Mutex::new((0, 0));
+
+    // Result
+    if m.lock().is_ok() {}
+    if Err::<(), _>(m.lock().unwrap().0).is_err() {}
+
+    {
+        if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+    }
+    if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {
+    } else {
+    }
+    if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+    if Err::<std::sync::MutexGuard<()>, _>(()).is_err() {}
+
+    if Ok::<_, ()>(String::new()).is_ok() {}
+    if Err::<(), _>((String::new(), ())).is_err() {}
+
+    // Option
+    if Some(m.lock()).is_some() {}
+    if Some(m.lock().unwrap().0).is_some() {}
+
+    {
+        if None::<std::sync::MutexGuard<()>>.is_none() {}
+    }
+    if None::<std::sync::MutexGuard<()>>.is_none() {
+    } else {
+    }
+
+    if None::<std::sync::MutexGuard<()>>.is_none() {}
+
+    if Some(String::new()).is_some() {}
+    if Some((String::new(), ())).is_some() {}
+
+    // Poll
+    if Ready(m.lock()).is_ready() {}
+    if Ready(m.lock().unwrap().0).is_ready() {}
+
+    {
+        if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+    }
+    if Pending::<std::sync::MutexGuard<()>>.is_pending() {
+    } else {
+    }
+
+    if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+
+    if Ready(String::new()).is_ready() {}
+    if Ready((String::new(), ())).is_ready() {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs
new file mode 100644 (file)
index 0000000..b9c82d8
--- /dev/null
@@ -0,0 +1,58 @@
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+    let m = std::sync::Mutex::new((0, 0));
+
+    // Result
+    if let Ok(_) = m.lock() {}
+    if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+
+    {
+        if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+    }
+    if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+    } else {
+    }
+    if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+    if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+
+    if let Ok(_) = Ok::<_, ()>(String::new()) {}
+    if let Err(_) = Err::<(), _>((String::new(), ())) {}
+
+    // Option
+    if let Some(_) = Some(m.lock()) {}
+    if let Some(_) = Some(m.lock().unwrap().0) {}
+
+    {
+        if let None = None::<std::sync::MutexGuard<()>> {}
+    }
+    if let None = None::<std::sync::MutexGuard<()>> {
+    } else {
+    }
+
+    if let None = None::<std::sync::MutexGuard<()>> {}
+
+    if let Some(_) = Some(String::new()) {}
+    if let Some(_) = Some((String::new(), ())) {}
+
+    // Poll
+    if let Ready(_) = Ready(m.lock()) {}
+    if let Ready(_) = Ready(m.lock().unwrap().0) {}
+
+    {
+        if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+    }
+    if let Pending = Pending::<std::sync::MutexGuard<()>> {
+    } else {
+    }
+
+    if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+
+    if let Ready(_) = Ready(String::new()) {}
+    if let Ready(_) = Ready((String::new(), ())) {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr
new file mode 100644 (file)
index 0000000..eb7aa70
--- /dev/null
@@ -0,0 +1,171 @@
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:12:12
+   |
+LL |     if let Ok(_) = m.lock() {}
+   |     -------^^^^^----------- help: try this: `if m.lock().is_ok()`
+   |
+   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_err()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:13:12
+   |
+LL |     if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+   |     -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:16:16
+   |
+LL |         if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+   |         -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:18:12
+   |
+LL |     if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+   |     -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:21:12
+   |
+LL |     if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+   |     -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:22:12
+   |
+LL |     if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+   |     -------^^^^^^------------------------------------------ help: try this: `if Err::<std::sync::MutexGuard<()>, _>(()).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:24:12
+   |
+LL |     if let Ok(_) = Ok::<_, ()>(String::new()) {}
+   |     -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:25:12
+   |
+LL |     if let Err(_) = Err::<(), _>((String::new(), ())) {}
+   |     -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:28:12
+   |
+LL |     if let Some(_) = Some(m.lock()) {}
+   |     -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:29:12
+   |
+LL |     if let Some(_) = Some(m.lock().unwrap().0) {}
+   |     -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:32:16
+   |
+LL |         if let None = None::<std::sync::MutexGuard<()>> {}
+   |         -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:34:12
+   |
+LL |     if let None = None::<std::sync::MutexGuard<()>> {
+   |     -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:38:12
+   |
+LL |     if let None = None::<std::sync::MutexGuard<()>> {}
+   |     -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:40:12
+   |
+LL |     if let Some(_) = Some(String::new()) {}
+   |     -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:41:12
+   |
+LL |     if let Some(_) = Some((String::new(), ())) {}
+   |     -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()`
+
+error: redundant pattern matching, consider using `is_ready()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:44:12
+   |
+LL |     if let Ready(_) = Ready(m.lock()) {}
+   |     -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ready()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:45:12
+   |
+LL |     if let Ready(_) = Ready(m.lock().unwrap().0) {}
+   |     -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:48:16
+   |
+LL |         if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+   |         -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:50:12
+   |
+LL |     if let Pending = Pending::<std::sync::MutexGuard<()>> {
+   |     -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:54:12
+   |
+LL |     if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+   |     -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:56:12
+   |
+LL |     if let Ready(_) = Ready(String::new()) {}
+   |     -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:57:12
+   |
+LL |     if let Ready(_) = Ready((String::new(), ())) {}
+   |     -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()`
+
+error: aborting due to 22 previous errors
+
index 66f580a0a683426e250479c01e5a8a6c1ff86dd6..997144772669be4fdabd45a0834deebf1c8c3388 100644 (file)
@@ -2,7 +2,12 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    clippy::if_same_then_else
+)]
 
 fn main() {
     if None::<()>.is_none() {}
index f18b27b8b95c30b4cc33e5756e2d51d4bf5bcd75..8309847e18162796d787c4bb771bb5ffd7f58600 100644 (file)
@@ -2,7 +2,12 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    clippy::if_same_then_else
+)]
 
 fn main() {
     if let None = None::<()> {}
index 58482a0ab70dcbe0c4c1a2bef214861dcbd06d3a..613a30d4a48453bbede0d3c9f4386a7b304938a3 100644 (file)
@@ -1,5 +1,5 @@
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:8:12
+  --> $DIR/redundant_pattern_matching_option.rs:13:12
    |
 LL |     if let None = None::<()> {}
    |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
@@ -7,43 +7,43 @@ LL |     if let None = None::<()> {}
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:10:12
+  --> $DIR/redundant_pattern_matching_option.rs:15:12
    |
 LL |     if let Some(_) = Some(42) {}
    |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:12:12
+  --> $DIR/redundant_pattern_matching_option.rs:17:12
    |
 LL |     if let Some(_) = Some(42) {
    |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:18:15
+  --> $DIR/redundant_pattern_matching_option.rs:23:15
    |
 LL |     while let Some(_) = Some(42) {}
    |     ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:20:15
+  --> $DIR/redundant_pattern_matching_option.rs:25:15
    |
 LL |     while let None = Some(42) {}
    |     ----------^^^^----------- help: try this: `while Some(42).is_none()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:22:15
+  --> $DIR/redundant_pattern_matching_option.rs:27:15
    |
 LL |     while let None = None::<()> {}
    |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:25:15
+  --> $DIR/redundant_pattern_matching_option.rs:30:15
    |
 LL |     while let Some(_) = v.pop() {
    |     ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:33:5
+  --> $DIR/redundant_pattern_matching_option.rs:38:5
    |
 LL | /     match Some(42) {
 LL | |         Some(_) => true,
@@ -52,7 +52,7 @@ LL | |     };
    | |_____^ help: try this: `Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:38:5
+  --> $DIR/redundant_pattern_matching_option.rs:43:5
    |
 LL | /     match None::<()> {
 LL | |         Some(_) => false,
@@ -61,7 +61,7 @@ LL | |     };
    | |_____^ help: try this: `None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:43:13
+  --> $DIR/redundant_pattern_matching_option.rs:48:13
    |
 LL |       let _ = match None::<()> {
    |  _____________^
@@ -71,49 +71,49 @@ LL | |     };
    | |_____^ help: try this: `None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:49:20
+  --> $DIR/redundant_pattern_matching_option.rs:54:20
    |
 LL |     let _ = if let Some(_) = opt { true } else { false };
    |             -------^^^^^^^------ help: try this: `if opt.is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:53:20
+  --> $DIR/redundant_pattern_matching_option.rs:58:20
    |
 LL |     let _ = if let Some(_) = gen_opt() {
    |             -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:55:19
+  --> $DIR/redundant_pattern_matching_option.rs:60:19
    |
 LL |     } else if let None = gen_opt() {
    |            -------^^^^------------ help: try this: `if gen_opt().is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:74:12
+  --> $DIR/redundant_pattern_matching_option.rs:79:12
    |
 LL |     if let Some(_) = Some(42) {}
    |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:76:12
+  --> $DIR/redundant_pattern_matching_option.rs:81:12
    |
 LL |     if let None = None::<()> {}
    |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:78:15
+  --> $DIR/redundant_pattern_matching_option.rs:83:15
    |
 LL |     while let Some(_) = Some(42) {}
    |     ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:80:15
+  --> $DIR/redundant_pattern_matching_option.rs:85:15
    |
 LL |     while let None = None::<()> {}
    |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:82:5
+  --> $DIR/redundant_pattern_matching_option.rs:87:5
    |
 LL | /     match Some(42) {
 LL | |         Some(_) => true,
@@ -122,7 +122,7 @@ LL | |     };
    | |_____^ help: try this: `Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:87:5
+  --> $DIR/redundant_pattern_matching_option.rs:92:5
    |
 LL | /     match None::<()> {
 LL | |         Some(_) => false,
index 465aa80dac27c4512eef6b6011dbe4840fea1f22..c297745380404030beee5b4487083387349af196 100644 (file)
@@ -2,7 +2,12 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    clippy::if_same_then_else
+)]
 
 use std::task::Poll::{self, Pending, Ready};
 
index 7891ff353b13ee5d9ba4c81a5bde2004a3c68dde..665c8c417504dbb1b54b4c48e1ef39216c6d6a83 100644 (file)
@@ -2,7 +2,12 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    clippy::if_same_then_else
+)]
 
 use std::task::Poll::{self, Pending, Ready};
 
index 5ffc6c47c90a220691a4069329a5ec0a3b279167..5ecf024a733a31a376e589f87898357995aceb97 100644 (file)
@@ -1,5 +1,5 @@
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:10:12
+  --> $DIR/redundant_pattern_matching_poll.rs:15:12
    |
 LL |     if let Pending = Pending::<()> {}
    |     -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
@@ -7,37 +7,37 @@ LL |     if let Pending = Pending::<()> {}
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:12:12
+  --> $DIR/redundant_pattern_matching_poll.rs:17:12
    |
 LL |     if let Ready(_) = Ready(42) {}
    |     -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:14:12
+  --> $DIR/redundant_pattern_matching_poll.rs:19:12
    |
 LL |     if let Ready(_) = Ready(42) {
    |     -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:20:15
+  --> $DIR/redundant_pattern_matching_poll.rs:25:15
    |
 LL |     while let Ready(_) = Ready(42) {}
    |     ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:22:15
+  --> $DIR/redundant_pattern_matching_poll.rs:27:15
    |
 LL |     while let Pending = Ready(42) {}
    |     ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:24:15
+  --> $DIR/redundant_pattern_matching_poll.rs:29:15
    |
 LL |     while let Pending = Pending::<()> {}
    |     ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:30:5
+  --> $DIR/redundant_pattern_matching_poll.rs:35:5
    |
 LL | /     match Ready(42) {
 LL | |         Ready(_) => true,
@@ -46,7 +46,7 @@ LL | |     };
    | |_____^ help: try this: `Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:35:5
+  --> $DIR/redundant_pattern_matching_poll.rs:40:5
    |
 LL | /     match Pending::<()> {
 LL | |         Ready(_) => false,
@@ -55,7 +55,7 @@ LL | |     };
    | |_____^ help: try this: `Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:40:13
+  --> $DIR/redundant_pattern_matching_poll.rs:45:13
    |
 LL |       let _ = match Pending::<()> {
    |  _____________^
@@ -65,49 +65,49 @@ LL | |     };
    | |_____^ help: try this: `Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:46:20
+  --> $DIR/redundant_pattern_matching_poll.rs:51:20
    |
 LL |     let _ = if let Ready(_) = poll { true } else { false };
    |             -------^^^^^^^^------- help: try this: `if poll.is_ready()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:50:20
+  --> $DIR/redundant_pattern_matching_poll.rs:55:20
    |
 LL |     let _ = if let Ready(_) = gen_poll() {
    |             -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:52:19
+  --> $DIR/redundant_pattern_matching_poll.rs:57:19
    |
 LL |     } else if let Pending = gen_poll() {
    |            -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:68:12
+  --> $DIR/redundant_pattern_matching_poll.rs:73:12
    |
 LL |     if let Ready(_) = Ready(42) {}
    |     -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:70:12
+  --> $DIR/redundant_pattern_matching_poll.rs:75:12
    |
 LL |     if let Pending = Pending::<()> {}
    |     -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:72:15
+  --> $DIR/redundant_pattern_matching_poll.rs:77:15
    |
 LL |     while let Ready(_) = Ready(42) {}
    |     ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:74:15
+  --> $DIR/redundant_pattern_matching_poll.rs:79:15
    |
 LL |     while let Pending = Pending::<()> {}
    |     ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:76:5
+  --> $DIR/redundant_pattern_matching_poll.rs:81:5
    |
 LL | /     match Ready(42) {
 LL | |         Ready(_) => true,
@@ -116,7 +116,7 @@ LL | |     };
    | |_____^ help: try this: `Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:81:5
+  --> $DIR/redundant_pattern_matching_poll.rs:86:5
    |
 LL | /     match Pending::<()> {
 LL | |         Ready(_) => false,
index e94c5704b48917fd89667bed89cd3bb52896ab98..d7af5d762ae4a9f8485feba9e4ae915c95f76100 100644 (file)
@@ -7,7 +7,8 @@
     clippy::needless_bool,
     clippy::match_like_matches_macro,
     clippy::unnecessary_wraps,
-    deprecated
+    deprecated,
+    clippy::if_same_then_else
 )]
 
 fn main() {
index 5d1752942322b0b19aa22d5bb46227bea1bb4eca..e06d4485ae4f295e98ff223ebb04a48084c07ffe 100644 (file)
@@ -7,7 +7,8 @@
     clippy::needless_bool,
     clippy::match_like_matches_macro,
     clippy::unnecessary_wraps,
-    deprecated
+    deprecated,
+    clippy::if_same_then_else
 )]
 
 fn main() {
index d6a46babb77952de2a4d3f602841b77172bef6d6..e06f095da20c6a723f19ef019ca64de33684072b 100644 (file)
@@ -1,5 +1,5 @@
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:15:12
+  --> $DIR/redundant_pattern_matching_result.rs:16:12
    |
 LL |     if let Ok(_) = &result {}
    |     -------^^^^^---------- help: try this: `if result.is_ok()`
@@ -7,31 +7,31 @@ LL |     if let Ok(_) = &result {}
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:17:12
+  --> $DIR/redundant_pattern_matching_result.rs:18:12
    |
 LL |     if let Ok(_) = Ok::<i32, i32>(42) {}
    |     -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:19:12
+  --> $DIR/redundant_pattern_matching_result.rs:20:12
    |
 LL |     if let Err(_) = Err::<i32, i32>(42) {}
    |     -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:21:15
+  --> $DIR/redundant_pattern_matching_result.rs:22:15
    |
 LL |     while let Ok(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:23:15
+  --> $DIR/redundant_pattern_matching_result.rs:24:15
    |
 LL |     while let Err(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:33:5
+  --> $DIR/redundant_pattern_matching_result.rs:34:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -40,7 +40,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:38:5
+  --> $DIR/redundant_pattern_matching_result.rs:39:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -49,7 +49,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:43:5
+  --> $DIR/redundant_pattern_matching_result.rs:44:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -58,7 +58,7 @@ LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:48:5
+  --> $DIR/redundant_pattern_matching_result.rs:49:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -67,73 +67,73 @@ LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:53:20
+  --> $DIR/redundant_pattern_matching_result.rs:54:20
    |
 LL |     let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
    |             -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:59:20
+  --> $DIR/redundant_pattern_matching_result.rs:60:20
    |
 LL |     let _ = if let Ok(_) = gen_res() {
    |             -------^^^^^------------ help: try this: `if gen_res().is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:61:19
+  --> $DIR/redundant_pattern_matching_result.rs:62:19
    |
 LL |     } else if let Err(_) = gen_res() {
    |            -------^^^^^^------------ help: try this: `if gen_res().is_err()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_result.rs:84:19
+  --> $DIR/redundant_pattern_matching_result.rs:85:19
    |
 LL |         while let Some(_) = r#try!(result_opt()) {}
    |         ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_result.rs:85:16
+  --> $DIR/redundant_pattern_matching_result.rs:86:16
    |
 LL |         if let Some(_) = r#try!(result_opt()) {}
    |         -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_result.rs:91:12
+  --> $DIR/redundant_pattern_matching_result.rs:92:12
    |
 LL |     if let Some(_) = m!() {}
    |     -------^^^^^^^------- help: try this: `if m!().is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_result.rs:92:15
+  --> $DIR/redundant_pattern_matching_result.rs:93:15
    |
 LL |     while let Some(_) = m!() {}
    |     ----------^^^^^^^------- help: try this: `while m!().is_some()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:110:12
+  --> $DIR/redundant_pattern_matching_result.rs:111:12
    |
 LL |     if let Ok(_) = Ok::<i32, i32>(42) {}
    |     -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:112:12
+  --> $DIR/redundant_pattern_matching_result.rs:113:12
    |
 LL |     if let Err(_) = Err::<i32, i32>(42) {}
    |     -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:114:15
+  --> $DIR/redundant_pattern_matching_result.rs:115:15
    |
 LL |     while let Ok(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:116:15
+  --> $DIR/redundant_pattern_matching_result.rs:117:15
    |
 LL |     while let Err(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:118:5
+  --> $DIR/redundant_pattern_matching_result.rs:119:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -142,7 +142,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:123:5
+  --> $DIR/redundant_pattern_matching_result.rs:124:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => false,
index ad5b8e4857d173f50bcbcd2bc9565e8b6c2d5b6a..6c7e2b854dc14b3379cc8b6448662861d4a88d02 100644 (file)
@@ -1,5 +1,5 @@
-#![allow(deprecated, invalid_value)]
-#![warn(clippy::all)]
+#![allow(deprecated, invalid_value, clippy::uninit_assumed_init)]
+#![warn(clippy::mem_replace_with_uninit)]
 
 use std::mem;
 
index a7a8499b58f000a58e163bed66534e50a2d38ea0..f66b445b7b6a35249600338185df6d6bb7774bb6 100644 (file)
@@ -19,3 +19,16 @@ fn main() {
     // False positive #5154, shouldn't trigger lint.
     m!();
 }
+
+mod hello_mod {
+    
+    #[allow(dead_code)]
+    fn hello_mod() {}
+}
+
+mod hi_mod {
+    use self::regex::{Regex, RegexSet};
+    use regex;
+    #[allow(dead_code)]
+    fn hi_mod() {}
+}
index 9a427e90ad3df619a43491aaae91be5f83a418ba..09d4865859584ed792063e9e6bb510a1b679c012 100644 (file)
@@ -19,3 +19,16 @@ fn main() {
     // False positive #5154, shouldn't trigger lint.
     m!();
 }
+
+mod hello_mod {
+    use regex;
+    #[allow(dead_code)]
+    fn hello_mod() {}
+}
+
+mod hi_mod {
+    use self::regex::{Regex, RegexSet};
+    use regex;
+    #[allow(dead_code)]
+    fn hi_mod() {}
+}
index 519ada0169a6d89ee87e2f37db80790d19512c30..7005fa8f125d36255c12bf6c8b93cf6644f64eba 100644 (file)
@@ -1,10 +1,16 @@
+error: this import is redundant
+  --> $DIR/single_component_path_imports.rs:24:5
+   |
+LL |     use regex;
+   |     ^^^^^^^^^^ help: remove it entirely
+   |
+   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
 error: this import is redundant
   --> $DIR/single_component_path_imports.rs:6:1
    |
 LL | use regex;
    | ^^^^^^^^^^ help: remove it entirely
-   |
-   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed b/src/tools/clippy/tests/ui/single_component_path_imports_macro.fixed
new file mode 100644 (file)
index 0000000..05863f9
--- /dev/null
@@ -0,0 +1,21 @@
+// run-rustfix
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+
+macro_rules! m1 {
+    () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+    () => {};
+}
+ // fail
+
+fn main() {
+    m1!();
+    m2!();
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs b/src/tools/clippy/tests/ui/single_component_path_imports_macro.rs
new file mode 100644 (file)
index 0000000..633deea
--- /dev/null
@@ -0,0 +1,21 @@
+// run-rustfix
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+
+macro_rules! m1 {
+    () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+    () => {};
+}
+use m2; // fail
+
+fn main() {
+    m1!();
+    m2!();
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr b/src/tools/clippy/tests/ui/single_component_path_imports_macro.stderr
new file mode 100644 (file)
index 0000000..239efb3
--- /dev/null
@@ -0,0 +1,10 @@
+error: this import is redundant
+  --> $DIR/single_component_path_imports_macro.rs:16:1
+   |
+LL | use m2; // fail
+   | ^^^^^^^ help: remove it entirely
+   |
+   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs
new file mode 100644 (file)
index 0000000..9411706
--- /dev/null
@@ -0,0 +1,17 @@
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+use serde as edres;
+pub use serde;
+
+fn main() {
+    regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+}
+
+mod root_nested_use_mod {
+    use {regex, serde};
+    #[allow(dead_code)]
+    fn root_nested_use_mod() {}
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr
new file mode 100644 (file)
index 0000000..0c3256c
--- /dev/null
@@ -0,0 +1,25 @@
+error: this import is redundant
+  --> $DIR/single_component_path_imports_nested_first.rs:14:10
+   |
+LL |     use {regex, serde};
+   |          ^^^^^
+   |
+   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+   = help: remove this import
+
+error: this import is redundant
+  --> $DIR/single_component_path_imports_nested_first.rs:14:17
+   |
+LL |     use {regex, serde};
+   |                 ^^^^^
+   |
+   = help: remove this import
+
+error: this import is redundant
+  --> $DIR/single_component_path_imports_nested_first.rs:5:1
+   |
+LL | use regex;
+   | ^^^^^^^^^^ help: remove it entirely
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs b/src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs
new file mode 100644 (file)
index 0000000..94319ad
--- /dev/null
@@ -0,0 +1,16 @@
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+    regex::{Regex, RegexSet},
+    some_mod::SomeType,
+};
+use regex;
+
+mod some_mod {
+    pub struct SomeType;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs b/src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs
new file mode 100644 (file)
index 0000000..c7437b2
--- /dev/null
@@ -0,0 +1,17 @@
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+    regex::{Regex, RegexSet},
+    some_mod::SomeType,
+};
+
+mod some_mod {
+    pub struct SomeType;
+}
+
+fn main() {}
index 8ca068293a611455cd246bd1d5a16b73456027c7..c307afffcb86325b7dac857ef288042e1a5bdce9 100644 (file)
@@ -8,4 +8,9 @@ fn main() {
         let item = &item1;
         println!("{}", item);
     }
+
+    {
+        let item = &item1;
+        println!("{:?}", item);
+    }
 }
index 57e9336a31fcf6c3d775273f7c7a78bedee5c0cf..2c0c03b7211993ee22d5c5022818eec6842cb283 100644 (file)
@@ -7,4 +7,8 @@ fn main() {
     for item in &[item1] {
         println!("{}", item);
     }
+
+    for item in [item1].iter() {
+        println!("{:?}", item);
+    }
 }
index 90be1dc328371f6bc611cc236bd14b31f600d6e1..0e35a33ded5ba0fc34a7aeaf46623630b74aaaa9 100644 (file)
@@ -15,5 +15,21 @@ LL |         println!("{}", item);
 LL |     }
    |
 
-error: aborting due to previous error
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:11:5
+   |
+LL | /     for item in [item1].iter() {
+LL | |         println!("{:?}", item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL |     {
+LL |         let item = &item1;
+LL |         println!("{:?}", item);
+LL |     }
+   |
+
+error: aborting due to 2 previous errors
 
index ca884b41c45792ae0a6a16d39802b06b3c6fb070..b1819e08d53bf5dc9054e41cde25c580077082c5 100644 (file)
@@ -135,6 +135,14 @@ impl Eq for Bar {}
         Bar::A => println!(),
         _ => (),
     }
+
+    // issue #7038
+    struct X;
+    let x = Some(X);
+    match x {
+        None => println!(),
+        _ => (),
+    };
 }
 
 macro_rules! single_match {
index 7ea6955b7401e4ecb17ca4995b820a10bf4bb5c3..9ef2a8668a6faffdcb9e92757bc742b597e0db1a 100644 (file)
@@ -119,5 +119,14 @@ LL | |         _ => (),
 LL | |     }
    | |_____^ help: try this: `if let Bar::A = x { println!() }`
 
-error: aborting due to 12 previous errors
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match.rs:142:5
+   |
+LL | /     match x {
+LL | |         None => println!(),
+LL | |         _ => (),
+LL | |     };
+   | |_____^ help: try this: `if let None = x { println!() }`
+
+error: aborting due to 13 previous errors
 
index dd22bfa5c53ef43e847237bbfc8abc554259b0ad..df2256e4f97de98afe0285e587ac9924c000fb3a 100644 (file)
@@ -22,7 +22,7 @@ fn str_lit_as_bytes() {
 
     let current_version = env!("CARGO_PKG_VERSION").as_bytes();
 
-    let includestr = include_bytes!("entry_unfixable.rs");
+    let includestr = include_bytes!("string_lit_as_bytes.rs");
 
     let _ = b"string with newline\t\n";
 }
index d2a710ed6b8ca5f575ac836d3ec80f5d563dbf1d..c6bf8f732ed9f615d1ea2d660d6ed7602513270b 100644 (file)
@@ -22,7 +22,7 @@ fn str_lit_as_bytes() {
 
     let current_version = env!("CARGO_PKG_VERSION").as_bytes();
 
-    let includestr = include_str!("entry_unfixable.rs").as_bytes();
+    let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
 
     let _ = "string with newline\t\n".as_bytes();
 }
index e0ddb070b504456eb8e6f237c9f30e0c5e273d5e..f47d6161c6cf2c8869b90783eebd684581ee9a91 100644 (file)
@@ -27,8 +27,8 @@ LL |     let bs = "lit to owned".to_owned().into_bytes();
 error: calling `as_bytes()` on `include_str!(..)`
   --> $DIR/string_lit_as_bytes.rs:25:22
    |
-LL |     let includestr = include_str!("entry_unfixable.rs").as_bytes();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")`
+LL |     let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("string_lit_as_bytes.rs")`
 
 error: calling `as_bytes()` on a string literal
   --> $DIR/string_lit_as_bytes.rs:27:13
index 226010ec6df3a24c63898f53a81c227b8a3781af..547615b10d9fb11741e2d9e59a1dbc2cc26f53cd 100644 (file)
@@ -40,6 +40,7 @@ fn main() {
     {
     }
 
+    // This is fine, though weird. Allman style braces on the else.
     if foo() {
     }
     else
@@ -76,4 +77,29 @@ fn main() {
     }
     if foo() {
     }
+
+    // Almost Allman style braces. Lint these.
+    if foo() {
+    }
+
+    else
+    {
+
+    }
+
+    if foo() {
+    }
+    else
+
+    {
+
+    }
+
+    // #3864 - Allman style braces
+    if foo()
+    {
+    }
+    else
+    {
+    }
 }
index bbc036d376fefc9ead1a8cb034eceb23f3a728c5..d8d67b4138ab3b95c9f107c4265ead6548416846 100644 (file)
@@ -41,37 +41,50 @@ LL | |     {
    |
    = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
 
-error: this is an `else {..}` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:44:6
+error: this is an `else if` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:51:6
    |
-LL |       }
+LL |       } else
    |  ______^
-LL | |     else
-LL | |     {
+LL | |     if foo() { // the span of the above error should continue here
    | |____^
    |
-   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
 
 error: this is an `else if` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:50:6
+  --> $DIR/suspicious_else_formatting.rs:56:6
    |
-LL |       } else
+LL |       }
    |  ______^
+LL | |     else
 LL | |     if foo() { // the span of the above error should continue here
    | |____^
    |
    = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
 
-error: this is an `else if` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:55:6
+error: this is an `else {..}` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:83:6
    |
 LL |       }
    |  ______^
+LL | |
 LL | |     else
-LL | |     if foo() { // the span of the above error should continue here
+LL | |     {
    | |____^
    |
-   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else {..}` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:91:6
+   |
+LL |       }
+   |  ______^
+LL | |     else
+LL | |
+LL | |     {
+   | |____^
+   |
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
 
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
 
index 5b96bb59c5f188beda95e9f595bb496114dd7706..264194419c7396680250c34fefb7ed935e0dc639 100644 (file)
@@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
 
     Poll::Ready(None)
 }
+
+// Tests that `return` is not duplicated
+pub fn try_return(x: bool) -> Result<i32, i32> {
+    if x {
+        return Err(42);
+    }
+    Ok(0)
+}
index f220d697d2cd7e5e0592596605bb0b038ef6a468..bc6979bf457199e79271e9bd3bc0a12580d242ea 100644 (file)
@@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
 
     Poll::Ready(None)
 }
+
+// Tests that `return` is not duplicated
+pub fn try_return(x: bool) -> Result<i32, i32> {
+    if x {
+        return Err(42)?;
+    }
+    Ok(0)
+}
index 2c01d37192e8e296439258134367a1c37a5c6c63..8f332a9b6492caf32c24c28edf46fec5c03eaa1d 100644 (file)
@@ -74,5 +74,11 @@ error: returning an `Err(_)` with the `?` operator
 LL |         Err(io::ErrorKind::NotFound)?
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`
 
-error: aborting due to 10 previous errors
+error: returning an `Err(_)` with the `?` operator
+  --> $DIR/try_err.rs:167:16
+   |
+LL |         return Err(42)?;
+   |                ^^^^^^^^ help: try this: `Err(42)`
+
+error: aborting due to 11 previous errors
 
index f42b884e0f0e546ce0bb95dd5793fbb85dda4e34..1ed3883c1f06059285c858c40323eec9b069de77 100644 (file)
@@ -1,6 +1,6 @@
 #![feature(stmt_expr_attributes)]
 
-use std::mem::MaybeUninit;
+use std::mem::{self, MaybeUninit};
 
 fn main() {
     let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
@@ -19,4 +19,7 @@ fn main() {
 
     // This is OK, because all constitutent types are uninit-compatible.
     let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() };
+
+    // Was a false negative.
+    let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
 }
index a37233ecddaee0dd4181bb95e80ecc347d9210e2..85b64a8419ab021bd36585ebde6e9431e84301e6 100644 (file)
@@ -12,5 +12,11 @@ error: this call for this type may be undefined behavior
 LL |     let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: this call for this type may be undefined behavior
+  --> $DIR/uninit.rs:24:29
+   |
+LL |     let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.fixed b/src/tools/clippy/tests/ui/unnecessary_self_imports.fixed
new file mode 100644 (file)
index 0000000..1185eaa
--- /dev/null
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::unnecessary_self_imports)]
+#![allow(unused_imports, dead_code)]
+
+use std::collections::hash_map::{self, *};
+use std::fs as alias;
+use std::io::{self, Read};
+use std::rc;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.rs b/src/tools/clippy/tests/ui/unnecessary_self_imports.rs
new file mode 100644 (file)
index 0000000..56bfbc0
--- /dev/null
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::unnecessary_self_imports)]
+#![allow(unused_imports, dead_code)]
+
+use std::collections::hash_map::{self, *};
+use std::fs::{self as alias};
+use std::io::{self, Read};
+use std::rc::{self};
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.stderr b/src/tools/clippy/tests/ui/unnecessary_self_imports.stderr
new file mode 100644 (file)
index 0000000..83a5618
--- /dev/null
@@ -0,0 +1,23 @@
+error: import ending with `::{self}`
+  --> $DIR/unnecessary_self_imports.rs:6:1
+   |
+LL | use std::fs::{self as alias};
+   | ^^^^^^^^^--------------------
+   |          |
+   |          help: consider omitting `::{self}`: `fs as alias;`
+   |
+   = note: `-D clippy::unnecessary-self-imports` implied by `-D warnings`
+   = note: this will slightly change semantics; any non-module items at the same path will also be imported
+
+error: import ending with `::{self}`
+  --> $DIR/unnecessary_self_imports.rs:8:1
+   |
+LL | use std::rc::{self};
+   | ^^^^^^^^^-----------
+   |          |
+   |          help: consider omitting `::{self}`: `rc;`
+   |
+   = note: this will slightly change semantics; any non-module items at the same path will also be imported
+
+error: aborting due to 2 previous errors
+
index ebaba9629db16d515367030d40d31f792754b057..8b141e25942d7f74a6eb90685def758340ce46ee 100644 (file)
@@ -1,7 +1,7 @@
 #![allow(dead_code)]
 #![warn(clippy::unused_io_amount)]
 
-use std::io;
+use std::io::{self, Read};
 
 fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
     s.write(b"test")?;
@@ -22,4 +22,43 @@ fn vectored<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
     Ok(())
 }
 
+fn ok(file: &str) -> Option<()> {
+    let mut reader = std::fs::File::open(file).ok()?;
+    let mut result = [0u8; 0];
+    reader.read(&mut result).ok()?;
+    Some(())
+}
+
+#[allow(clippy::redundant_closure)]
+#[allow(clippy::bind_instead_of_map)]
+fn or_else(file: &str) -> io::Result<()> {
+    let mut reader = std::fs::File::open(file)?;
+    let mut result = [0u8; 0];
+    reader.read(&mut result).or_else(|err| Err(err))?;
+    Ok(())
+}
+
+#[derive(Debug)]
+enum Error {
+    Kind,
+}
+
+fn or(file: &str) -> Result<(), Error> {
+    let mut reader = std::fs::File::open(file).unwrap();
+    let mut result = [0u8; 0];
+    reader.read(&mut result).or(Err(Error::Kind))?;
+    Ok(())
+}
+
+fn combine_or(file: &str) -> Result<(), Error> {
+    let mut reader = std::fs::File::open(file).unwrap();
+    let mut result = [0u8; 0];
+    reader
+        .read(&mut result)
+        .or(Err(Error::Kind))
+        .or(Err(Error::Kind))
+        .expect("error");
+    Ok(())
+}
+
 fn main() {}
index 5219d63980b4b164f2f9411a59fac8d95a55ccd1..d8dfc0e5a798c53edd720d8583f0a2d0745d2206 100644 (file)
@@ -36,5 +36,33 @@ error: written amount is not handled
 LL |     s.write_vectored(&[io::IoSlice::new(&[])])?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 6 previous errors
+error: read amount is not handled. Use `Read::read_exact` instead
+  --> $DIR/unused_io_amount.rs:28:5
+   |
+LL |     reader.read(&mut result).ok()?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+  --> $DIR/unused_io_amount.rs:37:5
+   |
+LL |     reader.read(&mut result).or_else(|err| Err(err))?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+  --> $DIR/unused_io_amount.rs:49:5
+   |
+LL |     reader.read(&mut result).or(Err(Error::Kind))?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+  --> $DIR/unused_io_amount.rs:56:5
+   |
+LL | /     reader
+LL | |         .read(&mut result)
+LL | |         .or(Err(Error::Kind))
+LL | |         .or(Err(Error::Kind))
+LL | |         .expect("error");
+   | |________________________^
+
+error: aborting due to 10 previous errors
 
index fd754e4c794f63416a1a496649eec7ff7e5d4126..129d82652d75915f55dc7907e52fddfbd08a0b9d 100644 (file)
@@ -77,7 +77,7 @@ fn main() {
     let error_kind = ErrorKind::NotFound;
     match error_kind {
         ErrorKind::NotFound => {},
-        ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _ => {},
+        ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _ => {},
     }
     match error_kind {
         ErrorKind::NotFound => {},
@@ -98,6 +98,7 @@ fn main() {
         ErrorKind::Interrupted => {},
         ErrorKind::Other => {},
         ErrorKind::UnexpectedEof => {},
+        ErrorKind::Unsupported => {},
         _ => {},
     }
 }
index 2dbf726d5d07236801f9fee0441f3c2e6b15b12f..028ecb63e7e6dcc7dddf75c0b170ef944d65e054 100644 (file)
@@ -98,6 +98,7 @@ fn main() {
         ErrorKind::Interrupted => {},
         ErrorKind::Other => {},
         ErrorKind::UnexpectedEof => {},
+        ErrorKind::Unsupported => {},
         _ => {},
     }
 }
index a513a62c748d6a7a683e70c9721ff93289f6b59c..fd45cad00d6b51bd6c5cc980957b46439899f9d8 100644 (file)
@@ -32,7 +32,7 @@ error: wildcard matches known variants and will also match future added variants
   --> $DIR/wildcard_enum_match_arm.rs:80:9
    |
 LL |         _ => {},
-   |         ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _`
+   |         ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _`
 
 error: aborting due to 5 previous errors
 
index 8b42aa59e1323ecb3552a56be6475bdad8bd2a6f..ae3a740d405d6fc103c8b2f33f8381cb6d6584cf 100644 (file)
@@ -30,3 +30,15 @@ fn to_u64(self) -> u64 {
         }
     }
 }
+
+mod issue7032 {
+    trait Foo {
+        fn from_usize(x: usize) -> Self;
+    }
+    // don't trigger
+    impl Foo for usize {
+        fn from_usize(x: usize) -> Self {
+            x
+        }
+    }
+}
index 082cb35c2e0349b012e5d052c7ce6b73bec82b48..27ecb532dd00e97bbaa2672049c03e64af9e5ef3 100644 (file)
             opacity: 30%;
         }
 
-        p > code {
+        :not(pre) > code {
             color: var(--inline-code-color);
             background-color: var(--inline-code-bg);
         }
index 363105a9f09c04079ce8a8d4f84a20465640eba0..f31a24738df6c7bae1312e96f037b3bdab330df9 100644 (file)
@@ -136,9 +136,7 @@ pub fn from_reader<R: Read>(config: &Config, testfile: &Path, rdr: R) -> Self {
                 props.aux_crate.push(ac);
             }
 
-            if let Some(r) = config.parse_revisions(ln) {
-                props.revisions.extend(r);
-            }
+            config.parse_and_update_revisions(ln, &mut props.revisions);
 
             props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail");
         });
@@ -432,9 +430,7 @@ fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
                     self.compile_flags.push(format!("--edition={}", edition));
                 }
 
-                if let Some(r) = config.parse_revisions(ln) {
-                    self.revisions.extend(r);
-                }
+                config.parse_and_update_revisions(ln, &mut self.revisions);
 
                 if self.run_flags.is_none() {
                     self.run_flags = config.parse_run_flags(ln);
@@ -723,9 +719,16 @@ fn parse_compile_flags(&self, line: &str) -> Option<String> {
         self.parse_name_value_directive(line, "compile-flags")
     }
 
-    fn parse_revisions(&self, line: &str) -> Option<Vec<String>> {
-        self.parse_name_value_directive(line, "revisions")
-            .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
+    fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) {
+        if let Some(raw) = self.parse_name_value_directive(line, "revisions") {
+            let mut duplicates: HashSet<_> = existing.iter().cloned().collect();
+            for revision in raw.split_whitespace().map(|r| r.to_string()) {
+                if !duplicates.insert(revision.clone()) {
+                    panic!("Duplicate revision: `{}` in line `{}`", revision, raw);
+                }
+                existing.push(revision);
+            }
+        }
     }
 
     fn parse_run_flags(&self, line: &str) -> Option<String> {
index c41b43cdd0b53c34283c0226dd5c48788cadf9ee..ca7458d255c3c2db552441697be297c115987d62 100644 (file)
@@ -248,3 +248,10 @@ fn test_extract_version_range() {
     assert_eq!(extract_version_range("   - 4.5.6", extract_llvm_version), None);
     assert_eq!(extract_version_range("0  -", extract_llvm_version), None);
 }
+
+#[test]
+#[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")]
+fn test_duplicate_revisions() {
+    let config = config();
+    parse_rs(&config, "// revisions: rpass1 rpass1");
+}
index 480916018619dfb6b9fbcaed3e00b90df2da252f..d1798a52df7c4b021b3abf456155f1c792a2b8cd 100644 (file)
@@ -975,7 +975,7 @@ fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> {
         }
     } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
         if let Some(idx) = lldb_ver.find(not_a_digit) {
-            let version: u32 = lldb_ver[..idx].parse().unwrap();
+            let version: u32 = lldb_ver[..idx].parse().ok()?;
             return Some((version * 100, full_version_line.contains("rust-enabled")));
         }
     }
index b13b79db73e3fa692fc648a8cd70f162a5eade34..5faf5a5ca059f6eb067fc86e47480f5668ac6e8c 160000 (submodule)
@@ -1 +1 @@
-Subproject commit b13b79db73e3fa692fc648a8cd70f162a5eade34
+Subproject commit 5faf5a5ca059f6eb067fc86e47480f5668ac6e8c
index 32c0fe006dcdc13e1ca0ca31de543e4436c1299e..74d1800c25498689c5b5120a1e8495fce0cd0d0d 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 32c0fe006dcdc13e1ca0ca31de543e4436c1299e
+Subproject commit 74d1800c25498689c5b5120a1e8495fce0cd0d0d
index 7be06139b632ee615fc18af04dd67947e2c794b2..617535393bb5ccc7adf0bac8a3b9a9c306454e79 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 7be06139b632ee615fc18af04dd67947e2c794b2
+Subproject commit 617535393bb5ccc7adf0bac8a3b9a9c306454e79
index 8da7db2dfddb45f0f2c3c936e6441d902dff856f..d39cb597b21c8af1b404599a88c3e9710bb3eaec 100644 (file)
@@ -63,15 +63,15 @@ features = [
 [dependencies]
 byteorder = { version = "1", features = ['default', 'std'] }
 curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true }
-crossbeam-utils = { version = "0.7.2", features = ["nightly"] }
+crossbeam-utils = { version = "0.8.0", features = ["nightly"] }
 libc = { version = "0.2.79", features = ["align"] }
 # Ensure default features of libz-sys, which are disabled in some scenarios.
 libz-sys = { version = "1.1.2" }
 proc-macro2 = { version = "1", features = ["default"] }
 quote = { version = "1", features = ["default"] }
+rand_core_0_5 = { package = "rand_core", version = "0.5.1", features = ["getrandom", "alloc", "std"] }
 serde = { version = "1.0.82", features = ['derive'] }
 serde_json = { version = "1.0.31", features = ["raw_value", "unbounded_depth"] }
-smallvec-0_6 = { package = "smallvec", version = "0.6.14", features = ['union', 'may_dangle'] }
 smallvec = { version = "1.6.1", features = ['union', 'may_dangle'] }
 syn = { version = "1", features = ['fold', 'full', 'extra-traits', 'visit', 'visit-mut'] }
 url = { version = "2.0", features = ['serde'] }
index a551a97bda55d64ba4dd9f22dd0c8024207c6eec..e583bd225a9eb164fe9901e3353712faeda67d9b 100644 (file)
@@ -246,7 +246,7 @@ function lookForEntry(entry, data) {
     return null;
 }
 
-function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) {
+function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
     if (searchIndex[searchIndex.length - 1].length === 0) {
         searchIndex.pop();
     }
@@ -270,9 +270,9 @@ function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) {
     ALIASES = {};
     finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n';
     finalJS += loadThings(["hasOwnProperty", "onEach"], 'function', extractFunction, storageJs);
-    finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs);
-    finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs);
-    finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs);
+    finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs);
+    finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs);
+    finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs);
 
     var loaded = loadContent(finalJS);
     var index = loaded.buildIndex(searchIndex.rawSearchIndex);
@@ -382,12 +382,12 @@ function runChecks(testFile, loaded, index) {
 }
 
 function load_files(doc_folder, resource_suffix, crate) {
-    var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js"));
+    var searchJs = readFile(path.join(doc_folder, "search" + resource_suffix + ".js"));
     var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js"));
     var searchIndex = readFile(
         path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n");
 
-    return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate);
+    return loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate);
 }
 
 function showHelp() {
index 51f48d8f2ecc7b9a0d6c9357085e841fdfe0f191..5a843ea61ec6f72335cdf0df44340811ae27421e 100644 (file)
     ("ryu", "Apache-2.0 OR BSL-1.0"),   // rls/cargo/... (because of serde)
     ("bytesize", "Apache-2.0"),         // cargo
     ("im-rc", "MPL-2.0+"),              // cargo
-    ("constant_time_eq", "CC0-1.0"),    // rustfmt
     ("sized-chunks", "MPL-2.0+"),       // cargo via im-rc
     ("bitmaps", "MPL-2.0+"),            // cargo via im-rc
     ("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:
index 7cc660454b492b6719d7f72a3f7eff6869cdc6fe..8334bc68ae72943a0cd3a0b8eddb861c62ba0829 100644 (file)
@@ -7,7 +7,7 @@
 
 const ENTRY_LIMIT: usize = 1000;
 // FIXME: The following limits should be reduced eventually.
-const ROOT_ENTRY_LIMIT: usize = 1390;
+const ROOT_ENTRY_LIMIT: usize = 1388;
 const ISSUES_ENTRY_LIMIT: usize = 2551;
 
 fn check_entries(path: &Path, bad: &mut bool) {