]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #85023 - RalfJung:array-to-raw-elem, r=Mark-Simulacrum
authorbors <bors@rust-lang.org>
Tue, 11 May 2021 14:08:06 +0000 (14:08 +0000)
committerbors <bors@rust-lang.org>
Tue, 11 May 2021 14:08:06 +0000 (14:08 +0000)
array-to-raw-elem cast: test that Retag covers entire array

Make sure that we `Retag` *before* doing the `ArrayToPointer` cast.

423 files changed:
Cargo.lock
RELEASES.md
compiler/rustc_ast_lowering/src/expr.rs
compiler/rustc_ast_passes/src/feature_gate.rs
compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
compiler/rustc_codegen_cranelift/src/archive.rs
compiler/rustc_codegen_llvm/src/back/archive.rs
compiler/rustc_codegen_llvm/src/back/lto.rs
compiler/rustc_codegen_llvm/src/back/write.rs
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
compiler/rustc_codegen_llvm/src/lib.rs
compiler/rustc_codegen_llvm/src/llvm/ffi.rs
compiler/rustc_codegen_llvm/src/llvm_util.rs
compiler/rustc_codegen_ssa/src/back/archive.rs
compiler/rustc_codegen_ssa/src/back/link.rs
compiler/rustc_codegen_ssa/src/back/linker.rs
compiler/rustc_codegen_ssa/src/back/lto.rs
compiler/rustc_codegen_ssa/src/back/write.rs
compiler/rustc_codegen_ssa/src/base.rs
compiler/rustc_codegen_ssa/src/lib.rs
compiler/rustc_codegen_ssa/src/traits/write.rs
compiler/rustc_data_structures/src/obligation_forest/mod.rs
compiler/rustc_errors/src/diagnostic.rs
compiler/rustc_errors/src/lib.rs
compiler/rustc_expand/src/config.rs
compiler/rustc_expand/src/module.rs
compiler/rustc_feature/src/active.rs
compiler/rustc_feature/src/builtin_attrs.rs
compiler/rustc_feature/src/removed.rs
compiler/rustc_infer/src/infer/error_reporting/mod.rs
compiler/rustc_infer/src/infer/mod.rs
compiler/rustc_interface/src/tests.rs
compiler/rustc_interface/src/util.rs
compiler/rustc_lint/src/early.rs
compiler/rustc_lint/src/non_fmt_panic.rs
compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
compiler/rustc_metadata/src/native_libs.rs
compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
compiler/rustc_middle/src/hir/map/mod.rs
compiler/rustc_middle/src/middle/cstore.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/ty/query/mod.rs
compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
compiler/rustc_mir/src/interpret/intrinsics.rs
compiler/rustc_mir/src/interpret/machine.rs
compiler/rustc_mir/src/interpret/step.rs
compiler/rustc_mir/src/interpret/validity.rs
compiler/rustc_mir/src/monomorphize/partitioning/mod.rs
compiler/rustc_mir/src/transform/const_goto.rs
compiler/rustc_mir/src/transform/coverage/graph.rs
compiler/rustc_mir/src/transform/coverage/mod.rs
compiler/rustc_mir/src/transform/deduplicate_blocks.rs
compiler/rustc_mir/src/transform/early_otherwise_branch.rs
compiler/rustc_mir/src/transform/generator.rs
compiler/rustc_mir/src/transform/inline.rs
compiler/rustc_mir/src/transform/match_branches.rs
compiler/rustc_mir/src/transform/multiple_return_terminators.rs
compiler/rustc_mir/src/transform/remove_unneeded_drops.rs
compiler/rustc_mir/src/transform/simplify.rs
compiler/rustc_mir/src/transform/simplify_try.rs
compiler/rustc_mir/src/transform/unreachable_prop.rs
compiler/rustc_parse/src/lexer/mod.rs
compiler/rustc_parse/src/parser/diagnostics.rs
compiler/rustc_parse/src/parser/item.rs
compiler/rustc_parse/src/parser/mod.rs
compiler/rustc_parse/src/parser/stmt.rs
compiler/rustc_parse/src/parser/ty.rs
compiler/rustc_passes/src/check_attr.rs
compiler/rustc_query_impl/src/plumbing.rs
compiler/rustc_query_system/src/query/caches.rs
compiler/rustc_query_system/src/query/config.rs
compiler/rustc_query_system/src/query/job.rs
compiler/rustc_query_system/src/query/plumbing.rs
compiler/rustc_resolve/src/late/diagnostics.rs
compiler/rustc_resolve/src/late/lifetimes.rs
compiler/rustc_session/src/cgu_reuse_tracker.rs
compiler/rustc_session/src/config.rs
compiler/rustc_session/src/filesearch.rs
compiler/rustc_session/src/options.rs
compiler/rustc_session/src/session.rs
compiler/rustc_session/src/utils.rs
compiler/rustc_span/src/source_map.rs
compiler/rustc_span/src/span_encoding.rs
compiler/rustc_span/src/symbol.rs
compiler/rustc_target/src/lib.rs
compiler/rustc_target/src/spec/illumos_base.rs
compiler/rustc_target/src/spec/mod.rs
compiler/rustc_target/src/spec/x86_64_unknown_none_hermitkernel.rs
compiler/rustc_trait_selection/src/traits/select/mod.rs
compiler/rustc_ty_utils/src/representability.rs
compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
compiler/rustc_typeck/src/check/wfcheck.rs
compiler/rustc_typeck/src/collect.rs
config.toml.example
library/alloc/src/collections/btree/map.rs
library/alloc/src/rc.rs
library/core/src/cmp.rs
library/core/src/intrinsics.rs
library/core/src/iter/range.rs
library/core/src/lib.rs
library/core/src/mem/maybe_uninit.rs
library/core/src/num/int_macros.rs
library/core/src/num/uint_macros.rs
library/std/src/collections/hash/map.rs
library/std/src/io/buffered/bufwriter.rs
library/std/src/io/mod.rs
library/std/src/lib.rs
library/std/src/os/emscripten/raw.rs
library/std/src/sys/sgx/abi/mod.rs
library/std/src/sys/sgx/abi/tls.rs [deleted file]
library/std/src/sys/sgx/abi/tls/mod.rs [new file with mode: 0644]
library/std/src/sys/sgx/mutex.rs
library/std/src/sys/sgx/thread.rs
library/std/src/sys/sgx/waitqueue.rs [deleted file]
library/std/src/sys/sgx/waitqueue/mod.rs [new file with mode: 0644]
library/std/src/sys/sgx/waitqueue/spin_mutex.rs
library/std/src/sys/sgx/waitqueue/unsafe_list.rs
library/std/src/sys/unix/os.rs
library/std/src/sys/unsupported/args.rs
library/std/src/sys/wasm/args.rs [deleted file]
library/std/src/sys/wasm/atomics/condvar.rs [new file with mode: 0644]
library/std/src/sys/wasm/atomics/futex.rs [new file with mode: 0644]
library/std/src/sys/wasm/atomics/mutex.rs [new file with mode: 0644]
library/std/src/sys/wasm/atomics/rwlock.rs [new file with mode: 0644]
library/std/src/sys/wasm/atomics/thread.rs [new file with mode: 0644]
library/std/src/sys/wasm/condvar_atomics.rs [deleted file]
library/std/src/sys/wasm/futex_atomics.rs [deleted file]
library/std/src/sys/wasm/mod.rs
library/std/src/sys/wasm/mutex_atomics.rs [deleted file]
library/std/src/sys/wasm/rwlock_atomics.rs [deleted file]
library/std/src/sys/wasm/thread.rs [deleted file]
library/std/src/thread/local/tests.rs
library/stdarch
src/bootstrap/bootstrap.py
src/bootstrap/builder.rs
src/bootstrap/builder/tests.rs
src/bootstrap/check.rs
src/bootstrap/compile.rs
src/bootstrap/flags.rs
src/bootstrap/format.rs
src/bootstrap/lib.rs
src/bootstrap/test.rs
src/ci/pgo.sh
src/ci/scripts/install-clang.sh
src/doc/unstable-book/src/compiler-flags/instrument-coverage.md [new file with mode: 0644]
src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md
src/doc/unstable-book/src/language-features/const-fn.md [deleted file]
src/doc/unstable-book/src/language-features/native-link-modifiers-as-needed.md [new file with mode: 0644]
src/doc/unstable-book/src/language-features/native-link-modifiers-bundle.md [new file with mode: 0644]
src/doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md [new file with mode: 0644]
src/doc/unstable-book/src/language-features/native-link-modifiers-whole-archive.md [new file with mode: 0644]
src/doc/unstable-book/src/language-features/native-link-modifiers.md [new file with mode: 0644]
src/doc/unstable-book/src/language-features/no-coverage.md [new file with mode: 0644]
src/librustdoc/clean/inline.rs
src/librustdoc/clean/mod.rs
src/librustdoc/clean/types.rs
src/librustdoc/clean/utils.rs
src/librustdoc/config.rs
src/librustdoc/core.rs
src/librustdoc/doctest.rs
src/librustdoc/html/highlight.rs
src/librustdoc/html/highlight/tests.rs
src/librustdoc/html/layout.rs
src/librustdoc/html/markdown.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/clipboard.svg [new file with mode: 0644]
src/librustdoc/html/static/main.js
src/librustdoc/html/static/rustdoc.css
src/librustdoc/html/static/themes/ayu.css
src/librustdoc/html/static/themes/dark.css
src/librustdoc/html/static/themes/light.css
src/librustdoc/html/static_files.rs
src/librustdoc/lib.rs
src/librustdoc/passes/calculate_doc_coverage.rs
src/librustdoc/passes/collect_intra_doc_links.rs
src/librustdoc/passes/doc_test_lints.rs
src/llvm-project
src/test/debuginfo/should-fail.rs
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.async2.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.closure.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.closure_macro.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.closure_macro_async.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.conditions.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.doctest.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.drop_trait.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.generics.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.loops_branches.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_crate.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt [deleted file]
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.panic_unwind.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.tight_inf_loop.txt
src/test/run-make-fulldeps/coverage/async2.rs
src/test/run-make-fulldeps/coverage/closure.rs
src/test/run-make-fulldeps/coverage/closure_macro.rs
src/test/run-make-fulldeps/coverage/closure_macro_async.rs
src/test/run-make-fulldeps/coverage/conditions.rs
src/test/run-make-fulldeps/coverage/generics.rs
src/test/run-make-fulldeps/coverage/loops_branches.rs
src/test/run-make-fulldeps/coverage/no_cov_crate.rs
src/test/run-make-fulldeps/coverage/no_cov_func.rs [deleted file]
src/test/run-make-fulldeps/coverage/panic_unwind.rs
src/test/rustdoc-gui/source-code-page.goml [new file with mode: 0644]
src/test/rustdoc-ui/doc-attr2.rs [deleted file]
src/test/rustdoc-ui/doc-attr2.stderr [deleted file]
src/test/rustdoc-ui/invalid-cfg.rs [new file with mode: 0644]
src/test/rustdoc-ui/invalid-cfg.stderr [new file with mode: 0644]
src/test/rustdoc-ui/invalid-doc-attr.rs [new file with mode: 0644]
src/test/rustdoc-ui/invalid-doc-attr.stderr [new file with mode: 0644]
src/test/rustdoc/doc-cfg.rs
src/test/rustdoc/issue-55364.rs
src/test/ui/attributes/doc-attr2.rs [deleted file]
src/test/ui/attributes/doc-attr2.stderr [deleted file]
src/test/ui/attributes/invalid-doc-attr.rs [new file with mode: 0644]
src/test/ui/attributes/invalid-doc-attr.stderr [new file with mode: 0644]
src/test/ui/backtrace-debuginfo.rs
src/test/ui/cfg/cfg-panic.rs
src/test/ui/consts/const-eval/erroneous-const.rs
src/test/ui/consts/const-eval/erroneous-const.stderr
src/test/ui/consts/const-eval/erroneous-const2.rs [new file with mode: 0644]
src/test/ui/consts/const-eval/erroneous-const2.stderr [new file with mode: 0644]
src/test/ui/consts/const-eval/promoted_errors.noopt.stderr
src/test/ui/consts/const-eval/promoted_errors.opt.stderr
src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr
src/test/ui/consts/const-eval/promoted_errors.rs
src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr
src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr
src/test/ui/consts/const_arg_local.rs
src/test/ui/consts/const_arg_local.stderr
src/test/ui/consts/const_arg_promotable.rs
src/test/ui/consts/const_arg_promotable.stderr
src/test/ui/consts/const_arg_wrapper.rs
src/test/ui/consts/const_arg_wrapper.stderr
src/test/ui/consts/offset_from_ub.stderr
src/test/ui/consts/offset_ub.stderr
src/test/ui/consts/ptr_comparisons.stderr
src/test/ui/entry-point/imported_main_from_extern_crate.rs
src/test/ui/entry-point/imported_main_from_extern_crate.stderr [deleted file]
src/test/ui/error-codes/E0583.stderr
src/test/ui/feature-gates/feature-gate-const_fn.rs [deleted file]
src/test/ui/feature-gates/feature-gate-const_fn.stderr [deleted file]
src/test/ui/feature-gates/feature-gate-native_link_modifiers.rs [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers.stderr [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.rs [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.stderr [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers_bundle.rs [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers_bundle.stderr [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.rs [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.stderr [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers_whole_archive.rs [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-native_link_modifiers_whole_archive.stderr [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-no_coverage.rs
src/test/ui/feature-gates/feature-gate-no_coverage.stderr
src/test/ui/feature-gates/feature-gate-rustc_const_unstable.rs
src/test/ui/feature-gates/feature-gate-rustc_const_unstable.stderr
src/test/ui/feature-gates/feature-gate-static-nobundle-2.stderr
src/test/ui/feature-gates/feature-gate-static-nobundle.rs
src/test/ui/feature-gates/feature-gate-static-nobundle.stderr
src/test/ui/internal/internal-unstable-const.rs
src/test/ui/intrinsics/intrinsic-alignment.rs
src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr
src/test/ui/invalid/invalid-llvm-passes.rs [new file with mode: 0644]
src/test/ui/invalid/invalid-llvm-passes.stderr [new file with mode: 0644]
src/test/ui/issues/issue-54954.rs
src/test/ui/issues/issue-54954.stderr
src/test/ui/issues/issue-70093.rs
src/test/ui/lifetimes/issue-84398.rs [new file with mode: 0644]
src/test/ui/meta/revision-bad.rs
src/test/ui/mismatched_types/const-fn-in-trait.rs
src/test/ui/mismatched_types/const-fn-in-trait.stderr
src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr
src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr
src/test/ui/native-library-link-flags/empty-kind-1.rs [new file with mode: 0644]
src/test/ui/native-library-link-flags/empty-kind-1.stderr [new file with mode: 0644]
src/test/ui/native-library-link-flags/empty-kind-2.rs [new file with mode: 0644]
src/test/ui/native-library-link-flags/empty-kind-2.stderr [new file with mode: 0644]
src/test/ui/non-fmt-panic.rs
src/test/ui/non-fmt-panic.stderr
src/test/ui/panic-handler/weak-lang-item.rs
src/test/ui/panic-handler/weak-lang-item.stderr
src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs
src/test/ui/panic-runtime/lto-unwind.rs
src/test/ui/panic-runtime/transitive-link-a-bunch.rs
src/test/ui/panic-runtime/want-unwind-got-abort.rs
src/test/ui/panic-runtime/want-unwind-got-abort2.rs
src/test/ui/parser/fn-header-semantic-fail.rs
src/test/ui/parser/fn-header-semantic-fail.stderr
src/test/ui/parser/mod_file_not_exist.stderr
src/test/ui/parser/mod_file_not_exist_windows.stderr
src/test/ui/parser/trait-object-delimiters.rs [new file with mode: 0644]
src/test/ui/parser/trait-object-delimiters.stderr [new file with mode: 0644]
src/test/ui/parser/unsafe-mod.stderr
src/test/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs
src/test/ui/rfc-2091-track-caller/const-caller-location.rs
src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr
src/test/ui/rustc-args-required-const2.rs
src/test/ui/stability-attribute/stability-attribute-sanity.rs
src/test/ui/static/static-drop-scope.rs
src/test/ui/static/static-drop-scope.stderr
src/test/ui/structs-enums/rec-align-u64.rs
src/test/ui/structs-enums/struct-rec/issue-74224.rs [new file with mode: 0644]
src/test/ui/structs-enums/struct-rec/issue-74224.stderr [new file with mode: 0644]
src/test/ui/structs-enums/struct-rec/issue-84611.rs [new file with mode: 0644]
src/test/ui/structs-enums/struct-rec/issue-84611.stderr [new file with mode: 0644]
src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.rs [new file with mode: 0644]
src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.stderr [new file with mode: 0644]
src/test/ui/structs/struct-fn-in-definition.rs [new file with mode: 0644]
src/test/ui/structs/struct-fn-in-definition.stderr [new file with mode: 0644]
src/test/ui/suggestions/issue-68049-1.rs [new file with mode: 0644]
src/test/ui/suggestions/issue-68049-1.stderr [new file with mode: 0644]
src/test/ui/suggestions/issue-68049-2.rs [new file with mode: 0644]
src/test/ui/suggestions/issue-68049-2.stderr [new file with mode: 0644]
src/test/ui/suggestions/issue-84592.rs [new file with mode: 0644]
src/test/ui/suggestions/issue-84592.stderr [new file with mode: 0644]
src/test/ui/suggestions/missing-lt-for-hrtb.stderr
src/test/ui/suggestions/return-elided-lifetime.rs [new file with mode: 0644]
src/test/ui/suggestions/return-elided-lifetime.stderr [new file with mode: 0644]
src/test/ui/suggestions/unsized-function-parameter.fixed [new file with mode: 0644]
src/test/ui/suggestions/unsized-function-parameter.rs [new file with mode: 0644]
src/test/ui/suggestions/unsized-function-parameter.stderr [new file with mode: 0644]
src/test/ui/target-feature/rust-specific-name-no-warnings.rs [new file with mode: 0644]
src/test/ui/test-panic-abort-disabled.rs
src/test/ui/thread-local-in-ctfe.rs
src/test/ui/threads-sendsync/auxiliary/thread-local-extern-static.rs
src/test/ui/threads-sendsync/issue-43733.rs
src/test/ui/threads-sendsync/issue-43733.stderr
src/test/ui/union/union-const-eval-field.rs
src/test/ui/unsafe/unsafe-unstable-const-fn.rs
src/test/ui/unsafe/unsafe-unstable-const-fn.stderr
src/test/ui/unwind-no-uwtable.rs
src/test/ui/x86stdcall.rs
src/tools/cargo
src/tools/cargotest/main.rs
src/tools/clippy/.gitignore
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/Cargo.toml
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/comparison_chain.rs
src/tools/clippy/clippy_lints/src/default.rs
src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
src/tools/clippy/clippy_lints/src/implicit_return.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
src/tools/clippy/clippy_lints/src/misc_early.rs [deleted file]
src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/double_neg.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/mod.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/unseparated_literal_suffix.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
src/tools/clippy/clippy_lints/src/unused_unit.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/utils/mod.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/diagnostics.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/rust-toolchain
src/tools/clippy/src/driver.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr
src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr
src/tools/clippy/tests/ui/builtin-type-shadow.rs [deleted file]
src/tools/clippy/tests/ui/builtin-type-shadow.stderr [deleted file]
src/tools/clippy/tests/ui/builtin_type_shadow.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/builtin_type_shadow.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/comparison_chain.rs
src/tools/clippy/tests/ui/copy_iterator.rs
src/tools/clippy/tests/ui/crashes/ice-7169.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-7169.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/deprecated.stderr
src/tools/clippy/tests/ui/deprecated_old.stderr
src/tools/clippy/tests/ui/eval_order_dependence.rs
src/tools/clippy/tests/ui/eval_order_dependence.stderr
src/tools/clippy/tests/ui/implicit_return.fixed
src/tools/clippy/tests/ui/implicit_return.rs
src/tools/clippy/tests/ui/implicit_return.stderr
src/tools/clippy/tests/ui/infinite_loop.rs
src/tools/clippy/tests/ui/needless_collect_indirect.rs
src/tools/clippy/tests/ui/needless_collect_indirect.stderr
src/tools/clippy/tests/ui/new_without_default.rs
src/tools/clippy/tests/ui/new_without_default.stderr
src/tools/clippy/tests/ui/rename.stderr
src/tools/clippy/tests/ui/single_char_pattern.fixed
src/tools/clippy/tests/ui/single_char_pattern.rs
src/tools/clippy/tests/ui/single_char_pattern.stderr
src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
src/tools/clippy/tests/ui/unnecessary_filter_map.rs
src/tools/clippy/tests/ui/unused_unit.fixed
src/tools/clippy/tests/ui/unused_unit.rs
src/tools/clippy/util/lintlib.py
src/tools/compiletest/src/common.rs
src/tools/compiletest/src/header.rs
src/tools/compiletest/src/main.rs
src/tools/compiletest/src/runtest.rs
src/tools/miri
src/tools/rls
src/tools/rust-analyzer
src/tools/rustdoc-gui/tester.js
src/tools/tidy/src/deps.rs
src/tools/tidy/src/features.rs
src/tools/tidy/src/pal.rs

index 0939f19cdfe2069d6bbee16be3e63d5b34e2649a..925898f6bbeeb86018afd91a70ca75254d7633c9 100644 (file)
@@ -252,7 +252,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da"
 
 [[package]]
 name = "cargo"
-version = "0.54.0"
+version = "0.55.0"
 dependencies = [
  "anyhow",
  "atty",
@@ -548,7 +548,7 @@ dependencies = [
 
 [[package]]
 name = "clippy"
-version = "0.1.53"
+version = "0.1.54"
 dependencies = [
  "cargo_metadata 0.12.0",
  "clippy-mini-macro-test",
@@ -585,7 +585,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_lints"
-version = "0.1.53"
+version = "0.1.54"
 dependencies = [
  "cargo_metadata 0.12.0",
  "clippy_utils",
@@ -597,6 +597,7 @@ dependencies = [
  "rustc-semver",
  "semver 0.11.0",
  "serde",
+ "serde_json",
  "toml",
  "unicode-normalization",
  "url 2.1.1",
@@ -604,7 +605,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_utils"
-version = "0.1.53"
+version = "0.1.54"
 dependencies = [
  "if_chain",
  "itertools 0.9.0",
@@ -864,24 +865,24 @@ dependencies = [
 
 [[package]]
 name = "curl"
-version = "0.4.34"
+version = "0.4.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e268162af1a5fe89917ae25ba3b0a77c8da752bdc58e7dbb4f15b91fbd33756e"
+checksum = "d0bac9f84ca0977c4d9b8db998689de55b9e976656a6bc87fada2ca710d504c7"
 dependencies = [
  "curl-sys",
  "libc",
  "openssl-probe",
  "openssl-sys",
  "schannel",
- "socket2",
+ "socket2 0.4.0",
  "winapi 0.3.9",
 ]
 
 [[package]]
 name = "curl-sys"
-version = "0.4.39+curl-7.74.0"
+version = "0.4.42+curl-7.76.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07a8ce861e7b68a0b394e814d7ee9f1b2750ff8bd10372c6ad3bacc10e86f874"
+checksum = "4636d8d6109c842707018a104051436bffb8991ea20b2d1293db70b6e0ee4c7c"
 dependencies = [
  "cc",
  "libc",
@@ -2273,7 +2274,7 @@ version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
 dependencies = [
- "socket2",
+ "socket2 0.3.16",
  "winapi 0.3.9",
 ]
 
@@ -2390,15 +2391,15 @@ dependencies = [
 
 [[package]]
 name = "openssl"
-version = "0.10.30"
+version = "0.10.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
+checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577"
 dependencies = [
  "bitflags",
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "foreign-types",
- "lazy_static",
  "libc",
+ "once_cell",
  "openssl-sys",
 ]
 
@@ -2410,18 +2411,18 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
 
 [[package]]
 name = "openssl-src"
-version = "111.12.0+1.1.1h"
+version = "111.15.0+1.1.1k"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "858a4132194f8570a7ee9eb8629e85b23cbc4565f2d4a162e87556e5956abf61"
+checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a"
 dependencies = [
  "cc",
 ]
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.58"
+version = "0.9.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
+checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
 dependencies = [
  "autocfg",
  "cc",
@@ -4863,6 +4864,16 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
+[[package]]
+name = "socket2"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2"
+dependencies = [
+ "libc",
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
index 1f940e6bc2d3b6d8be4ee23c37c38802139b259c..92312d8d556ee53d730b60ed05b87d7a67afe75a 100644 (file)
@@ -58,7 +58,7 @@ The following previously stable APIs are now `const`.
 Rustdoc
 -------
 - [Rustdoc lints are now treated as a tool lint, meaning that
-  lints are now prefixed with `rustdoc::` (e.g. `#[warn(rustdoc::non_autolinks)]`).][80527]
+  lints are now prefixed with `rustdoc::` (e.g. `#[warn(rustdoc::broken_intra_doc_links)]`).][80527]
   Using the old style is still allowed, and will become a warning in
   a future release.
 - [Rustdoc now supports argument files.][82261]
index bf70a41fd79e093d597b283d36b4682f2cbe3223..ea0770daf0eed86232bc7dca6c1944fdcd6108b1 100644 (file)
@@ -1236,9 +1236,7 @@ fn lower_expr_range(
             (Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
             (None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
             (Some(..), Some(..), Closed) => unreachable!(),
-            (_, None, Closed) => {
-                self.diagnostic().span_fatal(span, "inclusive range with no end").raise()
-            }
+            (_, None, Closed) => self.diagnostic().span_fatal(span, "inclusive range with no end"),
         };
 
         let fields = self.arena.alloc_from_iter(
index da516f5cb412928c4272ae7bdaa52fefde992f74..dc3383dae843e20c131dad919fe4085adc0a3670 100644 (file)
@@ -326,6 +326,45 @@ macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
                 );
             }
         }
+
+        // Check for unstable modifiers on `#[link(..)]` attribute
+        if self.sess.check_name(attr, sym::link) {
+            for nested_meta in attr.meta_item_list().unwrap_or_default() {
+                if nested_meta.has_name(sym::modifiers) {
+                    gate_feature_post!(
+                        self,
+                        native_link_modifiers,
+                        nested_meta.span(),
+                        "native link modifiers are experimental"
+                    );
+
+                    if let Some(modifiers) = nested_meta.value_str() {
+                        for modifier in modifiers.as_str().split(',') {
+                            if let Some(modifier) = modifier.strip_prefix(&['+', '-'][..]) {
+                                macro_rules! gate_modifier { ($($name:literal => $feature:ident)*) => {
+                                    $(if modifier == $name {
+                                        let msg = concat!("`#[link(modifiers=\"", $name, "\")]` is unstable");
+                                        gate_feature_post!(
+                                            self,
+                                            $feature,
+                                            nested_meta.name_value_literal_span().unwrap(),
+                                            msg
+                                        );
+                                    })*
+                                }}
+
+                                gate_modifier!(
+                                    "bundle" => native_link_modifiers_bundle
+                                    "verbatim" => native_link_modifiers_verbatim
+                                    "whole-archive" => native_link_modifiers_whole_archive
+                                    "as-needed" => native_link_modifiers_as_needed
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        }
     }
 
     fn visit_item(&mut self, i: &'a ast::Item) {
index 5a4e7fd9d07b4691fa70ee404373263f5531db49..54ab88dc3ffc9abd2949be3aef93a4edb8e0a406 100644 (file)
@@ -15,20 +15,12 @@ pub fn expand_deriving_eq(
     item: &Annotatable,
     push: &mut dyn FnMut(Annotatable),
 ) {
+    let span = cx.with_def_site_ctxt(span);
     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(no_coverage_feature),
-        cx.attribute(no_coverage),
-        cx.attribute(doc),
-    ];
+    let no_coverage = cx.meta_word(span, sym::no_coverage);
+    let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)];
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
index 7583fc424071e251cb158c390265c294bbe30836..fc0823302e0189971c492fcf6c96cd77292c2cfb 100644 (file)
@@ -85,8 +85,8 @@ fn add_file(&mut self, file: &Path) {
         ));
     }
 
-    fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) {
-        let location = find_library(name, &self.lib_search_paths, self.sess);
+    fn add_native_library(&mut self, name: rustc_span::symbol::Symbol, verbatim: bool) {
+        let location = find_library(name, verbatim, &self.lib_search_paths, self.sess);
         self.add_archive(location.clone(), |_| false).unwrap_or_else(|e| {
             panic!("failed to add native library {}: {}", location.to_string_lossy(), e);
         });
index 4e7213853b015a596f32875f26807a88b7b2d5ba..261affe2c427e1f01aa63dfe57cad8d5a7a4e9f3 100644 (file)
@@ -100,8 +100,9 @@ fn src_files(&mut self) -> Vec<String> {
 
     /// Adds all of the contents of a native library to this archive. This will
     /// search in the relevant locations for a library named `name`.
-    fn add_native_library(&mut self, name: Symbol) {
-        let location = find_library(name, &self.config.lib_search_paths, self.config.sess);
+    fn add_native_library(&mut self, name: Symbol, verbatim: bool) {
+        let location =
+            find_library(name, verbatim, &self.config.lib_search_paths, self.config.sess);
         self.add_archive(&location, |_| false).unwrap_or_else(|e| {
             self.config.sess.fatal(&format!(
                 "failed to add native library {}: {}",
index 4226ed7d99be13cd1731e63dccd408d1659eccb2..f612785e5a416c582321e4efa0b7c1aec41e91b6 100644 (file)
@@ -568,10 +568,11 @@ fn thin_lto(
 
 pub(crate) fn run_pass_manager(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
+    diag_handler: &Handler,
     module: &ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
     thin: bool,
-) {
+) -> Result<(), FatalError> {
     let _timer = cgcx.prof.extra_verbose_generic_activity("LLVM_lto_optimize", &module.name[..]);
 
     // Now we have one massive module inside of llmod. Time to run the
@@ -584,15 +585,16 @@ pub(crate) fn run_pass_manager(
         if write::should_use_new_llvm_pass_manager(config) {
             let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
             let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
-            // See comment below for why this is necessary.
-            let opt_level = if let config::OptLevel::No = opt_level {
-                config::OptLevel::Less
-            } else {
-                opt_level
-            };
-            write::optimize_with_new_llvm_pass_manager(cgcx, module, config, opt_level, opt_stage);
+            write::optimize_with_new_llvm_pass_manager(
+                cgcx,
+                diag_handler,
+                module,
+                config,
+                opt_level,
+                opt_stage,
+            )?;
             debug!("lto done");
-            return;
+            return Ok(());
         }
 
         let pm = llvm::LLVMCreatePassManager();
@@ -603,26 +605,10 @@ pub(crate) fn run_pass_manager(
             llvm::LLVMRustAddPass(pm, pass.unwrap());
         }
 
-        // When optimizing for LTO we don't actually pass in `-O0`, but we force
-        // it to always happen at least with `-O1`.
-        //
-        // With ThinLTO we mess around a lot with symbol visibility in a way
-        // that will actually cause linking failures if we optimize at O0 which
-        // notable is lacking in dead code elimination. To ensure we at least
-        // get some optimizations and correctly link we forcibly switch to `-O1`
-        // to get dead code elimination.
-        //
-        // Note that in general this shouldn't matter too much as you typically
-        // only turn on ThinLTO when you're compiling with optimizations
-        // otherwise.
         let opt_level = config
             .opt_level
             .map(|x| to_llvm_opt_settings(x).0)
             .unwrap_or(llvm::CodeGenOptLevel::None);
-        let opt_level = match opt_level {
-            llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less,
-            level => level,
-        };
         with_llvm_pmb(module.module_llvm.llmod(), config, opt_level, false, &mut |b| {
             if thin {
                 llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm);
@@ -650,6 +636,7 @@ pub(crate) fn run_pass_manager(
         llvm::LLVMDisposePassManager(pm);
     }
     debug!("lto done");
+    Ok(())
 }
 
 pub struct ModuleBuffer(&'static mut llvm::ModuleBuffer);
@@ -872,7 +859,7 @@ pub unsafe fn optimize_thin_module(
         {
             info!("running thin lto passes over {}", module.name);
             let config = cgcx.config(module.kind);
-            run_pass_manager(cgcx, &module, config, true);
+            run_pass_manager(cgcx, &diag_handler, &module, config, true)?;
             save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
         }
     }
index b628ae3ae3afccbc6afa3eda3e80e8a5438555c1..4219797c5cdd0b007bb485025108cf292133bb61 100644 (file)
@@ -410,16 +410,17 @@ fn get_pgo_use_path(config: &ModuleConfig) -> Option<CString> {
 
 pub(crate) fn should_use_new_llvm_pass_manager(config: &ModuleConfig) -> bool {
     // The new pass manager is disabled by default.
-    config.new_llvm_pass_manager
+    config.new_llvm_pass_manager.unwrap_or(false)
 }
 
 pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
+    diag_handler: &Handler,
     module: &ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
     opt_level: config::OptLevel,
     opt_stage: llvm::OptStage,
-) {
+) -> Result<(), FatalError> {
     let unroll_loops =
         opt_level != config::OptLevel::Size && opt_level != config::OptLevel::SizeMin;
     let using_thin_buffers = opt_stage == llvm::OptStage::PreLinkThinLTO || config.bitcode_needed();
@@ -449,13 +450,12 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
         std::ptr::null_mut()
     };
 
+    let extra_passes = config.passes.join(",");
+
     // FIXME: NewPM doesn't provide a facility to pass custom InlineParams.
     // We would have to add upstream support for this first, before we can support
     // config.inline_threshold and our more aggressive default thresholds.
-    // FIXME: NewPM uses an different and more explicit way to textually represent
-    // pass pipelines. It would probably make sense to expose this, but it would
-    // require a different format than the current -C passes.
-    llvm::LLVMRustOptimizeWithNewPassManager(
+    let result = llvm::LLVMRustOptimizeWithNewPassManager(
         module.module_llvm.llmod(),
         &*module.module_llvm.tm,
         to_pass_builder_opt_level(opt_level),
@@ -472,10 +472,15 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
         sanitizer_options.as_ref(),
         pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
         pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
+        config.instrument_coverage,
+        config.instrument_gcov,
         llvm_selfprofiler,
         selfprofile_before_pass_callback,
         selfprofile_after_pass_callback,
+        extra_passes.as_ptr().cast(),
+        extra_passes.len(),
     );
+    result.into_result().map_err(|()| llvm_err(diag_handler, "failed to run LLVM passes"))
 }
 
 // Unsafe due to LLVM calls.
@@ -484,7 +489,7 @@ pub(crate) unsafe fn optimize(
     diag_handler: &Handler,
     module: &ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
-) {
+) -> Result<(), FatalError> {
     let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &module.name[..]);
 
     let llmod = module.module_llvm.llmod();
@@ -509,8 +514,14 @@ pub(crate) unsafe fn optimize(
                 _ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO,
                 _ => llvm::OptStage::PreLinkNoLTO,
             };
-            optimize_with_new_llvm_pass_manager(cgcx, module, config, opt_level, opt_stage);
-            return;
+            return optimize_with_new_llvm_pass_manager(
+                cgcx,
+                diag_handler,
+                module,
+                config,
+                opt_level,
+                opt_stage,
+            );
         }
 
         if cgcx.prof.llvm_recording_enabled() {
@@ -545,15 +556,6 @@ pub(crate) unsafe fn optimize(
                     llvm::LLVMRustAddPass(fpm, find_pass("lint").unwrap());
                     continue;
                 }
-                if pass_name == "insert-gcov-profiling" || pass_name == "instrprof" {
-                    // Instrumentation must be inserted before optimization,
-                    // otherwise LLVM may optimize some functions away which
-                    // breaks llvm-cov.
-                    //
-                    // This mirrors what Clang does in lib/CodeGen/BackendUtil.cpp.
-                    llvm::LLVMRustAddPass(mpm, find_pass(pass_name).unwrap());
-                    continue;
-                }
 
                 if let Some(pass) = find_pass(pass_name) {
                     extra_passes.push(pass);
@@ -566,6 +568,18 @@ pub(crate) unsafe fn optimize(
                 }
             }
 
+            // Instrumentation must be inserted before optimization,
+            // otherwise LLVM may optimize some functions away which
+            // breaks llvm-cov.
+            //
+            // This mirrors what Clang does in lib/CodeGen/BackendUtil.cpp.
+            if config.instrument_gcov {
+                llvm::LLVMRustAddPass(mpm, find_pass("insert-gcov-profiling").unwrap());
+            }
+            if config.instrument_coverage {
+                llvm::LLVMRustAddPass(mpm, find_pass("instrprof").unwrap());
+            }
+
             add_sanitizer_passes(config, &mut extra_passes);
 
             // Some options cause LLVM bitcode to be emitted, which uses ThinLTOBuffers, so we need
@@ -642,6 +656,7 @@ pub(crate) unsafe fn optimize(
         llvm::LLVMDisposePassManager(fpm);
         llvm::LLVMDisposePassManager(mpm);
     }
+    Ok(())
 }
 
 unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static mut llvm::Pass>) {
index 1faaa7e86f619ae300031dd51772c7ab990740fa..30f125ca3beaed6a90b907e121f583c792951f11 100644 (file)
@@ -8,7 +8,6 @@
 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;
 
@@ -249,7 +248,7 @@ fn save_function_record(
 ///
 /// We can find the unused functions (including generic functions) by the set difference of all MIR
 /// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query
-/// `collect_and_partition_mono_items`).
+/// `codegened_and_inlined_items`).
 ///
 /// *HOWEVER* the codegenned `DefId`s are partitioned across multiple `CodegenUnit`s (CGUs), and
 /// this function is processing a `function_coverage_map` for the functions (`Instance`/`DefId`)
@@ -281,11 +280,8 @@ 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
+        // Make sure the non-codegenned (unused) function has at least one MIR
+        // `Coverage` statement with a code region, and return its file name.
         if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) {
             let def_ids =
                 unused_def_ids_by_file.entry(*non_codegenned_file_name).or_insert_with(Vec::new);
index 5ca4b226c38fba32a404b06182947fa1428fac09..bc35aa72965681b9a7bab3532cdd50813049bb67 100644 (file)
@@ -162,7 +162,7 @@ unsafe fn optimize(
         module: &ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<(), FatalError> {
-        Ok(back::write::optimize(cgcx, diag_handler, module, config))
+        back::write::optimize(cgcx, diag_handler, module, config)
     }
     unsafe fn optimize_thin(
         cgcx: &CodegenContext<Self>,
@@ -189,8 +189,9 @@ fn run_lto_pass_manager(
         module: &ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
         thin: bool,
-    ) {
-        back::lto::run_pass_manager(cgcx, module, config, thin)
+    ) -> Result<(), FatalError> {
+        let diag_handler = cgcx.create_diag_handler();
+        back::lto::run_pass_manager(cgcx, &diag_handler, module, config, thin)
     }
 }
 
index 32b1526f6e44c3463440a0e3abfad1ea6f3b70cf..32fdde9b42e5f0c3abc69eb506e5209b97211758 100644 (file)
@@ -2203,10 +2203,14 @@ pub fn LLVMRustOptimizeWithNewPassManager(
         SanitizerOptions: Option<&SanitizerOptions>,
         PGOGenPath: *const c_char,
         PGOUsePath: *const c_char,
+        InstrumentCoverage: bool,
+        InstrumentGCOV: bool,
         llvm_selfprofiler: *mut c_void,
         begin_callback: SelfProfileBeforePassCallback,
         end_callback: SelfProfileAfterPassCallback,
-    );
+        ExtraPasses: *const c_char,
+        ExtraPassesLen: size_t,
+    ) -> LLVMRustResult;
     pub fn LLVMRustPrintModule(
         M: &'a Module,
         Output: *const c_char,
index b44553e4f6d3bf84517a27fafc5d4c68fbc15def..6101b90aea6dae8c7ca146092824bed8c5f4e560 100644 (file)
@@ -339,24 +339,32 @@ pub fn llvm_global_features(sess: &Session) -> Vec<String> {
         Some(_) | None => {}
     };
 
+    let filter = |s: &str| {
+        if s.is_empty() {
+            return None;
+        }
+        let feature = if s.starts_with("+") || s.starts_with("-") {
+            &s[1..]
+        } else {
+            return Some(s.to_string());
+        };
+        // Rustc-specific feature requests like `+crt-static` or `-crt-static`
+        // are not passed down to LLVM.
+        if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
+            return None;
+        }
+        // ... otherwise though we run through `to_llvm_feature` feature when
+        // passing requests down to LLVM. This means that all in-language
+        // features also work on the command line instead of having two
+        // different names when the LLVM name and the Rust name differ.
+        Some(format!("{}{}", &s[..1], to_llvm_feature(sess, feature)))
+    };
+
     // Features implied by an implicit or explicit `--target`.
-    features.extend(
-        sess.target
-            .features
-            .split(',')
-            .filter(|f| !f.is_empty() && !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s)))
-            .map(String::from),
-    );
+    features.extend(sess.target.features.split(',').filter_map(&filter));
 
     // -Ctarget-features
-    features.extend(
-        sess.opts
-            .cg
-            .target_feature
-            .split(',')
-            .filter(|f| !f.is_empty() && !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s)))
-            .map(String::from),
-    );
+    features.extend(sess.opts.cg.target_feature.split(',').filter_map(&filter));
 
     features
 }
index c477ac6462acb21420303dcf06eedccb3b3c9ec9..c197d48d4ea64da30bbea8ef8ff51978f08a74fa 100644 (file)
@@ -4,11 +4,19 @@
 use std::io;
 use std::path::{Path, PathBuf};
 
-pub fn find_library(name: Symbol, search_paths: &[PathBuf], sess: &Session) -> PathBuf {
+pub fn find_library(
+    name: Symbol,
+    verbatim: bool,
+    search_paths: &[PathBuf],
+    sess: &Session,
+) -> PathBuf {
     // On Windows, static libraries sometimes show up as libfoo.a and other
     // times show up as foo.lib
-    let oslibname =
-        format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix);
+    let oslibname = if verbatim {
+        name.to_string()
+    } else {
+        format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix)
+    };
     let unixlibname = format!("lib{}.a", name);
 
     for path in search_paths {
@@ -45,7 +53,7 @@ fn add_rlib(
         lto: bool,
         skip_objects: bool,
     ) -> io::Result<()>;
-    fn add_native_library(&mut self, name: Symbol);
+    fn add_native_library(&mut self, name: Symbol, verbatim: bool);
     fn update_symbols(&mut self);
 
     fn build(self);
index dcdd0910aa6af216348afa4ef2872d65d5116294..30a56badeb52e2fdb1e5d9bc742d77c7c09bcf0b 100644 (file)
@@ -329,15 +329,15 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
     // metadata of the rlib we're generating somehow.
     for lib in codegen_results.crate_info.used_libraries.iter() {
         match lib.kind {
-            NativeLibKind::StaticBundle => {}
-            NativeLibKind::StaticNoBundle
-            | NativeLibKind::Dylib
-            | NativeLibKind::Framework
+            NativeLibKind::Static { bundle: None | Some(true), .. } => {}
+            NativeLibKind::Static { bundle: Some(false), .. }
+            | NativeLibKind::Dylib { .. }
+            | NativeLibKind::Framework { .. }
             | NativeLibKind::RawDylib
             | NativeLibKind::Unspecified => continue,
         }
         if let Some(name) = lib.name {
-            ab.add_native_library(name);
+            ab.add_native_library(name, lib.verbatim.unwrap_or(false));
         }
     }
 
@@ -430,9 +430,10 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
         // Clearly this is not sufficient for a general purpose feature, and
         // we'd want to read from the library's metadata to determine which
         // object files come from where and selectively skip them.
-        let skip_object_files = native_libs
-            .iter()
-            .any(|lib| lib.kind == NativeLibKind::StaticBundle && !relevant_lib(sess, lib));
+        let skip_object_files = native_libs.iter().any(|lib| {
+            matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
+                && !relevant_lib(sess, lib)
+        });
         ab.add_rlib(
             path,
             &name.as_str(),
@@ -931,7 +932,7 @@ fn find_sanitizer_runtime(sess: &Session, filename: &String) -> PathBuf {
         let path = find_sanitizer_runtime(&sess, &filename);
         let rpath = path.to_str().expect("non-utf8 component in path");
         linker.args(&["-Wl,-rpath", "-Xlinker", rpath]);
-        linker.link_dylib(Symbol::intern(&filename));
+        linker.link_dylib(Symbol::intern(&filename), false, true);
     } else {
         let filename = format!("librustc{}_rt.{}.a", channel, name);
         let path = find_sanitizer_runtime(&sess, &filename).join(&filename);
@@ -1080,21 +1081,25 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
         .filter_map(|lib| {
             let name = lib.name?;
             match lib.kind {
-                NativeLibKind::StaticNoBundle
-                | NativeLibKind::Dylib
+                NativeLibKind::Static { bundle: Some(false), .. }
+                | NativeLibKind::Dylib { .. }
                 | NativeLibKind::Unspecified => {
+                    let verbatim = lib.verbatim.unwrap_or(false);
                     if sess.target.is_like_msvc {
-                        Some(format!("{}.lib", name))
+                        Some(format!("{}{}", name, if verbatim { "" } else { ".lib" }))
+                    } else if sess.target.linker_is_gnu {
+                        Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name))
                     } else {
                         Some(format!("-l{}", name))
                     }
                 }
-                NativeLibKind::Framework => {
+                NativeLibKind::Framework { .. } => {
                     // ld-only syntax, since there are no frameworks in MSVC
                     Some(format!("-framework {}", name))
                 }
                 // These are included, no need to print them
-                NativeLibKind::StaticBundle | NativeLibKind::RawDylib => None,
+                NativeLibKind::Static { bundle: None | Some(true), .. }
+                | NativeLibKind::RawDylib => None,
             }
         })
         .collect();
@@ -1571,7 +1576,7 @@ fn add_rpath_args(
         let target_triple = sess.opts.target_triple.triple();
         let mut get_install_prefix_lib_path = || {
             let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX");
-            let tlib = filesearch::relative_target_lib_path(&sess.sysroot, target_triple);
+            let tlib = rustc_target::target_rustlib_path(&sess.sysroot, target_triple).join("lib");
             let mut path = PathBuf::from(install_prefix);
             path.push(&tlib);
 
@@ -1812,11 +1817,20 @@ fn add_local_native_libraries(
             Some(l) => l,
             None => continue,
         };
+        let verbatim = lib.verbatim.unwrap_or(false);
         match lib.kind {
-            NativeLibKind::Dylib | NativeLibKind::Unspecified => cmd.link_dylib(name),
-            NativeLibKind::Framework => cmd.link_framework(name),
-            NativeLibKind::StaticNoBundle => cmd.link_staticlib(name),
-            NativeLibKind::StaticBundle => cmd.link_whole_staticlib(name, &search_path),
+            NativeLibKind::Dylib { as_needed } => {
+                cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
+            }
+            NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true),
+            NativeLibKind::Framework { as_needed } => {
+                cmd.link_framework(name, as_needed.unwrap_or(true))
+            }
+            NativeLibKind::Static { bundle: None | Some(true), .. }
+            | NativeLibKind::Static { whole_archive: Some(true), .. } => {
+                cmd.link_whole_staticlib(name, verbatim, &search_path);
+            }
+            NativeLibKind::Static { .. } => cmd.link_staticlib(name, verbatim),
             NativeLibKind::RawDylib => {
                 // FIXME(#58713): Proper handling for raw dylibs.
                 bug!("raw_dylib feature not yet implemented");
@@ -2000,9 +2014,10 @@ fn add_static_crate<'a, B: ArchiveBuilder<'a>>(
         // there's a static library that's not relevant we skip all object
         // files.
         let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
-        let skip_native = native_libs
-            .iter()
-            .any(|lib| lib.kind == NativeLibKind::StaticBundle && !relevant_lib(sess, lib));
+        let skip_native = native_libs.iter().any(|lib| {
+            matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
+                && !relevant_lib(sess, lib)
+        });
 
         if (!are_upstream_rust_objects_already_included(sess)
             || ignored_for_lto(sess, &codegen_results.crate_info, cnum))
@@ -2144,22 +2159,28 @@ fn add_upstream_native_libraries(
             if !relevant_lib(sess, &lib) {
                 continue;
             }
+            let verbatim = lib.verbatim.unwrap_or(false);
             match lib.kind {
-                NativeLibKind::Dylib | NativeLibKind::Unspecified => cmd.link_dylib(name),
-                NativeLibKind::Framework => cmd.link_framework(name),
-                NativeLibKind::StaticNoBundle => {
+                NativeLibKind::Dylib { as_needed } => {
+                    cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
+                }
+                NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true),
+                NativeLibKind::Framework { as_needed } => {
+                    cmd.link_framework(name, as_needed.unwrap_or(true))
+                }
+                NativeLibKind::Static { bundle: Some(false), .. } => {
                     // Link "static-nobundle" native libs only if the crate they originate from
                     // is being linked statically to the current crate.  If it's linked dynamically
                     // or is an rlib already included via some other dylib crate, the symbols from
                     // native libs will have already been included in that dylib.
                     if data[cnum.as_usize() - 1] == Linkage::Static {
-                        cmd.link_staticlib(name)
+                        cmd.link_staticlib(name, verbatim)
                     }
                 }
                 // ignore statically included native libraries here as we've
                 // already included them when we included the rust library
                 // previously
-                NativeLibKind::StaticBundle => {}
+                NativeLibKind::Static { bundle: None | Some(true), .. } => {}
                 NativeLibKind::RawDylib => {
                     // FIXME(#58713): Proper handling for raw dylibs.
                     bug!("raw_dylib feature not yet implemented");
index 77d8ab49ff258d2b2acaac870182fefc5c759f4a..cb3c98354c8750242c5c91f4a631118651ca59f1 100644 (file)
@@ -103,18 +103,19 @@ pub fn to_linker<'a>(
 pub trait Linker {
     fn cmd(&mut self) -> &mut Command;
     fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path);
-    fn link_dylib(&mut self, lib: Symbol);
+    fn link_dylib(&mut self, lib: Symbol, verbatim: bool, as_needed: bool);
     fn link_rust_dylib(&mut self, lib: Symbol, path: &Path);
-    fn link_framework(&mut self, framework: Symbol);
-    fn link_staticlib(&mut self, lib: Symbol);
+    fn link_framework(&mut self, framework: Symbol, as_needed: bool);
+    fn link_staticlib(&mut self, lib: Symbol, verbatim: bool);
     fn link_rlib(&mut self, lib: &Path);
     fn link_whole_rlib(&mut self, lib: &Path);
-    fn link_whole_staticlib(&mut self, lib: Symbol, search_path: &[PathBuf]);
+    fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, search_path: &[PathBuf]);
     fn include_path(&mut self, path: &Path);
     fn framework_path(&mut self, path: &Path);
     fn output_filename(&mut self, path: &Path);
     fn add_object(&mut self, path: &Path);
     fn gc_sections(&mut self, keep_metadata: bool);
+    fn no_gc_sections(&mut self);
     fn full_relro(&mut self);
     fn partial_relro(&mut self);
     fn no_relro(&mut self);
@@ -338,13 +339,40 @@ fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path)
         }
     }
 
-    fn link_dylib(&mut self, lib: Symbol) {
+    fn link_dylib(&mut self, lib: Symbol, verbatim: bool, as_needed: bool) {
+        if self.sess.target.os == "illumos" && lib.as_str() == "c" {
+            // libc will be added via late_link_args on illumos so that it will
+            // appear last in the library search order.
+            // FIXME: This should be replaced by a more complete and generic
+            // mechanism for controlling the order of library arguments passed
+            // to the linker.
+            return;
+        }
+        if !as_needed {
+            if self.sess.target.is_like_osx {
+                // FIXME(81490): ld64 doesn't support these flags but macOS 11
+                // has -needed-l{} / -needed_library {}
+                // but we have no way to detect that here.
+                self.sess.warn("`as-needed` modifier not implemented yet for ld64");
+            } else if self.sess.target.linker_is_gnu {
+                self.linker_arg("--no-as-needed");
+            } else {
+                self.sess.warn("`as-needed` modifier not supported for current linker");
+            }
+        }
         self.hint_dynamic();
-        self.cmd.arg(format!("-l{}", lib));
+        self.cmd.arg(format!("-l{}{}", if verbatim { ":" } else { "" }, lib));
+        if !as_needed {
+            if self.sess.target.is_like_osx {
+                // See above FIXME comment
+            } else if self.sess.target.linker_is_gnu {
+                self.linker_arg("--as-needed");
+            }
+        }
     }
-    fn link_staticlib(&mut self, lib: Symbol) {
+    fn link_staticlib(&mut self, lib: Symbol, verbatim: bool) {
         self.hint_static();
-        self.cmd.arg(format!("-l{}", lib));
+        self.cmd.arg(format!("-l{}{}", if verbatim { ":" } else { "" }, lib));
     }
     fn link_rlib(&mut self, lib: &Path) {
         self.hint_static();
@@ -378,8 +406,14 @@ fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) {
         self.cmd.arg(format!("-l{}", lib));
     }
 
-    fn link_framework(&mut self, framework: Symbol) {
+    fn link_framework(&mut self, framework: Symbol, as_needed: bool) {
         self.hint_dynamic();
+        if !as_needed {
+            // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework
+            // flag but we have no way to detect that here.
+            // self.cmd.arg("-needed_framework").sym_arg(framework);
+            self.sess.warn("`as-needed` modifier not implemented yet for ld64");
+        }
         self.cmd.arg("-framework").sym_arg(framework);
     }
 
@@ -389,17 +423,21 @@ fn link_framework(&mut self, framework: Symbol) {
     // don't otherwise explicitly reference them. This can occur for
     // libraries which are just providing bindings, libraries with generic
     // functions, etc.
-    fn link_whole_staticlib(&mut self, lib: Symbol, search_path: &[PathBuf]) {
+    fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, search_path: &[PathBuf]) {
         self.hint_static();
         let target = &self.sess.target;
         if !target.is_like_osx {
-            self.linker_arg("--whole-archive").cmd.arg(format!("-l{}", lib));
+            self.linker_arg("--whole-archive").cmd.arg(format!(
+                "-l{}{}",
+                if verbatim { ":" } else { "" },
+                lib
+            ));
             self.linker_arg("--no-whole-archive");
         } else {
             // -force_load is the macOS equivalent of --whole-archive, but it
             // involves passing the full path to the library to link.
             self.linker_arg("-force_load");
-            let lib = archive::find_library(lib, search_path, &self.sess);
+            let lib = archive::find_library(lib, verbatim, search_path, &self.sess);
             self.linker_arg(&lib);
         }
     }
@@ -432,8 +470,6 @@ fn gc_sections(&mut self, keep_metadata: bool) {
         // insert it here.
         if self.sess.target.is_like_osx {
             self.linker_arg("-dead_strip");
-        } else if self.sess.target.is_like_solaris {
-            self.linker_arg("-zignore");
 
         // If we're building a dylib, we don't use --gc-sections because LLVM
         // has already done the best it can do, and we also don't want to
@@ -445,6 +481,16 @@ fn gc_sections(&mut self, keep_metadata: bool) {
         }
     }
 
+    fn no_gc_sections(&mut self) {
+        if self.sess.target.is_like_osx {
+            self.linker_arg("-no_dead_strip");
+        } else if self.sess.target.is_like_solaris {
+            self.linker_arg("-zrecord");
+        } else {
+            self.linker_arg("--no-gc-sections");
+        }
+    }
+
     fn optimize(&mut self) {
         if !self.sess.target.linker_is_gnu {
             return;
@@ -655,6 +701,10 @@ fn add_no_exec(&mut self) {
     fn add_as_needed(&mut self) {
         if self.sess.target.linker_is_gnu {
             self.linker_arg("--as-needed");
+        } else if self.sess.target.is_like_solaris {
+            // -z ignore is the Solaris equivalent to the GNU ld --as-needed option
+            self.linker_arg("-z");
+            self.linker_arg("ignore");
         }
     }
 }
@@ -708,8 +758,12 @@ fn gc_sections(&mut self, _keep_metadata: bool) {
         }
     }
 
-    fn link_dylib(&mut self, lib: Symbol) {
-        self.cmd.arg(&format!("{}.lib", lib));
+    fn no_gc_sections(&mut self) {
+        self.cmd.arg("/OPT:NOREF,NOICF");
+    }
+
+    fn link_dylib(&mut self, lib: Symbol, verbatim: bool, _as_needed: bool) {
+        self.cmd.arg(format!("{}{}", lib, if verbatim { "" } else { ".lib" }));
     }
 
     fn link_rust_dylib(&mut self, lib: Symbol, path: &Path) {
@@ -718,13 +772,13 @@ fn link_rust_dylib(&mut self, lib: Symbol, path: &Path) {
         // check to see if the file is there and just omit linking to it if it's
         // not present.
         let name = format!("{}.dll.lib", lib);
-        if fs::metadata(&path.join(&name)).is_ok() {
+        if path.join(&name).exists() {
             self.cmd.arg(name);
         }
     }
 
-    fn link_staticlib(&mut self, lib: Symbol) {
-        self.cmd.arg(&format!("{}.lib", lib));
+    fn link_staticlib(&mut self, lib: Symbol, verbatim: bool) {
+        self.cmd.arg(format!("{}{}", lib, if verbatim { "" } else { ".lib" }));
     }
 
     fn full_relro(&mut self) {
@@ -762,16 +816,14 @@ fn output_filename(&mut self, path: &Path) {
     fn framework_path(&mut self, _path: &Path) {
         bug!("frameworks are not supported on windows")
     }
-    fn link_framework(&mut self, _framework: Symbol) {
+    fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) {
         bug!("frameworks are not supported on windows")
     }
 
-    fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) {
-        self.link_staticlib(lib);
-        self.cmd.arg(format!("/WHOLEARCHIVE:{}.lib", lib));
+    fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, _search_path: &[PathBuf]) {
+        self.cmd.arg(format!("/WHOLEARCHIVE:{}{}", lib, if verbatim { "" } else { ".lib" }));
     }
     fn link_whole_rlib(&mut self, path: &Path) {
-        self.link_rlib(path);
         let mut arg = OsString::from("/WHOLEARCHIVE:");
         arg.push(path);
         self.cmd.arg(arg);
@@ -917,7 +969,7 @@ fn include_path(&mut self, path: &Path) {
         self.cmd.arg("-L").arg(path);
     }
 
-    fn link_staticlib(&mut self, lib: Symbol) {
+    fn link_staticlib(&mut self, lib: Symbol, _verbatim: bool) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
@@ -929,14 +981,14 @@ fn add_object(&mut self, path: &Path) {
         self.cmd.arg(path);
     }
 
-    fn link_dylib(&mut self, lib: Symbol) {
+    fn link_dylib(&mut self, lib: Symbol, verbatim: bool, _as_needed: bool) {
         // Emscripten always links statically
-        self.link_staticlib(lib);
+        self.link_staticlib(lib, verbatim);
     }
 
-    fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) {
+    fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, _search_path: &[PathBuf]) {
         // not supported?
-        self.link_staticlib(lib);
+        self.link_staticlib(lib, verbatim);
     }
 
     fn link_whole_rlib(&mut self, lib: &Path) {
@@ -945,7 +997,7 @@ fn link_whole_rlib(&mut self, lib: &Path) {
     }
 
     fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) {
-        self.link_dylib(lib);
+        self.link_dylib(lib, false, true);
     }
 
     fn link_rlib(&mut self, lib: &Path) {
@@ -968,7 +1020,7 @@ fn framework_path(&mut self, _path: &Path) {
         bug!("frameworks are not supported on Emscripten")
     }
 
-    fn link_framework(&mut self, _framework: Symbol) {
+    fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) {
         bug!("frameworks are not supported on Emscripten")
     }
 
@@ -976,6 +1028,10 @@ fn gc_sections(&mut self, _keep_metadata: bool) {
         // noop
     }
 
+    fn no_gc_sections(&mut self) {
+        // noop
+    }
+
     fn optimize(&mut self) {
         // Emscripten performs own optimizations
         self.cmd.arg(match self.sess.opts.optimize {
@@ -1119,11 +1175,11 @@ fn set_output_kind(&mut self, output_kind: LinkOutputKind, _out_filename: &Path)
         }
     }
 
-    fn link_dylib(&mut self, lib: Symbol) {
+    fn link_dylib(&mut self, lib: Symbol, _verbatim: bool, _as_needed: bool) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
-    fn link_staticlib(&mut self, lib: Symbol) {
+    fn link_staticlib(&mut self, lib: Symbol, _verbatim: bool) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
@@ -1157,11 +1213,11 @@ fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
-    fn link_framework(&mut self, _framework: Symbol) {
+    fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) {
         panic!("frameworks not supported")
     }
 
-    fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) {
+    fn link_whole_staticlib(&mut self, lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
@@ -1173,6 +1229,10 @@ fn gc_sections(&mut self, _keep_metadata: bool) {
         self.cmd.arg("--gc-sections");
     }
 
+    fn no_gc_sections(&mut self) {
+        self.cmd.arg("--no-gc-sections");
+    }
+
     fn optimize(&mut self) {
         self.cmd.arg(match self.sess.opts.optimize {
             OptLevel::No => "-O0",
@@ -1327,7 +1387,7 @@ fn finalize(&mut self) {
         });
     }
 
-    fn link_dylib(&mut self, _lib: Symbol) {
+    fn link_dylib(&mut self, _lib: Symbol, _verbatim: bool, _as_needed: bool) {
         panic!("external dylibs not supported")
     }
 
@@ -1335,11 +1395,11 @@ fn link_rust_dylib(&mut self, _lib: Symbol, _path: &Path) {
         panic!("external dylibs not supported")
     }
 
-    fn link_staticlib(&mut self, _lib: Symbol) {
+    fn link_staticlib(&mut self, _lib: Symbol, _verbatim: bool) {
         panic!("staticlibs not supported")
     }
 
-    fn link_whole_staticlib(&mut self, _lib: Symbol, _search_path: &[PathBuf]) {
+    fn link_whole_staticlib(&mut self, _lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) {
         panic!("staticlibs not supported")
     }
 
@@ -1347,7 +1407,7 @@ fn framework_path(&mut self, _path: &Path) {
         panic!("frameworks not supported")
     }
 
-    fn link_framework(&mut self, _framework: Symbol) {
+    fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) {
         panic!("frameworks not supported")
     }
 
@@ -1359,6 +1419,8 @@ fn no_relro(&mut self) {}
 
     fn gc_sections(&mut self, _keep_metadata: bool) {}
 
+    fn no_gc_sections(&mut self) {}
+
     fn pgo_gen(&mut self) {}
 
     fn no_crt_objects(&mut self) {}
index 0ff05229466ac92847e5b7574551428015b0da62..d6ae689f254b1508b2f48d7ce75b5378ce43838f 100644 (file)
@@ -72,7 +72,7 @@ pub unsafe fn optimize(
                 let module = module.take().unwrap();
                 {
                     let config = cgcx.config(module.kind);
-                    B::run_lto_pass_manager(cgcx, &module, config, false);
+                    B::run_lto_pass_manager(cgcx, &module, config, false)?;
                 }
                 Ok(module)
             }
index c8688faa80bc394667a22171f6bd9dc506fcf053..0dfb007a2473e31a6fc135add423bcc8475f9282 100644 (file)
@@ -84,6 +84,8 @@ pub struct ModuleConfig {
 
     pub pgo_gen: SwitchWithOptPath,
     pub pgo_use: Option<PathBuf>,
+    pub instrument_coverage: bool,
+    pub instrument_gcov: bool,
 
     pub sanitizer: SanitizerSet,
     pub sanitizer_recover: SanitizerSet,
@@ -108,7 +110,7 @@ pub struct ModuleConfig {
     pub vectorize_slp: bool,
     pub merge_functions: bool,
     pub inline_threshold: Option<u32>,
-    pub new_llvm_pass_manager: bool,
+    pub new_llvm_pass_manager: Option<bool>,
     pub emit_lifetime_markers: bool,
 }
 
@@ -165,25 +167,7 @@ macro_rules! if_regular {
         };
 
         ModuleConfig {
-            passes: if_regular!(
-                {
-                    let mut passes = sess.opts.cg.passes.clone();
-                    // compiler_builtins overrides the codegen-units settings,
-                    // which is incompatible with -Zprofile which requires that
-                    // only a single codegen unit is used per crate.
-                    if sess.opts.debugging_opts.profile && !is_compiler_builtins {
-                        passes.push("insert-gcov-profiling".to_owned());
-                    }
-
-                    // The rustc option `-Zinstrument_coverage` injects intrinsic calls to
-                    // `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass.
-                    if sess.instrument_coverage() {
-                        passes.push("instrprof".to_owned());
-                    }
-                    passes
-                },
-                vec![]
-            ),
+            passes: if_regular!(sess.opts.cg.passes.clone(), vec![]),
 
             opt_level: opt_level_and_size,
             opt_size: opt_level_and_size,
@@ -193,6 +177,14 @@ macro_rules! if_regular {
                 SwitchWithOptPath::Disabled
             ),
             pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None),
+            instrument_coverage: if_regular!(sess.instrument_coverage(), false),
+            instrument_gcov: if_regular!(
+                // compiler_builtins overrides the codegen-units settings,
+                // which is incompatible with -Zprofile which requires that
+                // only a single codegen unit is used per crate.
+                sess.opts.debugging_opts.profile && !is_compiler_builtins,
+                false
+            ),
 
             sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer, SanitizerSet::empty()),
             sanitizer_recover: if_regular!(
index e045a23eb0ce3bcb62d52f7f3e0906be1d9dc6e7..7a19b0e4d5ac3565205aaa74147e05f6f75cb010 100644 (file)
@@ -357,20 +357,9 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         if !cx.codegen_unit().contains_item(&MonoItem::Fn(instance)) {
             return None;
         }
-    } else {
-        // FIXME: Add support for non-local main fn codegen
-        let span = cx.tcx().main_def.unwrap().span;
-        let n = 28937;
-        cx.sess()
-            .struct_span_err(span, "entry symbol `main` from foreign crate is not yet supported.")
-            .note(&format!(
-                "see issue #{} <https://github.com/rust-lang/rust/issues/{}> \
-                 for more information",
-                n, n,
-            ))
-            .emit();
-        cx.sess().abort_if_errors();
-        bug!();
+    } else if !cx.codegen_unit().is_primary() {
+        // We want to create the wrapper only when the codegen unit is the primary one
+        return None;
     }
 
     let main_llfn = cx.get_fn_addr(instance);
index f0f45b067b35261a81230ae8436f634e47cf4ac7..1b53b551901645601bece68dc0a8e54f1f11c004 100644 (file)
@@ -114,11 +114,12 @@ pub struct NativeLib {
     pub kind: NativeLibKind,
     pub name: Option<Symbol>,
     pub cfg: Option<ast::MetaItem>,
+    pub verbatim: Option<bool>,
 }
 
 impl From<&cstore::NativeLib> for NativeLib {
     fn from(lib: &cstore::NativeLib) -> Self {
-        NativeLib { kind: lib.kind, name: lib.name, cfg: lib.cfg.clone() }
+        NativeLib { kind: lib.kind, name: lib.name, cfg: lib.cfg.clone(), verbatim: lib.verbatim }
     }
 }
 
index 264e7c2aa92c0c44551aa53a7f20166ba238016b..93fbee2b49bb57a84c9921982cf00e5543e6e15a 100644 (file)
@@ -58,7 +58,7 @@ fn run_lto_pass_manager(
         llmod: &ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
         thin: bool,
-    );
+    ) -> Result<(), FatalError>;
 }
 
 pub trait ThinBufferMethods: Send + Sync {
index a5b2df1da5d6d8c6d83fab062cdaecbb7506caa1..29d685ab530d63f035f889d0b007a1a6751d7dae 100644 (file)
@@ -336,12 +336,13 @@ pub fn register_obligation(&mut self, obligation: O) {
 
     // Returns Err(()) if we already know this obligation failed.
     fn register_obligation_at(&mut self, obligation: O, parent: Option<usize>) -> Result<(), ()> {
-        if self.done_cache.contains(&obligation.as_cache_key()) {
+        let cache_key = obligation.as_cache_key();
+        if self.done_cache.contains(&cache_key) {
             debug!("register_obligation_at: ignoring already done obligation: {:?}", obligation);
             return Ok(());
         }
 
-        match self.active_cache.entry(obligation.as_cache_key()) {
+        match self.active_cache.entry(cache_key.clone()) {
             Entry::Occupied(o) => {
                 let node = &mut self.nodes[*o.get()];
                 if let Some(parent_index) = parent {
@@ -365,7 +366,7 @@ fn register_obligation_at(&mut self, obligation: O, parent: Option<usize>) -> Re
                     && self
                         .error_cache
                         .get(&obligation_tree_id)
-                        .map(|errors| errors.contains(&obligation.as_cache_key()))
+                        .map(|errors| errors.contains(&cache_key))
                         .unwrap_or(false);
 
                 if already_failed {
index b2f6a0c10142df0645e35d21f3b6ad9e3a18d5b5..14ccced2c6a56678fd4e6f4ac2d5d34555a57c52 100644 (file)
@@ -282,6 +282,22 @@ pub fn multipart_suggestion(
         msg: &str,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
+    ) -> &mut Self {
+        self.multipart_suggestion_with_style(
+            msg,
+            suggestion,
+            applicability,
+            SuggestionStyle::ShowCode,
+        )
+    }
+
+    /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
+    pub fn multipart_suggestion_with_style(
+        &mut self,
+        msg: &str,
+        suggestion: Vec<(Span, String)>,
+        applicability: Applicability,
+        style: SuggestionStyle,
     ) -> &mut Self {
         assert!(!suggestion.is_empty());
         self.suggestions.push(CodeSuggestion {
@@ -292,7 +308,7 @@ pub fn multipart_suggestion(
                     .collect(),
             }],
             msg: msg.to_owned(),
-            style: SuggestionStyle::ShowCode,
+            style,
             applicability,
             tool_metadata: Default::default(),
         });
index f1a31f0d4f5c9eac35b9fd29d8520afc838f26a3..dc1664bb2baa866f5ff53d8a2c0b8b7075bf56be 100644 (file)
@@ -294,6 +294,7 @@ impl error::Error for ExplicitBug {}
 
 pub use diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, SubDiagnostic};
 pub use diagnostic_builder::DiagnosticBuilder;
+use std::backtrace::Backtrace;
 
 /// A handler deals with errors and other compiler output.
 /// Certain errors (fatal, bug, unimpl) may cause immediate exit,
@@ -317,7 +318,7 @@ struct HandlerInner {
     deduplicated_err_count: usize,
     emitter: Box<dyn Emitter + sync::Send>,
     delayed_span_bugs: Vec<Diagnostic>,
-    delayed_good_path_bugs: Vec<Diagnostic>,
+    delayed_good_path_bugs: Vec<DelayedDiagnostic>,
 
     /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid
     /// emitting the same diagnostic with extended help (`--teach`) twice, which
@@ -388,7 +389,7 @@ fn drop(&mut self) {
         if !self.has_any_message() {
             let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new());
             self.flush_delayed(
-                bugs,
+                bugs.into_iter().map(DelayedDiagnostic::decorate).collect(),
                 "no warnings or errors encountered even though `delayed_good_path_bugs` issued",
             );
         }
@@ -633,9 +634,9 @@ pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> {
         DiagnosticBuilder::new(self, Level::Note, msg)
     }
 
-    pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> FatalError {
+    pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> ! {
         self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span);
-        FatalError
+        FatalError.raise()
     }
 
     pub fn span_fatal_with_code(
@@ -643,9 +644,9 @@ pub fn span_fatal_with_code(
         span: impl Into<MultiSpan>,
         msg: &str,
         code: DiagnosticId,
-    ) -> FatalError {
+    ) -> ! {
         self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span);
-        FatalError
+        FatalError.raise()
     }
 
     pub fn span_err(&self, span: impl Into<MultiSpan>, msg: &str) {
@@ -691,6 +692,7 @@ pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_> {
         db
     }
 
+    // NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread
     pub fn fatal(&self, msg: &str) -> FatalError {
         self.inner.borrow_mut().fatal(msg)
     }
@@ -968,12 +970,12 @@ fn delay_span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) {
     }
 
     fn delay_good_path_bug(&mut self, msg: &str) {
-        let mut diagnostic = Diagnostic::new(Level::Bug, msg);
+        let diagnostic = Diagnostic::new(Level::Bug, msg);
         if self.flags.report_delayed_bugs {
             self.emit_diagnostic(&diagnostic);
         }
-        diagnostic.note(&format!("delayed at {}", std::backtrace::Backtrace::force_capture()));
-        self.delayed_good_path_bugs.push(diagnostic);
+        let backtrace = std::backtrace::Backtrace::force_capture();
+        self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
     }
 
     fn failure(&mut self, msg: &str) {
@@ -1042,6 +1044,22 @@ fn panic_if_treat_err_as_bug(&self) {
     }
 }
 
+struct DelayedDiagnostic {
+    inner: Diagnostic,
+    note: Backtrace,
+}
+
+impl DelayedDiagnostic {
+    fn with_backtrace(diagnostic: Diagnostic, backtrace: Backtrace) -> Self {
+        DelayedDiagnostic { inner: diagnostic, note: backtrace }
+    }
+
+    fn decorate(mut self) -> Diagnostic {
+        self.inner.note(&format!("delayed at {}", self.note));
+        self.inner
+    }
+}
+
 #[derive(Copy, PartialEq, Clone, Hash, Debug, Encodable, Decodable)]
 pub enum Level {
     Bug,
index 03c83f9c07b5d99acd3b1b7d6efdfe1614a1456c..f9140609c0f3c86f048d0b542c4feddb75adba82 100644 (file)
@@ -464,31 +464,9 @@ fn in_cfg(&self, attrs: &[Attribute]) -> bool {
                     return true;
                 }
             };
-            let error = |span, msg, suggestion: &str| {
-                let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
-                if !suggestion.is_empty() {
-                    err.span_suggestion(
-                        span,
-                        "expected syntax is",
-                        suggestion.into(),
-                        Applicability::MaybeIncorrect,
-                    );
-                }
-                err.emit();
-                true
-            };
-            let span = meta_item.span;
-            match meta_item.meta_item_list() {
-                None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
-                Some([]) => error(span, "`cfg` predicate is not specified", ""),
-                Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
-                Some([single]) => match single.meta_item() {
-                    Some(meta_item) => {
-                        attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
-                    }
-                    None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
-                },
-            }
+            parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
+                attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
+            })
         })
     }
 
@@ -532,6 +510,32 @@ pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) {
     }
 }
 
+pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> {
+    let error = |span, msg, suggestion: &str| {
+        let mut err = sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
+        if !suggestion.is_empty() {
+            err.span_suggestion(
+                span,
+                "expected syntax is",
+                suggestion.into(),
+                Applicability::HasPlaceholders,
+            );
+        }
+        err.emit();
+        None
+    };
+    let span = meta_item.span;
+    match meta_item.meta_item_list() {
+        None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
+        Some([]) => error(span, "`cfg` predicate is not specified", ""),
+        Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
+        Some([single]) => match single.meta_item() {
+            Some(meta_item) => Some(meta_item),
+            None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
+        },
+    }
+}
+
 fn is_cfg(sess: &Session, attr: &Attribute) -> bool {
     sess.check_name(attr, sym::cfg)
 }
index 993522d01d86744bec64592c31f698d58a0e7ae2..4d777049f0d627614d9d322fa7b47d0f175c7753 100644 (file)
@@ -36,7 +36,7 @@ pub struct ModulePathSuccess {
 pub enum ModError<'a> {
     CircularInclusion(Vec<PathBuf>),
     ModInBlock(Option<Ident>),
-    FileNotFound(Ident, PathBuf),
+    FileNotFound(Ident, PathBuf, PathBuf),
     MultipleCandidates(Ident, PathBuf, PathBuf),
     ParserError(DiagnosticBuilder<'a>),
 }
@@ -219,7 +219,7 @@ pub fn default_submod_path<'a>(
             file_path: secondary_path,
             dir_ownership: DirOwnership::Owned { relative: None },
         }),
-        (false, false) => Err(ModError::FileNotFound(ident, default_path)),
+        (false, false) => Err(ModError::FileNotFound(ident, default_path, secondary_path)),
         (true, true) => Err(ModError::MultipleCandidates(ident, default_path, secondary_path)),
     }
 }
@@ -247,7 +247,7 @@ fn report(self, sess: &Session, span: Span) {
                 }
                 err
             }
-            ModError::FileNotFound(ident, default_path) => {
+            ModError::FileNotFound(ident, default_path, secondary_path) => {
                 let mut err = struct_span_err!(
                     diag,
                     span,
@@ -256,9 +256,10 @@ fn report(self, sess: &Session, span: Span) {
                     ident,
                 );
                 err.help(&format!(
-                    "to create the module `{}`, create file \"{}\"",
+                    "to create the module `{}`, create file \"{}\" or \"{}\"",
                     ident,
                     default_path.display(),
+                    secondary_path.display(),
                 ));
                 err
             }
index 4ad5f085ad05502cf9f30de44536ca6c6695ea7f..535cb13276646b7f5bd145efbe7b76ec0fc9e170 100644 (file)
@@ -274,9 +274,6 @@ pub fn set(&self, features: &mut Features, span: Span) {
     /// Allows using non lexical lifetimes (RFC 2094).
     (active, nll, "1.0.0", Some(43234), None),
 
-    /// Allows the definition of `const` functions with some advanced features.
-    (active, const_fn, "1.2.0", Some(57563), None),
-
     /// Allows associated type defaults.
     (active, associated_type_defaults, "1.2.0", Some(29661), None),
 
@@ -656,6 +653,21 @@ pub fn set(&self, features: &mut Features, span: Span) {
     /// Allows using imported `main` function
     (active, imported_main, "1.53.0", Some(28937), None),
 
+    /// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]`
+    (active, native_link_modifiers, "1.53.0", Some(81490), None),
+
+    /// Allows specifying the bundle link modifier
+    (active, native_link_modifiers_bundle, "1.53.0", Some(81490), None),
+
+    /// Allows specifying the verbatim link modifier
+    (active, native_link_modifiers_verbatim, "1.53.0", Some(81490), None),
+
+    /// Allows specifying the whole-archive link modifier
+    (active, native_link_modifiers_whole_archive, "1.53.0", Some(81490), None),
+
+    /// Allows specifying the as-needed link modifier
+    (active, native_link_modifiers_as_needed, "1.53.0", Some(81490), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
@@ -683,6 +695,11 @@ pub fn set(&self, features: &mut Features, span: Span) {
     sym::const_generics_defaults,
     sym::inherent_associated_types,
     sym::type_alias_impl_trait,
+    sym::native_link_modifiers,
+    sym::native_link_modifiers_bundle,
+    sym::native_link_modifiers_verbatim,
+    sym::native_link_modifiers_whole_archive,
+    sym::native_link_modifiers_as_needed,
 ];
 
 /// Some features are not allowed to be used together at the same time, if
index 5474fea9c78577c7b843d922e685c00a2bb8e83a..a8719be84c2a44c68ed793f65bd3b16a36c0cc52 100644 (file)
@@ -273,13 +273,7 @@ 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),
-    ),
+    gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)),
 
     // FIXME: #14408 assume docs are used since rustdoc looks at them.
     ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
index fa8ef182aeddf2a0eaf815fe6503742cbb304697..138398825af586123ec3c59e07c4fdd96f556113 100644 (file)
@@ -136,6 +136,9 @@ macro_rules! declare_features {
     (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")),
+     /// Allows the definition of `const` functions with some advanced features.
+    (removed, const_fn, "1.54.0", Some(57563), None,
+     Some("split into finer-grained feature gates")),
 
     // -------------------------------------------------------------------------
     // feature-group-end: removed features
index a91bd9ce2ff7474c085d5e189401fcc9e76f9e6f..1cafb2fe1a24f48be2c35cb106bb53a5c9973a2a 100644 (file)
@@ -2398,9 +2398,6 @@ fn report_inference_failure(
                 self.tcx.associated_item(def_id).ident
             ),
             infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name),
-            infer::BoundRegionInCoherence(name) => {
-                format!(" for lifetime parameter `{}` in coherence check", name)
-            }
             infer::UpvarRegion(ref upvar_id, _) => {
                 let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
                 format!(" for capture of `{}` by closure", var_name)
index eaec6b46bcd1456c30b15262808f3c684e1a0b7a..f39431f2494b1868f53c19f58ff893eb0c93a614 100644 (file)
@@ -453,8 +453,6 @@ pub enum RegionVariableOrigin {
 
     UpvarRegion(ty::UpvarId, Span),
 
-    BoundRegionInCoherence(Symbol),
-
     /// This origin is used for the inference variables that we create
     /// during NLL region processing.
     Nll(NllRegionVariableOrigin),
@@ -1749,7 +1747,6 @@ pub fn span(&self) -> Span {
             | EarlyBoundRegion(a, ..)
             | LateBoundRegion(a, ..)
             | UpvarRegion(_, a) => a,
-            BoundRegionInCoherence(_) => rustc_span::DUMMY_SP,
             Nll(..) => bug!("NLL variable used with `span`"),
         }
     }
index d8c1a7a26822085b60b5ec80dff19f1caea65baa..fd13cb3d59acd203b48255f5f09ca60c4ffc574e 100644 (file)
@@ -12,7 +12,7 @@
 };
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
-use rustc_session::utils::{CanonicalizedPath, NativeLibKind};
+use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use rustc_session::{build_session, getopts, DiagnosticOutput, Session};
 use rustc_span::edition::{Edition, DEFAULT_EDITION};
 use rustc_span::symbol::sym;
@@ -303,38 +303,122 @@ fn test_native_libs_tracking_hash_different_values() {
     let mut v2 = Options::default();
     let mut v3 = Options::default();
     let mut v4 = Options::default();
+    let mut v5 = Options::default();
 
     // Reference
     v1.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), None, NativeLibKind::Framework),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     // Change label
     v2.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("X"), None, NativeLibKind::Framework),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("X"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     // Change kind
     v3.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), None, NativeLibKind::StaticBundle),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     // Change new-name
     v4.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), Some(String::from("X")), NativeLibKind::Framework),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: Some(String::from("X")),
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
+    ];
+
+    // Change verbatim
+    v5.libs = vec![
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: Some(true),
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     assert_different_hash(&v1, &v2);
     assert_different_hash(&v1, &v3);
     assert_different_hash(&v1, &v4);
+    assert_different_hash(&v1, &v5);
 }
 
 #[test]
@@ -345,21 +429,66 @@ fn test_native_libs_tracking_hash_different_order() {
 
     // Reference
     v1.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), None, NativeLibKind::Framework),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     v2.libs = vec![
-        (String::from("b"), None, NativeLibKind::Framework),
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     v3.libs = vec![
-        (String::from("c"), None, NativeLibKind::Unspecified),
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), None, NativeLibKind::Framework),
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
     ];
 
     assert_same_hash(&v1, &v2);
@@ -580,7 +709,7 @@ macro_rules! tracked {
     tracked!(mir_emit_retag, true);
     tracked!(mir_opt_level, Some(4));
     tracked!(mutable_noalias, Some(true));
-    tracked!(new_llvm_pass_manager, true);
+    tracked!(new_llvm_pass_manager, Some(true));
     tracked!(no_codegen, true);
     tracked!(no_generate_arange_section, true);
     tracked!(no_link, true);
index 59488fc80a5e2e312b889bbe4b64926c11011ad5..fd29053433e5555910730018003f6e38bddfe54d 100644 (file)
@@ -423,8 +423,7 @@ pub fn get_codegen_sysroot(
         .iter()
         .chain(sysroot_candidates.iter())
         .map(|sysroot| {
-            let libdir = filesearch::relative_target_lib_path(&sysroot, &target);
-            sysroot.join(libdir).with_file_name("codegen-backends")
+            filesearch::make_target_lib_path(&sysroot, &target).with_file_name("codegen-backends")
         })
         .find(|f| {
             info!("codegen backend candidate: {}", f.display());
index 54fcaef414f2238169f2a991b901b631ebc111bd..eb2e495f73d3c31a5392afe7ed590b802129c0fc 100644 (file)
@@ -367,11 +367,11 @@ pub fn check_ast_crate<T: EarlyLintPass>(
                 krate,
                 EarlyLintPassObjects { lints: &mut passes[..] },
                 buffered,
-                pre_expansion,
+                false,
             );
         }
     } else {
-        for pass in &mut passes {
+        for (i, pass) in passes.iter_mut().enumerate() {
             buffered =
                 sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| {
                     early_lint_crate(
@@ -380,7 +380,7 @@ pub fn check_ast_crate<T: EarlyLintPass>(
                         krate,
                         EarlyLintPassObjects { lints: slice::from_mut(pass) },
                         buffered,
-                        pre_expansion,
+                        pre_expansion && i == 0,
                     )
                 });
         }
index 5a27135581747f15e5d91d1412996406502c69f6..070bc3522a453c28cc59199bb086b585ab6e4d83 100644 (file)
@@ -4,7 +4,7 @@
 use rustc_hir as hir;
 use rustc_middle::ty;
 use rustc_parse_format::{ParseMode, Parser, Piece};
-use rustc_span::{sym, symbol::kw, InnerSpan, Span, Symbol};
+use rustc_span::{hygiene, sym, symbol::kw, symbol::SymbolStr, InnerSpan, Span, Symbol};
 
 declare_lint! {
     /// The `non_fmt_panic` lint detects `panic!(..)` invocations where the first
@@ -67,7 +67,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
 
     // The argument is *not* a string literal.
 
-    let (span, panic) = panic_call(cx, f);
+    let (span, panic, symbol_str) = panic_call(cx, f);
 
     // Find the span of the argument to `panic!()`, before expansion in the
     // case of `panic!(some_macro!())`.
@@ -95,7 +95,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
         }
         if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
             // A case of `panic!(format!(..))`.
-            l.note("the panic!() macro supports formatting, so there's no need for the format!() macro here");
+            l.note(format!("the {}!() macro supports formatting, so there's no need for the format!() macro here", symbol_str).as_str());
             if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
                 l.multipart_suggestion(
                     "remove the `format!(..)` macro call",
@@ -160,7 +160,7 @@ fn check_panic_str<'tcx>(
         Parser::new(fmt.as_ref(), style, snippet.clone(), false, ParseMode::Format);
     let n_arguments = (&mut fmt_parser).filter(|a| matches!(a, Piece::NextArgument(_))).count();
 
-    let (span, _) = panic_call(cx, f);
+    let (span, _, _) = panic_call(cx, f);
 
     if n_arguments > 0 && fmt_parser.errors.is_empty() {
         let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] {
@@ -230,7 +230,7 @@ fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Sp
     ))
 }
 
-fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol) {
+fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol, SymbolStr) {
     let mut expn = f.span.ctxt().outer_expn_data();
 
     let mut panic_macro = kw::Empty;
@@ -248,5 +248,10 @@ fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span,
         }
     }
 
-    (expn.call_site, panic_macro)
+    let macro_symbol = if let hygiene::ExpnKind::Macro(_, symbol) = expn.kind {
+        symbol
+    } else {
+        Symbol::intern("panic")
+    };
+    (expn.call_site, panic_macro, macro_symbol.as_str())
 }
index 65a988629c3b2646127e953561dd709dfb3f2470..99ce13a6ed59c8b95ba939a84a904f4fe9adc639 100644 (file)
@@ -32,6 +32,8 @@
 #include "llvm/Transforms/Instrumentation.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
 #include "llvm/Support/TimeProfiler.h"
+#include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
+#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
 #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
 #include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
 #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
@@ -734,7 +736,7 @@ struct LLVMRustSanitizerOptions {
   bool SanitizeHWAddressRecover;
 };
 
-extern "C" void
+extern "C" LLVMRustResult
 LLVMRustOptimizeWithNewPassManager(
     LLVMModuleRef ModuleRef,
     LLVMTargetMachineRef TMRef,
@@ -745,9 +747,11 @@ LLVMRustOptimizeWithNewPassManager(
     bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers,
     LLVMRustSanitizerOptions *SanitizerOptions,
     const char *PGOGenPath, const char *PGOUsePath,
+    bool InstrumentCoverage, bool InstrumentGCOV,
     void* LlvmSelfProfiler,
     LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
-    LLVMRustSelfProfileAfterPassCallback AfterPassCallback) {
+    LLVMRustSelfProfileAfterPassCallback AfterPassCallback,
+    const char *ExtraPasses, size_t ExtraPassesLen) {
   Module *TheModule = unwrap(ModuleRef);
   TargetMachine *TM = unwrap(TMRef);
   PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust);
@@ -834,6 +838,23 @@ LLVMRustOptimizeWithNewPassManager(
     );
   }
 
+  if (InstrumentGCOV) {
+    PipelineStartEPCallbacks.push_back(
+      [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
+        MPM.addPass(GCOVProfilerPass(GCOVOptions::getDefault()));
+      }
+    );
+  }
+
+  if (InstrumentCoverage) {
+    PipelineStartEPCallbacks.push_back(
+      [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
+        InstrProfOptions Options;
+        MPM.addPass(InstrProfiling(Options, false));
+      }
+    );
+  }
+
   if (SanitizerOptions) {
     if (SanitizerOptions->SanitizeMemory) {
       MemorySanitizerOptions Options(
@@ -1042,6 +1063,14 @@ LLVMRustOptimizeWithNewPassManager(
     }
   }
 
+  if (ExtraPassesLen) {
+    if (auto Err = PB.parsePassPipeline(MPM, StringRef(ExtraPasses, ExtraPassesLen))) {
+      std::string ErrMsg = toString(std::move(Err));
+      LLVMRustSetLastError(ErrMsg.c_str());
+      return LLVMRustResult::Failure;
+    }
+  }
+
   if (NeedThinLTOBufferPasses) {
     MPM.addPass(CanonicalizeAliasesPass());
     MPM.addPass(NameAnonGlobalPass());
@@ -1052,6 +1081,7 @@ LLVMRustOptimizeWithNewPassManager(
     UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove
 
   MPM.run(*TheModule, MAM);
+  return LLVMRustResult::Success;
 }
 
 // Callback to demangle function name
index 523e016eeb9f2726dbc700504b8d7634261de200..bc342119efb991caee85c946c82fa7c9f5331a41 100644 (file)
@@ -8,8 +8,8 @@
 use rustc_session::parse::feature_err;
 use rustc_session::utils::NativeLibKind;
 use rustc_session::Session;
-use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
 crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
@@ -56,6 +56,7 @@ fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
                 cfg: None,
                 foreign_module: Some(it.def_id.to_def_id()),
                 wasm_import_module: None,
+                verbatim: None,
             };
             let mut kind_specified = false;
 
@@ -67,10 +68,18 @@ fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
                         None => continue, // skip like historical compilers
                     };
                     lib.kind = match &*kind.as_str() {
-                        "static" => NativeLibKind::StaticBundle,
-                        "static-nobundle" => NativeLibKind::StaticNoBundle,
-                        "dylib" => NativeLibKind::Dylib,
-                        "framework" => NativeLibKind::Framework,
+                        "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
+                        "static-nobundle" => {
+                            sess.struct_span_warn(
+                                item.span(),
+                                "library kind `static-nobundle` has been superseded by specifying \
+                                modifier `-bundle` with library kind `static`",
+                            )
+                            .emit();
+                            NativeLibKind::Static { bundle: Some(false), whole_archive: None }
+                        }
+                        "dylib" => NativeLibKind::Dylib { as_needed: None },
+                        "framework" => NativeLibKind::Framework { as_needed: None },
                         "raw-dylib" => NativeLibKind::RawDylib,
                         k => {
                             struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
@@ -108,6 +117,71 @@ fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
                 }
             }
 
+            // Do this outside the above loop so we don't depend on modifiers coming
+            // after kinds
+            if let Some(item) = items.iter().find(|item| item.has_name(sym::modifiers)) {
+                if let Some(modifiers) = item.value_str() {
+                    let span = item.name_value_literal_span().unwrap();
+                    for modifier in modifiers.as_str().split(',') {
+                        let (modifier, value) = match modifier.strip_prefix(&['+', '-'][..]) {
+                            Some(m) => (m, modifier.starts_with('+')),
+                            None => {
+                                sess.span_err(
+                                    span,
+                                    "invalid linking modifier syntax, expected '+' or '-' prefix \
+                                    before one of: bundle, verbatim, whole-archive, as-needed",
+                                );
+                                continue;
+                            }
+                        };
+
+                        match (modifier, &mut lib.kind) {
+                            ("bundle", NativeLibKind::Static { bundle, .. }) => {
+                                *bundle = Some(value);
+                            }
+                            ("bundle", _) => sess.span_err(
+                                span,
+                                "bundle linking modifier is only compatible with \
+                                `static` linking kind",
+                            ),
+
+                            ("verbatim", _) => lib.verbatim = Some(value),
+
+                            ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
+                                *whole_archive = Some(value);
+                            }
+                            ("whole-archive", _) => sess.span_err(
+                                span,
+                                "whole-archive linking modifier is only compatible with \
+                                `static` linking kind",
+                            ),
+
+                            ("as-needed", NativeLibKind::Dylib { as_needed })
+                            | ("as-needed", NativeLibKind::Framework { as_needed }) => {
+                                *as_needed = Some(value);
+                            }
+                            ("as-needed", _) => sess.span_err(
+                                span,
+                                "as-needed linking modifier is only compatible with \
+                                `dylib` and `framework` linking kinds",
+                            ),
+
+                            _ => sess.span_err(
+                                span,
+                                &format!(
+                                    "unrecognized linking modifier `{}`, expected one \
+                                    of: bundle, verbatim, whole-archive, as-needed",
+                                    modifier
+                                ),
+                            ),
+                        }
+                    }
+                } else {
+                    let msg = "must be of the form `#[link(modifiers = \"...\")]`";
+                    sess.span_err(item.span(), msg);
+                }
+            }
+
             // In general we require #[link(name = "...")] but we allow
             // #[link(wasm_import_module = "...")] without the `name`.
             let requires_name = kind_specified || lib.wasm_import_module.is_none();
@@ -152,7 +226,7 @@ fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) {
             return;
         }
         let is_osx = self.tcx.sess.target.is_like_osx;
-        if lib.kind == NativeLibKind::Framework && !is_osx {
+        if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx {
             let msg = "native frameworks are only available on macOS targets";
             match span {
                 Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(),
@@ -168,7 +242,9 @@ fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) {
             )
             .emit();
         }
-        if lib.kind == NativeLibKind::StaticNoBundle && !self.tcx.features().static_nobundle {
+        if matches!(lib.kind, NativeLibKind::Static { bundle: Some(false), .. })
+            && !self.tcx.features().static_nobundle
+        {
             feature_err(
                 &self.tcx.sess.parse_sess,
                 sym::static_nobundle,
@@ -193,30 +269,30 @@ fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLib) {
     fn process_command_line(&mut self) {
         // First, check for errors
         let mut renames = FxHashSet::default();
-        for (name, new_name, _) in &self.tcx.sess.opts.libs {
-            if let Some(ref new_name) = new_name {
+        for lib in &self.tcx.sess.opts.libs {
+            if let Some(ref new_name) = lib.new_name {
                 let any_duplicate = self
                     .libs
                     .iter()
                     .filter_map(|lib| lib.name.as_ref())
-                    .any(|n| &n.as_str() == name);
+                    .any(|n| &n.as_str() == &lib.name);
                 if new_name.is_empty() {
                     self.tcx.sess.err(&format!(
                         "an empty renaming target was specified for library `{}`",
-                        name
+                        lib.name
                     ));
                 } else if !any_duplicate {
                     self.tcx.sess.err(&format!(
                         "renaming of the library `{}` was specified, \
                                                 however this crate contains no `#[link(...)]` \
                                                 attributes referencing this library.",
-                        name
+                        lib.name
                     ));
-                } else if !renames.insert(name) {
+                } else if !renames.insert(&lib.name) {
                     self.tcx.sess.err(&format!(
                         "multiple renamings were \
                                                 specified for library `{}` .",
-                        name
+                        lib.name
                     ));
                 }
             }
@@ -229,7 +305,7 @@ fn process_command_line(&mut self) {
         // it.  (This ensures that the linker is able to see symbols from
         // all possible dependent libraries before linking in the library
         // in question.)
-        for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
+        for passed_lib in &self.tcx.sess.opts.libs {
             // If we've already added any native libraries with the same
             // name, they will be pulled out into `existing`, so that we
             // can move them to the end of the list below.
@@ -237,13 +313,14 @@ fn process_command_line(&mut self) {
                 .libs
                 .drain_filter(|lib| {
                     if let Some(lib_name) = lib.name {
-                        if lib_name.as_str() == *name {
-                            if kind != NativeLibKind::Unspecified {
-                                lib.kind = kind;
+                        if lib_name.as_str() == passed_lib.name {
+                            if passed_lib.kind != NativeLibKind::Unspecified {
+                                lib.kind = passed_lib.kind;
                             }
-                            if let Some(new_name) = new_name {
+                            if let Some(new_name) = &passed_lib.new_name {
                                 lib.name = Some(Symbol::intern(new_name));
                             }
+                            lib.verbatim = passed_lib.verbatim;
                             return true;
                         }
                     }
@@ -252,13 +329,14 @@ fn process_command_line(&mut self) {
                 .collect::<Vec<_>>();
             if existing.is_empty() {
                 // Add if not found
-                let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
+                let new_name = passed_lib.new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
                 let lib = NativeLib {
-                    name: Some(Symbol::intern(new_name.unwrap_or(name))),
-                    kind,
+                    name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
+                    kind: passed_lib.kind,
                     cfg: None,
                     foreign_module: None,
                     wasm_import_module: None,
+                    verbatim: passed_lib.verbatim,
                 };
                 self.register_native_lib(None, lib);
             } else {
index 8595a70dd9478a759d3bddf53c3a171a989e8b04..b11ad6c7ff86757450be57681c88ab9921076d7b 100644 (file)
@@ -256,16 +256,13 @@ pub fn provide(providers: &mut Providers) {
     // resolve! Does this work? Unsure! That's what the issue is about
     *providers = Providers {
         is_dllimport_foreign_item: |tcx, id| match tcx.native_library_kind(id) {
-            Some(NativeLibKind::Dylib | NativeLibKind::RawDylib | NativeLibKind::Unspecified) => {
-                true
-            }
+            Some(
+                NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified,
+            ) => true,
             _ => false,
         },
         is_statically_included_foreign_item: |tcx, id| {
-            matches!(
-                tcx.native_library_kind(id),
-                Some(NativeLibKind::StaticBundle | NativeLibKind::StaticNoBundle)
-            )
+            matches!(tcx.native_library_kind(id), Some(NativeLibKind::Static { .. }))
         },
         native_library_kind: |tcx, id| {
             tcx.native_libraries(id.krate)
index 73f3b550c3721f32ca5efb7c00d596eb4c28acad..09736c6f1e5069ffb2eae46705506c7fbc082e7e 100644 (file)
@@ -1,6 +1,6 @@
 use self::collector::NodeCollector;
 
-use crate::hir::{HirOwnerData, IndexedHir};
+use crate::hir::{AttributeMap, HirOwnerData, IndexedHir};
 use crate::middle::cstore::CrateStore;
 use crate::ty::TyCtxt;
 use rustc_ast as ast;
@@ -943,14 +943,19 @@ pub(super) fn index_hir<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> &'tcx Indexe
 }
 
 pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
+    let mut hcx = tcx.create_stable_hashing_context();
+
     let mut hir_body_nodes: Vec<_> = tcx
         .index_hir(crate_num)
         .map
         .iter_enumerated()
         .filter_map(|(def_id, hod)| {
             let def_path_hash = tcx.definitions.def_path_hash(def_id);
-            let hash = hod.with_bodies.as_ref()?.hash;
-            Some((def_path_hash, hash))
+            let mut hasher = StableHasher::new();
+            hod.with_bodies.as_ref()?.hash_stable(&mut hcx, &mut hasher);
+            AttributeMap { map: &tcx.untracked_crate.attrs, prefix: def_id }
+                .hash_stable(&mut hcx, &mut hasher);
+            Some((def_path_hash, hasher.finish()))
         })
         .collect();
     hir_body_nodes.sort_unstable_by_key(|bn| bn.0);
@@ -980,13 +985,13 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
 
     source_file_names.sort_unstable();
 
-    let mut hcx = tcx.create_stable_hashing_context();
     let mut stable_hasher = StableHasher::new();
     node_hashes.hash_stable(&mut hcx, &mut stable_hasher);
     upstream_crates.hash_stable(&mut hcx, &mut stable_hasher);
     source_file_names.hash_stable(&mut hcx, &mut stable_hasher);
     tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
     tcx.sess.local_crate_disambiguator().to_fingerprint().hash_stable(&mut hcx, &mut stable_hasher);
+    tcx.untracked_crate.non_exported_macro_attrs.hash_stable(&mut hcx, &mut stable_hasher);
 
     let crate_hash: Fingerprint = stable_hasher.finish();
     Svh::new(crate_hash.to_smaller_hash())
index 4f1ca968c3018a342a3cc704da5ffe6da7d48636..82b9ebcc7eca32ca4edd1dc5c372851639809a4d 100644 (file)
@@ -94,6 +94,7 @@ pub struct NativeLib {
     pub cfg: Option<ast::MetaItem>,
     pub foreign_module: Option<DefId>,
     pub wasm_import_module: Option<Symbol>,
+    pub verbatim: Option<bool>,
 }
 
 #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
index e452463fbf5de8beb1acf99bd5860e7b06da050f..9c3bed6ec0ad8b8c618000eda597692e6526b5f6 100644 (file)
@@ -170,22 +170,25 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 /// Details of why a pointer had to be in-bounds.
 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
 pub enum CheckInAllocMsg {
+    /// We are access memory.
     MemoryAccessTest,
+    /// We are doing pointer arithmetic.
     PointerArithmeticTest,
+    /// None of the above -- generic/unspecific inbounds test.
     InboundsTest,
 }
 
 impl fmt::Display for CheckInAllocMsg {
     /// When this is printed as an error the context looks like this
-    /// "{test name} failed: pointer must be in-bounds at offset..."
+    /// "{msg}pointer must be in-bounds at offset..."
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(
             f,
             "{}",
             match *self {
-                CheckInAllocMsg::MemoryAccessTest => "memory access",
-                CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic",
-                CheckInAllocMsg::InboundsTest => "inbounds test",
+                CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
+                CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ",
+                CheckInAllocMsg::InboundsTest => "",
             }
         )
     }
@@ -299,15 +302,18 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             }
             PointerOutOfBounds { ptr, msg, allocation_size } => write!(
                 f,
-                "{} failed: pointer must be in-bounds at offset {}, \
+                "{}pointer must be in-bounds at offset {}, \
                            but is outside bounds of {} which has size {}",
                 msg,
                 ptr.offset.bytes(),
                 ptr.alloc_id,
                 allocation_size.bytes()
             ),
+            DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
+                write!(f, "null pointer is not a valid pointer for this operation")
+            }
             DanglingIntPointer(i, msg) => {
-                write!(f, "{} failed: 0x{:x} is not a valid pointer", msg, i)
+                write!(f, "{}0x{:x} is not a valid pointer", msg, i)
             }
             AlignmentCheckFailed { required, has } => write!(
                 f,
index 252b5fc70de9a4028ced2afefeb57c22295ac1bb..de0c4f2e30e8b0e12ac92e6a3f0d114c9f1dcd81 100644 (file)
@@ -461,7 +461,7 @@ pub fn terminator_loc(&self, bb: BasicBlock) -> Location {
     }
 
     #[inline]
-    pub fn predecessors(&self) -> impl std::ops::Deref<Target = Predecessors> + '_ {
+    pub fn predecessors(&self) -> &Predecessors {
         self.predecessor_cache.compute(&self.basic_blocks)
     }
 
@@ -2815,13 +2815,13 @@ impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> {
 
 impl graph::GraphPredecessors<'graph> for Body<'tcx> {
     type Item = BasicBlock;
-    type Iter = smallvec::IntoIter<[BasicBlock; 4]>;
+    type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>;
 }
 
 impl graph::WithPredecessors for Body<'tcx> {
     #[inline]
     fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
-        self.predecessors()[node].clone().into_iter()
+        self.predecessors()[node].iter().copied()
     }
 }
 
index 77f38e52ad2e415e22a6411ed55aa586c53f59a2..67440e6e0edf03140ae0c5f7a1a2932087a19c19 100644 (file)
@@ -229,6 +229,7 @@ pub struct CodegenUnit<'tcx> {
     name: Symbol,
     items: FxHashMap<MonoItem<'tcx>, (Linkage, Visibility)>,
     size_estimate: Option<usize>,
+    primary: bool,
 }
 
 /// Specifies the linkage type for a `MonoItem`.
@@ -258,7 +259,7 @@ pub enum Visibility {
 
 impl<'tcx> CodegenUnit<'tcx> {
     pub fn new(name: Symbol) -> CodegenUnit<'tcx> {
-        CodegenUnit { name, items: Default::default(), size_estimate: None }
+        CodegenUnit { name, items: Default::default(), size_estimate: None, primary: false }
     }
 
     pub fn name(&self) -> Symbol {
@@ -269,6 +270,14 @@ pub fn set_name(&mut self, name: Symbol) {
         self.name = name;
     }
 
+    pub fn is_primary(&self) -> bool {
+        self.primary
+    }
+
+    pub fn make_primary(&mut self) {
+        self.primary = true;
+    }
+
     pub fn items(&self) -> &FxHashMap<MonoItem<'tcx>, (Linkage, Visibility)> {
         &self.items
     }
@@ -378,6 +387,7 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
             name,
             // The size estimate is not relevant to the hash
             size_estimate: _,
+            primary: _,
         } = *self;
 
         name.hash_stable(hcx, hasher);
index 81230e32f56bd926cfecbc86a61682721a9f4bdd..068f81486118a7098ce673adad17c8383f23a391 100644 (file)
@@ -232,6 +232,7 @@ fn clone(&self) -> Self { *self }
         }
 
         pub trait QueryEngine<'tcx>: rustc_data_structures::sync::Sync {
+            #[cfg(parallel_compiler)]
             unsafe fn deadlock(&'tcx self, tcx: TyCtxt<'tcx>, registry: &rustc_rayon_core::Registry);
 
             fn encode_query_results(
index 88122777d2e67e2d32e9e939594b855f0d488dba..1e2714a2c1b2b9f00b1e07f85740a9f0c7909ea6 100644 (file)
@@ -6,7 +6,7 @@
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{
     hir::place::PlaceBase,
-    mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location},
+    mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, LocalKind, Location},
 };
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::{kw, Symbol};
@@ -424,15 +424,28 @@ pub(crate) fn report_mutability_error(
 
                         match label {
                             Some((true, err_help_span, suggested_code)) => {
-                                err.span_suggestion(
-                                    err_help_span,
-                                    &format!(
-                                        "consider changing this to be a mutable {}",
-                                        pointer_desc
-                                    ),
-                                    suggested_code,
-                                    Applicability::MachineApplicable,
-                                );
+                                let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
+                                if !is_trait_sig {
+                                    err.span_suggestion(
+                                        err_help_span,
+                                        &format!(
+                                            "consider changing this to be a mutable {}",
+                                            pointer_desc
+                                        ),
+                                        suggested_code,
+                                        Applicability::MachineApplicable,
+                                    );
+                                } else if let Some(x) = local_trait {
+                                    err.span_suggestion(
+                                        x,
+                                        &format!(
+                                            "consider changing that to be a mutable {}",
+                                            pointer_desc
+                                        ),
+                                        suggested_code,
+                                        Applicability::MachineApplicable,
+                                    );
+                                }
                             }
                             Some((false, err_label_span, message)) => {
                                 err.span_label(err_label_span, &message);
@@ -503,6 +516,65 @@ pub(crate) fn report_mutability_error(
         err.buffer(&mut self.errors_buffer);
     }
 
+    /// User cannot make signature of a trait mutable without changing the
+    /// trait. So we find if this error belongs to a trait and if so we move
+    /// suggestion to the trait or disable it if it is out of scope of this crate
+    fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
+        if self.body.local_kind(local) != LocalKind::Arg {
+            return (false, None);
+        }
+        let hir_map = self.infcx.tcx.hir();
+        let my_def = self.body.source.def_id();
+        let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
+        let td = if let Some(a) =
+            self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
+        {
+            a
+        } else {
+            return (false, None);
+        };
+        (
+            true,
+            td.as_local().and_then(|tld| {
+                let h = hir_map.local_def_id_to_hir_id(tld);
+                match hir_map.find(h) {
+                    Some(Node::Item(hir::Item {
+                        kind: hir::ItemKind::Trait(_, _, _, _, items),
+                        ..
+                    })) => {
+                        let mut f_in_trait_opt = None;
+                        for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
+                            let hi = fi.hir_id();
+                            if !matches!(k, hir::AssocItemKind::Fn { .. }) {
+                                continue;
+                            }
+                            if hir_map.name(hi) != hir_map.name(my_hir) {
+                                continue;
+                            }
+                            f_in_trait_opt = Some(hi);
+                            break;
+                        }
+                        f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
+                            Some(Node::TraitItem(hir::TraitItem {
+                                kind:
+                                    hir::TraitItemKind::Fn(
+                                        hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
+                                        _,
+                                    ),
+                                ..
+                            })) => {
+                                let hir::Ty { span, .. } = inputs[local.index() - 1];
+                                Some(span)
+                            }
+                            _ => None,
+                        })
+                    }
+                    _ => None,
+                }
+            }),
+        )
+    }
+
     // point to span of upvar making closure call require mutable borrow
     fn show_mutating_upvar(
         &self,
index dea1b11331549ef862e382c3cda18b071a5e5a3c..69ab50fa86ede291ca440996b60dc85869a79e28 100644 (file)
@@ -323,7 +323,7 @@ pub fn emulate_intrinsic(
                 self.write_scalar(result, dest)?;
             }
             sym::copy => {
-                self.copy(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
+                self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
             }
             sym::offset => {
                 let ptr = self.read_scalar(&args[0])?.check_init()?;
@@ -526,8 +526,40 @@ pub fn ptr_offset_inbounds(
             min_ptr,
             Size::from_bytes(size),
             None,
-            CheckInAllocMsg::InboundsTest,
+            CheckInAllocMsg::PointerArithmeticTest,
         )?;
         Ok(offset_ptr)
     }
+
+    /// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
+    pub(crate) fn copy_intrinsic(
+        &mut self,
+        src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+        dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+        count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+        nonoverlapping: bool,
+    ) -> InterpResult<'tcx> {
+        let count = self.read_scalar(&count)?.to_machine_usize(self)?;
+        let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
+        let (size, align) = (layout.size, layout.align.abi);
+        let size = size.checked_mul(count, self).ok_or_else(|| {
+            err_ub_format!(
+                "overflow computing total size of `{}`",
+                if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
+            )
+        })?;
+
+        // Make sure we check both pointers for an access of the total size and aligment,
+        // *even if* the total size is 0.
+        let src =
+            self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?;
+
+        let dst =
+            self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?;
+
+        if let (Some(src), Some(dst)) = (src, dst) {
+            self.memory.copy(src, dst, size, nonoverlapping)?;
+        }
+        Ok(())
+    }
 }
index 65869f956397f16ab79a89e5e5d5867bd4dc68f6..52baf1a63305748e130813c822fed9ad3ad4254d 100644 (file)
@@ -369,6 +369,7 @@ fn int_to_ptr(
     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
         Err((if int == 0 {
             // This is UB, seriously.
+            // (`DanglingIntPointer` with these exact arguments has special printing code.)
             err_ub!(DanglingIntPointer(0, CheckInAllocMsg::InboundsTest))
         } else {
             // This is just something we cannot support during const-eval.
index 6084f67abd78e8824378ed1acc59fb6df20e54e5..5a10ffe6d6199c92032cf5ed3884147b19324352 100644 (file)
@@ -2,7 +2,6 @@
 //!
 //! The main entry point is the `step` method.
 
-use crate::interpret::OpTy;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_target::abi::LayoutOf;
@@ -119,7 +118,7 @@ pub fn step(&mut self) -> InterpResult<'tcx, bool> {
                 let src = self.eval_operand(src, None)?;
                 let dst = self.eval_operand(dst, None)?;
                 let count = self.eval_operand(count, None)?;
-                self.copy(&src, &dst, &count, /* nonoverlapping */ true)?;
+                self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true)?;
             }
 
             // Statements we do not track.
@@ -149,37 +148,6 @@ pub fn step(&mut self) -> InterpResult<'tcx, bool> {
         Ok(())
     }
 
-    pub(crate) fn copy(
-        &mut self,
-        src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        nonoverlapping: bool,
-    ) -> InterpResult<'tcx> {
-        let count = self.read_scalar(&count)?.to_machine_usize(self)?;
-        let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
-        let (size, align) = (layout.size, layout.align.abi);
-        let size = size.checked_mul(count, self).ok_or_else(|| {
-            err_ub_format!(
-                "overflow computing total size of `{}`",
-                if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
-            )
-        })?;
-
-        // Make sure we check both pointers for an access of the total size and aligment,
-        // *even if* the total size is 0.
-        let src =
-            self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?;
-
-        let dst =
-            self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?;
-
-        if let (Some(src), Some(dst)) = (src, dst) {
-            self.memory.copy(src, dst, size, nonoverlapping)?;
-        }
-        Ok(())
-    }
-
     /// Evaluate an assignment statement.
     ///
     /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
index 19ceec70e04265397090fd9cc0357c291b6b833f..6e6e64d25ac38cb50c2efddc2cd3d0d9442d900c 100644 (file)
@@ -330,7 +330,7 @@ fn check_wide_ptr_meta(
                         vtable,
                         3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align
                         Some(self.ecx.tcx.data_layout.pointer_align.abi),
-                        CheckInAllocMsg::InboundsTest,
+                        CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
                     ),
                     self.path,
                     err_ub!(DanglingIntPointer(..)) |
@@ -416,7 +416,7 @@ fn check_safe_pointer(
                 place.ptr,
                 size,
                 Some(align),
-                CheckInAllocMsg::InboundsTest,
+                CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
             ),
             self.path,
             err_ub!(AlignmentCheckFailed { required, has }) =>
index dc2379fd92b834cba2dc31d3919230fbbede99f1..333cb30159093b4eaab17c675f6992c4b30d76fe 100644 (file)
@@ -350,12 +350,14 @@ fn collect_and_partition_mono_items<'tcx>(
     let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
         sync::join(
             || {
-                &*tcx.arena.alloc_from_iter(partition(
+                let mut codegen_units = partition(
                     tcx,
                     &mut items.iter().cloned(),
                     tcx.sess.codegen_units(),
                     &inlining_map,
-                ))
+                );
+                codegen_units[0].make_primary();
+                &*tcx.arena.alloc_from_iter(codegen_units)
             },
             || assert_symbols_are_distinct(tcx, items.iter()),
         )
index b5c8b4bebc360496349001822c0dc24c1bece0f2..ba10b54c5ae2e37fa579dc694bcfb35a79d50e1f 100644 (file)
@@ -47,7 +47,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // if we applied optimizations, we potentially have some cfg to cleanup to
         // make it easier for further passes
         if should_simplify {
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
             simplify_locals(body, tcx);
         }
     }
index 6f5fa858e25379bd9ad93a231cf2784da25a2e37..0521f507ec7d3908e7d1b3e729af5124c86caf10 100644 (file)
@@ -269,13 +269,13 @@ fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
 
 impl graph::GraphPredecessors<'graph> for CoverageGraph {
     type Item = BasicCoverageBlock;
-    type Iter = std::vec::IntoIter<BasicCoverageBlock>;
+    type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicCoverageBlock>>;
 }
 
 impl graph::WithPredecessors for CoverageGraph {
     #[inline]
     fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
-        self.predecessors[node].clone().into_iter()
+        self.predecessors[node].iter().copied()
     }
 }
 
index eaeb44289cfb2097c2c5354cc73ed3eb42d13848..c1e8f620b30c1dda7283b18eacc412256731bf79 100644 (file)
@@ -32,7 +32,7 @@
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::DefId;
 use rustc_span::source_map::SourceMap;
-use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol};
+use rustc_span::{CharPos, ExpnKind, Pos, SourceFile, Span, Symbol};
 
 /// A simple error message wrapper for `coverage::Error`s.
 #[derive(Debug)]
@@ -113,8 +113,29 @@ struct Instrumentor<'a, 'tcx> {
 impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
     fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
         let source_map = tcx.sess.source_map();
-        let (some_fn_sig, hir_body) = fn_sig_and_body(tcx, mir_body.source.def_id());
-        let body_span = hir_body.value.span;
+        let def_id = mir_body.source.def_id();
+        let (some_fn_sig, hir_body) = fn_sig_and_body(tcx, def_id);
+
+        let mut body_span = hir_body.value.span;
+
+        if tcx.is_closure(def_id) {
+            // If the MIR function is a closure, and if the closure body span
+            // starts from a macro, but it's content is not in that macro, try
+            // to find a non-macro callsite, and instrument the spans there
+            // instead.
+            loop {
+                let expn_data = body_span.ctxt().outer_expn_data();
+                if expn_data.is_root() {
+                    break;
+                }
+                if let ExpnKind::Macro(..) = expn_data.kind {
+                    body_span = expn_data.call_site;
+                } else {
+                    break;
+                }
+            }
+        }
+
         let source_file = source_map.lookup_source_file(body_span.lo());
         let fn_sig_span = match some_fn_sig.filter(|fn_sig| {
             fn_sig.span.ctxt() == body_span.ctxt()
index c41e71e09a4efc7d64186850460538b2b254d0cb..912505c65983edb8f06df2ef6fe0016035ef90db 100644 (file)
@@ -26,7 +26,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         if has_opts_to_apply {
             let mut opt_applier = OptApplier { tcx, duplicates };
             opt_applier.visit_body(body);
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
         }
     }
 }
index f7ea9faec47283cf2903062fedd031d0f9c4a440..ac392066233089914105843a2e224df382ad3761 100644 (file)
@@ -164,7 +164,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // Since this optimization adds new basic blocks and invalidates others,
         // clean up the cfg to make it nicer for other passes
         if should_cleanup {
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
         }
     }
 }
index 003003a8abbeae90f8f736d89e8a3a504e271de8..3560b4b1e8645e77ed17488ac8ba954b9ea4c28c 100644 (file)
@@ -964,7 +964,7 @@ fn create_generator_drop_shim<'tcx>(
 
     // Make sure we remove dead blocks to remove
     // unrelated code from the resume part of the function
-    simplify::remove_dead_blocks(&mut body);
+    simplify::remove_dead_blocks(tcx, &mut body);
 
     dump_mir(tcx, None, "generator_drop", &0, &body, |_, _| Ok(()));
 
@@ -1137,7 +1137,7 @@ fn create_generator_resume_function<'tcx>(
 
     // Make sure we remove dead blocks to remove
     // unrelated code from the drop part of the function
-    simplify::remove_dead_blocks(body);
+    simplify::remove_dead_blocks(tcx, body);
 
     dump_mir(tcx, None, "generator_resume", &0, body, |_, _| Ok(()));
 }
index b6f80763bc8c4c2b94b445b999a0d1bb85c2f3bb..f1c95a84ade85a04800a9af36440ba28429fbe6d 100644 (file)
@@ -57,7 +57,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         if inline(tcx, body) {
             debug!("running simplify cfg on {:?}", body.source);
             CfgSimplifier::new(body).simplify();
-            remove_dead_blocks(body);
+            remove_dead_blocks(tcx, body);
         }
     }
 }
index f7a9835353e5cff77c14c762641f2bf99103e088..21b208a08c2dca13cad206c4c28bf986187f3e43 100644 (file)
@@ -167,7 +167,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         }
 
         if should_cleanup {
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
         }
     }
 }
index 4aaa0baa9f46a26fb640f59b73e5f322eb84c6db..cd2db180552868c681f5bf862da1b988b43f9375 100644 (file)
@@ -38,6 +38,6 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             }
         }
 
-        simplify::remove_dead_blocks(body)
+        simplify::remove_dead_blocks(tcx, body)
     }
 }
index 5144d48750de70d7febebd7a0c6dcc013c157781..02e45021a0aaf343fe3f73173934dbbead3e9f0d 100644 (file)
@@ -36,7 +36,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // if we applied optimizations, we potentially have some cfg to cleanup to
         // make it easier for further passes
         if should_simplify {
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
         }
     }
 }
index 65e2d096b209462dd496320f589244ea13d12241..63373b0cffbb033c6f2f11ed4aceddd1194d6299 100644 (file)
@@ -29,6 +29,7 @@
 
 use crate::transform::MirPass;
 use rustc_index::vec::{Idx, IndexVec};
+use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
@@ -46,9 +47,9 @@ pub fn new(label: &str) -> Self {
     }
 }
 
-pub fn simplify_cfg(body: &mut Body<'_>) {
+pub fn simplify_cfg(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) {
     CfgSimplifier::new(body).simplify();
-    remove_dead_blocks(body);
+    remove_dead_blocks(tcx, body);
 
     // FIXME: Should probably be moved into some kind of pass manager
     body.basic_blocks_mut().raw.shrink_to_fit();
@@ -59,9 +60,9 @@ fn name(&self) -> Cow<'_, str> {
         Cow::Borrowed(&self.label)
     }
 
-    fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body.source);
-        simplify_cfg(body);
+        simplify_cfg(tcx, body);
     }
 }
 
@@ -286,7 +287,7 @@ fn strip_nops(&mut self) {
     }
 }
 
-pub fn remove_dead_blocks(body: &mut Body<'_>) {
+pub fn remove_dead_blocks(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) {
     let reachable = traversal::reachable_as_bitset(body);
     let num_blocks = body.basic_blocks().len();
     if num_blocks == reachable.count() {
@@ -306,6 +307,11 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
         }
         used_blocks += 1;
     }
+
+    if tcx.sess.instrument_coverage() {
+        save_unreachable_coverage(basic_blocks, used_blocks);
+    }
+
     basic_blocks.raw.truncate(used_blocks);
 
     for block in basic_blocks {
@@ -315,6 +321,32 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
     }
 }
 
+fn save_unreachable_coverage(
+    basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>,
+    first_dead_block: usize,
+) {
+    // retain coverage info for dead blocks, so coverage reports will still
+    // report `0` executions for the uncovered code regions.
+    let mut dropped_coverage = Vec::new();
+    for dead_block in first_dead_block..basic_blocks.len() {
+        for statement in basic_blocks[BasicBlock::new(dead_block)].statements.iter() {
+            if let StatementKind::Coverage(coverage) = &statement.kind {
+                if let Some(code_region) = &coverage.code_region {
+                    dropped_coverage.push((statement.source_info, code_region.clone()));
+                }
+            }
+        }
+    }
+    for (source_info, code_region) in dropped_coverage {
+        basic_blocks[START_BLOCK].statements.push(Statement {
+            source_info,
+            kind: StatementKind::Coverage(box Coverage {
+                kind: CoverageKind::Unreachable,
+                code_region: Some(code_region),
+            }),
+        })
+    }
+}
 pub struct SimplifyLocals;
 
 impl<'tcx> MirPass<'tcx> for SimplifyLocals {
index b42543c04eb3dd4d0dec4d2f1af86b94f14e5132..c9c4492062720199bf7d5ed0c63b73d892e7b2ef 100644 (file)
@@ -558,7 +558,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
 
         if did_remove_blocks {
             // We have dead blocks now, so remove those.
-            simplify::remove_dead_blocks(body);
+            simplify::remove_dead_blocks(tcx, body);
         }
     }
 }
index 658c6b6e9db20086855c89e94b73799352195b87..e7fb6b4f6b4ade858cf5210480c5a4aece8647d9 100644 (file)
@@ -60,7 +60,7 @@ fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         }
 
         if replaced {
-            simplify::remove_dead_blocks(body);
+            simplify::remove_dead_blocks(tcx, body);
         }
     }
 }
index bd8dfd678a97b3bbddd8b203dc1542e7737317c6..1c2f9a9645fe0286401561f2b4ca08c0a5103a9c 100644 (file)
@@ -148,15 +148,11 @@ fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> Opt
                         None => "unterminated block comment",
                     };
                     let last_bpos = self.pos;
-                    self.sess
-                        .span_diagnostic
-                        .struct_span_fatal_with_code(
-                            self.mk_sp(start, last_bpos),
-                            msg,
-                            error_code!(E0758),
-                        )
-                        .emit();
-                    FatalError.raise();
+                    self.sess.span_diagnostic.span_fatal_with_code(
+                        self.mk_sp(start, last_bpos),
+                        msg,
+                        error_code!(E0758),
+                    );
                 }
 
                 // Skip non-doc comments
@@ -315,57 +311,41 @@ fn cook_lexer_literal(
         let (lit_kind, mode, prefix_len, postfix_len) = match kind {
             rustc_lexer::LiteralKind::Char { terminated } => {
                 if !terminated {
-                    self.sess
-                        .span_diagnostic
-                        .struct_span_fatal_with_code(
-                            self.mk_sp(start, suffix_start),
-                            "unterminated character literal",
-                            error_code!(E0762),
-                        )
-                        .emit();
-                    FatalError.raise();
+                    self.sess.span_diagnostic.span_fatal_with_code(
+                        self.mk_sp(start, suffix_start),
+                        "unterminated character literal",
+                        error_code!(E0762),
+                    )
                 }
                 (token::Char, Mode::Char, 1, 1) // ' '
             }
             rustc_lexer::LiteralKind::Byte { terminated } => {
                 if !terminated {
-                    self.sess
-                        .span_diagnostic
-                        .struct_span_fatal_with_code(
-                            self.mk_sp(start + BytePos(1), suffix_start),
-                            "unterminated byte constant",
-                            error_code!(E0763),
-                        )
-                        .emit();
-                    FatalError.raise();
+                    self.sess.span_diagnostic.span_fatal_with_code(
+                        self.mk_sp(start + BytePos(1), suffix_start),
+                        "unterminated byte constant",
+                        error_code!(E0763),
+                    )
                 }
                 (token::Byte, Mode::Byte, 2, 1) // b' '
             }
             rustc_lexer::LiteralKind::Str { terminated } => {
                 if !terminated {
-                    self.sess
-                        .span_diagnostic
-                        .struct_span_fatal_with_code(
-                            self.mk_sp(start, suffix_start),
-                            "unterminated double quote string",
-                            error_code!(E0765),
-                        )
-                        .emit();
-                    FatalError.raise();
+                    self.sess.span_diagnostic.span_fatal_with_code(
+                        self.mk_sp(start, suffix_start),
+                        "unterminated double quote string",
+                        error_code!(E0765),
+                    )
                 }
                 (token::Str, Mode::Str, 1, 1) // " "
             }
             rustc_lexer::LiteralKind::ByteStr { terminated } => {
                 if !terminated {
-                    self.sess
-                        .span_diagnostic
-                        .struct_span_fatal_with_code(
-                            self.mk_sp(start + BytePos(1), suffix_start),
-                            "unterminated double quote byte string",
-                            error_code!(E0766),
-                        )
-                        .emit();
-                    FatalError.raise();
+                    self.sess.span_diagnostic.span_fatal_with_code(
+                        self.mk_sp(start + BytePos(1), suffix_start),
+                        "unterminated double quote byte string",
+                        error_code!(E0766),
+                    )
                 }
                 (token::ByteStr, Mode::ByteStr, 2, 1) // b" "
             }
index 70a5ac6f15ec221c3ee9c0ab3a9aa1bdf8a0fa13..72fdc78c30cbc3a101a0f9b81e74beaed2123407 100644 (file)
@@ -144,11 +144,7 @@ pub fn no(&self) -> bool {
 }
 
 impl<'a> Parser<'a> {
-    pub(super) fn span_fatal_err<S: Into<MultiSpan>>(
-        &self,
-        sp: S,
-        err: Error,
-    ) -> DiagnosticBuilder<'a> {
+    pub(super) fn span_err<S: Into<MultiSpan>>(&self, sp: S, err: Error) -> DiagnosticBuilder<'a> {
         err.span_err(sp, self.diagnostic())
     }
 
index acf3867cf8920608c2ad4b40858c0de037656210..553ffda814fe995eeb091a21cfb78b29aa058011 100644 (file)
@@ -1124,11 +1124,11 @@ fn parse_enum_variant(&mut self) -> PResult<'a, Option<Variant>> {
                 if !this.recover_nested_adt_item(kw::Enum)? {
                     return Ok((None, TrailingToken::None));
                 }
-                let ident = this.parse_ident()?;
+                let ident = this.parse_field_ident("enum", vlo)?;
 
                 let struct_def = if this.check(&token::OpenDelim(token::Brace)) {
                     // Parse a struct variant.
-                    let (fields, recovered) = this.parse_record_struct_body()?;
+                    let (fields, recovered) = this.parse_record_struct_body("struct")?;
                     VariantData::Struct(fields, recovered)
                 } else if this.check(&token::OpenDelim(token::Paren)) {
                     VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID)
@@ -1182,7 +1182,7 @@ fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
                 VariantData::Unit(DUMMY_NODE_ID)
             } else {
                 // If we see: `struct Foo<T> where T: Copy { ... }`
-                let (fields, recovered) = self.parse_record_struct_body()?;
+                let (fields, recovered) = self.parse_record_struct_body("struct")?;
                 VariantData::Struct(fields, recovered)
             }
         // No `where` so: `struct Foo<T>;`
@@ -1190,7 +1190,7 @@ fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
             VariantData::Unit(DUMMY_NODE_ID)
         // Record-style struct definition
         } else if self.token == token::OpenDelim(token::Brace) {
-            let (fields, recovered) = self.parse_record_struct_body()?;
+            let (fields, recovered) = self.parse_record_struct_body("struct")?;
             VariantData::Struct(fields, recovered)
         // Tuple-style struct definition with optional where-clause.
         } else if self.token == token::OpenDelim(token::Paren) {
@@ -1220,10 +1220,10 @@ fn parse_item_union(&mut self) -> PResult<'a, ItemInfo> {
 
         let vdata = if self.token.is_keyword(kw::Where) {
             generics.where_clause = self.parse_where_clause()?;
-            let (fields, recovered) = self.parse_record_struct_body()?;
+            let (fields, recovered) = self.parse_record_struct_body("union")?;
             VariantData::Struct(fields, recovered)
         } else if self.token == token::OpenDelim(token::Brace) {
-            let (fields, recovered) = self.parse_record_struct_body()?;
+            let (fields, recovered) = self.parse_record_struct_body("union")?;
             VariantData::Struct(fields, recovered)
         } else {
             let token_str = super::token_descr(&self.token);
@@ -1236,12 +1236,15 @@ fn parse_item_union(&mut self) -> PResult<'a, ItemInfo> {
         Ok((class_name, ItemKind::Union(vdata, generics)))
     }
 
-    fn parse_record_struct_body(&mut self) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
+    fn parse_record_struct_body(
+        &mut self,
+        adt_ty: &str,
+    ) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
         let mut fields = Vec::new();
         let mut recovered = false;
         if self.eat(&token::OpenDelim(token::Brace)) {
             while self.token != token::CloseDelim(token::Brace) {
-                let field = self.parse_field_def().map_err(|e| {
+                let field = self.parse_field_def(adt_ty).map_err(|e| {
                     self.consume_block(token::Brace, ConsumeClosingDelim::No);
                     recovered = true;
                     e
@@ -1294,24 +1297,25 @@ fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<FieldDef>> {
     }
 
     /// Parses an element of a struct declaration.
-    fn parse_field_def(&mut self) -> PResult<'a, FieldDef> {
+    fn parse_field_def(&mut self, adt_ty: &str) -> PResult<'a, FieldDef> {
         let attrs = self.parse_outer_attributes()?;
         self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
             let lo = this.token.span;
             let vis = this.parse_visibility(FollowedByType::No)?;
-            Ok((this.parse_single_struct_field(lo, vis, attrs)?, TrailingToken::None))
+            Ok((this.parse_single_struct_field(adt_ty, lo, vis, attrs)?, TrailingToken::None))
         })
     }
 
     /// Parses a structure field declaration.
     fn parse_single_struct_field(
         &mut self,
+        adt_ty: &str,
         lo: Span,
         vis: Visibility,
         attrs: Vec<Attribute>,
     ) -> PResult<'a, FieldDef> {
         let mut seen_comma: bool = false;
-        let a_var = self.parse_name_and_ty(lo, vis, attrs)?;
+        let a_var = self.parse_name_and_ty(adt_ty, lo, vis, attrs)?;
         if self.token == token::Comma {
             seen_comma = true;
         }
@@ -1322,7 +1326,7 @@ fn parse_single_struct_field(
             token::CloseDelim(token::Brace) => {}
             token::DocComment(..) => {
                 let previous_span = self.prev_token.span;
-                let mut err = self.span_fatal_err(self.token.span, Error::UselessDocComment);
+                let mut err = self.span_err(self.token.span, Error::UselessDocComment);
                 self.bump(); // consume the doc comment
                 let comma_after_doc_seen = self.eat(&token::Comma);
                 // `seen_comma` is always false, because we are inside doc block
@@ -1398,11 +1402,12 @@ fn parse_single_struct_field(
     /// Parses a structure field.
     fn parse_name_and_ty(
         &mut self,
+        adt_ty: &str,
         lo: Span,
         vis: Visibility,
         attrs: Vec<Attribute>,
     ) -> PResult<'a, FieldDef> {
-        let name = self.parse_ident_common(false)?;
+        let name = self.parse_field_ident(adt_ty, lo)?;
         self.expect(&token::Colon)?;
         let ty = self.parse_ty()?;
         Ok(FieldDef {
@@ -1416,6 +1421,29 @@ fn parse_name_and_ty(
         })
     }
 
+    /// Parses a field identifier. Specialized version of `parse_ident_common`
+    /// for better diagnostics and suggestions.
+    fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
+        let (ident, is_raw) = self.ident_or_err()?;
+        if !is_raw && ident.is_reserved() {
+            let err = if self.check_fn_front_matter(false) {
+                let _ = self.parse_fn(&mut Vec::new(), |_| true, lo);
+                let mut err = self.struct_span_err(
+                    lo.to(self.prev_token.span),
+                    &format!("functions are not allowed in {} definitions", adt_ty),
+                );
+                err.help("unlike in C++, Java, and C#, functions are declared in `impl` blocks");
+                err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information");
+                err
+            } else {
+                self.expected_ident_found()
+            };
+            return Err(err);
+        }
+        self.bump();
+        Ok(ident)
+    }
+
     /// Parses a declarative macro 2.0 definition.
     /// The `macro` keyword has already been parsed.
     /// ```
index ed95a5661b18ec58ab87cfc250e30fa4c16398ad..35cfaae13a4a0942cc8d35a722993d95f666e0d4 100644 (file)
@@ -522,27 +522,27 @@ pub fn parse_ident(&mut self) -> PResult<'a, Ident> {
         self.parse_ident_common(true)
     }
 
+    fn ident_or_err(&mut self) -> PResult<'a, (Ident, /* is_raw */ bool)> {
+        self.token.ident().ok_or_else(|| match self.prev_token.kind {
+            TokenKind::DocComment(..) => {
+                self.span_err(self.prev_token.span, Error::UselessDocComment)
+            }
+            _ => self.expected_ident_found(),
+        })
+    }
+
     fn parse_ident_common(&mut self, recover: bool) -> PResult<'a, Ident> {
-        match self.token.ident() {
-            Some((ident, is_raw)) => {
-                if !is_raw && ident.is_reserved() {
-                    let mut err = self.expected_ident_found();
-                    if recover {
-                        err.emit();
-                    } else {
-                        return Err(err);
-                    }
-                }
-                self.bump();
-                Ok(ident)
+        let (ident, is_raw) = self.ident_or_err()?;
+        if !is_raw && ident.is_reserved() {
+            let mut err = self.expected_ident_found();
+            if recover {
+                err.emit();
+            } else {
+                return Err(err);
             }
-            _ => Err(match self.prev_token.kind {
-                TokenKind::DocComment(..) => {
-                    self.span_fatal_err(self.prev_token.span, Error::UselessDocComment)
-                }
-                _ => self.expected_ident_found(),
-            }),
         }
+        self.bump();
+        Ok(ident)
     }
 
     /// Checks if the next token is `tok`, and returns `true` if so.
@@ -1077,7 +1077,7 @@ fn parse_mac_args_common(&mut self, delimited_only: bool) -> PResult<'a, MacArgs
                     let span = expr.span;
 
                     match &expr.kind {
-                        // Not gated to supporte things like `doc = $expr` that work on stable.
+                        // Not gated to support things like `doc = $expr` that work on stable.
                         _ if is_interpolated_expr => {}
                         ExprKind::Lit(lit) if lit.kind.is_unsuffixed() => {}
                         _ => self.sess.gated_spans.gate(sym::extended_key_value_attributes, span),
index 592f64f4a399fce133b4757986379fd5b0b55993..b40eed8c5d118752a4b075b59fff080e3bced2ce 100644 (file)
@@ -168,7 +168,7 @@ fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResu
     fn error_outer_attrs(&self, attrs: &[Attribute]) {
         if let [.., last] = attrs {
             if last.is_doc_comment() {
-                self.span_fatal_err(last.span, Error::UselessDocComment).emit();
+                self.span_err(last.span, Error::UselessDocComment).emit();
             } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
                 self.struct_span_err(last.span, "expected statement after outer attribute").emit();
             }
index 0f7b8ebd376b9e5c6438afd070f3c456bb1d3be9..d537741c749c5aa789e2bf6a8b314d2ca259c3ed 100644 (file)
@@ -470,7 +470,7 @@ fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
     /// Is a `dyn B0 + ... + Bn` type allowed here?
     fn is_explicit_dyn_type(&mut self) -> bool {
         self.check_keyword(kw::Dyn)
-            && (self.token.uninterpolated_span().rust_2018()
+            && (!self.token.uninterpolated_span().rust_2015()
                 || self.look_ahead(1, |t| {
                     t.can_begin_bound() && !can_continue_type_after_non_fn_ident(t)
                 }))
@@ -539,7 +539,21 @@ fn parse_generic_bounds_common(
     ) -> PResult<'a, GenericBounds> {
         let mut bounds = Vec::new();
         let mut negative_bounds = Vec::new();
-        while self.can_begin_bound() {
+
+        while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) {
+            if self.token.is_keyword(kw::Dyn) {
+                // Account for `&dyn Trait + dyn Other`.
+                self.struct_span_err(self.token.span, "invalid `dyn` keyword")
+                    .help("`dyn` is only needed at the start of a trait `+`-separated list")
+                    .span_suggestion(
+                        self.token.span,
+                        "remove this keyword",
+                        String::new(),
+                        Applicability::MachineApplicable,
+                    )
+                    .emit();
+                self.bump();
+            }
             match self.parse_generic_bound()? {
                 Ok(bound) => bounds.push(bound),
                 Err(neg_sp) => negative_bounds.push(neg_sp),
@@ -721,7 +735,26 @@ fn parse_generic_ty_bound(
         let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
         let path = self.parse_path(PathStyle::Type)?;
         if has_parens {
-            self.expect(&token::CloseDelim(token::Paren))?;
+            if self.token.is_like_plus() {
+                // Someone has written something like `&dyn (Trait + Other)`. The correct code
+                // would be `&(dyn Trait + Other)`, but we don't have access to the appropriate
+                // span to suggest that. When written as `&dyn Trait + Other`, an appropriate
+                // suggestion is given.
+                let bounds = vec![];
+                self.parse_remaining_bounds(bounds, true)?;
+                self.expect(&token::CloseDelim(token::Paren))?;
+                let sp = vec![lo, self.prev_token.span];
+                let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect();
+                self.struct_span_err(sp, "incorrect braces around trait bounds")
+                    .multipart_suggestion(
+                        "remove the parentheses",
+                        sugg,
+                        Applicability::MachineApplicable,
+                    )
+                    .emit();
+            } else {
+                self.expect(&token::CloseDelim(token::Paren))?;
+            }
         }
 
         let modifier = modifiers.to_trait_bound_modifier();
index d57cba6420f7b2781b47fb9f90e034f3aad55c2f..cb06cbfcc7be02c517cbd9285e7bf12517b02c60 100644 (file)
@@ -8,7 +8,7 @@
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 
-use rustc_ast::{Attribute, Lit, LitKind, NestedMetaItem};
+use rustc_ast::{AttrStyle, Attribute, Lit, LitKind, NestedMetaItem};
 use rustc_errors::{pluralize, struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
@@ -22,7 +22,7 @@
 };
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::{sym, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{MultiSpan, Span, DUMMY_SP};
 
 pub(crate) fn target_from_impl_item<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -67,6 +67,7 @@ fn check_attributes(
         item: Option<ItemLike<'_>>,
     ) {
         let mut is_valid = true;
+        let mut specified_inline = None;
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
             is_valid &= match attr.name_or_empty() {
@@ -77,7 +78,7 @@ fn check_attributes(
                 sym::track_caller => {
                     self.check_track_caller(hir_id, &attr.span, attrs, span, target)
                 }
-                sym::doc => self.check_doc_attrs(attr, hir_id, target),
+                sym::doc => self.check_doc_attrs(attr, hir_id, target, &mut specified_inline),
                 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 => {
@@ -564,7 +565,71 @@ fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
         true
     }
 
-    fn check_attr_crate_level(
+    /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid.
+    ///
+    /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
+    /// if there are conflicting attributes for one item.
+    ///
+    /// `specified_inline` is used to keep track of whether we have
+    /// already seen an inlining attribute for this item.
+    /// If so, `specified_inline` holds the value and the span of
+    /// the first `inline`/`no_inline` attribute.
+    fn check_doc_inline(
+        &self,
+        attr: &Attribute,
+        meta: &NestedMetaItem,
+        hir_id: HirId,
+        target: Target,
+        specified_inline: &mut Option<(bool, Span)>,
+    ) -> bool {
+        if target == Target::Use {
+            let do_inline = meta.name_or_empty() == sym::inline;
+            if let Some((prev_inline, prev_span)) = *specified_inline {
+                if do_inline != prev_inline {
+                    let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
+                    spans.push_span_label(prev_span, String::from("this attribute..."));
+                    spans.push_span_label(
+                        meta.span(),
+                        String::from("...conflicts with this attribute"),
+                    );
+                    self.tcx
+                        .sess
+                        .struct_span_err(spans, "conflicting doc inlining attributes")
+                        .help("remove one of the conflicting attributes")
+                        .emit();
+                    return false;
+                }
+                true
+            } else {
+                *specified_inline = Some((do_inline, meta.span()));
+                true
+            }
+        } else {
+            self.tcx.struct_span_lint_hir(
+                INVALID_DOC_ATTRIBUTES,
+                hir_id,
+                meta.span(),
+                |lint| {
+                    let mut err = lint.build(
+                        "this attribute can only be applied to a `use` item",
+                    );
+                    err.span_label(meta.span(), "only applicable on `use` items");
+                    if attr.style == AttrStyle::Outer {
+                        err.span_label(
+                            self.tcx.hir().span(hir_id),
+                            "not a `use` item",
+                        );
+                    }
+                    err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#docno_inlinedocinline for more information")
+                        .emit();
+                },
+            );
+            false
+        }
+    }
+
+    /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
+    fn check_attr_not_crate_level(
         &self,
         meta: &NestedMetaItem,
         hir_id: HirId,
@@ -586,7 +651,62 @@ fn check_attr_crate_level(
         true
     }
 
-    fn check_doc_attrs(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool {
+    /// Checks that an attribute is used at the crate level. Returns `true` if valid.
+    fn check_attr_crate_level(
+        &self,
+        attr: &Attribute,
+        meta: &NestedMetaItem,
+        hir_id: HirId,
+    ) -> bool {
+        if hir_id != CRATE_HIR_ID {
+            self.tcx.struct_span_lint_hir(
+                INVALID_DOC_ATTRIBUTES,
+                hir_id,
+                meta.span(),
+                |lint| {
+                    let mut err = lint.build(
+                        "this attribute can only be applied at the crate level",
+                    );
+                    if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_HIR_ID {
+                        if let Ok(mut src) =
+                            self.tcx.sess.source_map().span_to_snippet(attr.span)
+                        {
+                            src.insert(1, '!');
+                            err.span_suggestion_verbose(
+                                attr.span,
+                                "to apply to the crate, use an inner attribute",
+                                src,
+                                Applicability::MaybeIncorrect,
+                            );
+                        } else {
+                            err.span_help(
+                                attr.span,
+                                "to apply to the crate, use an inner attribute",
+                            );
+                        }
+                    }
+                    err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information")
+                        .emit();
+                },
+            );
+            return false;
+        }
+        true
+    }
+
+    /// Runs various checks on `#[doc]` attributes. Returns `true` if valid.
+    ///
+    /// `specified_inline` should be initialized to `None` and kept for the scope
+    /// of one item. Read the documentation of [`check_doc_inline`] for more information.
+    ///
+    /// [`check_doc_inline`]: Self::check_doc_inline
+    fn check_doc_attrs(
+        &self,
+        attr: &Attribute,
+        hir_id: HirId,
+        target: Target,
+        specified_inline: &mut Option<(bool, Span)>,
+    ) -> bool {
         let mut is_valid = true;
 
         if let Some(list) = attr.meta().and_then(|mi| mi.meta_item_list().map(|l| l.to_vec())) {
@@ -594,32 +714,40 @@ fn check_doc_attrs(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bo
                 if let Some(i_meta) = meta.meta_item() {
                     match i_meta.name_or_empty() {
                         sym::alias
-                            if !self.check_attr_crate_level(&meta, hir_id, "alias")
+                            if !self.check_attr_not_crate_level(&meta, hir_id, "alias")
                                 || !self.check_doc_alias(&meta, hir_id, target) =>
                         {
                             is_valid = false
                         }
 
                         sym::keyword
-                            if !self.check_attr_crate_level(&meta, hir_id, "keyword")
+                            if !self.check_attr_not_crate_level(&meta, hir_id, "keyword")
                                 || !self.check_doc_keyword(&meta, hir_id) =>
                         {
                             is_valid = false
                         }
 
-                        sym::test if CRATE_HIR_ID != hir_id => {
-                            self.tcx.struct_span_lint_hir(
-                                INVALID_DOC_ATTRIBUTES,
+                        sym::html_favicon_url
+                        | sym::html_logo_url
+                        | sym::html_playground_url
+                        | sym::issue_tracker_base_url
+                        | sym::html_root_url
+                        | sym::html_no_source
+                        | sym::test
+                            if !self.check_attr_crate_level(&attr, &meta, hir_id) =>
+                        {
+                            is_valid = false;
+                        }
+
+                        sym::inline | sym::no_inline
+                            if !self.check_doc_inline(
+                                &attr,
+                                &meta,
                                 hir_id,
-                                meta.span(),
-                                |lint| {
-                                    lint.build(
-                                        "`#![doc(test(...)]` is only allowed \
-                                         as a crate-level attribute",
-                                    )
-                                    .emit();
-                                },
-                            );
+                                target,
+                                specified_inline,
+                            ) =>
+                        {
                             is_valid = false;
                         }
 
index ee914fa1ba95cfb2cedf993b0a1da6168042dd79..c789aa2fa596e996c6fb889bc02a34e911bcacde 100644 (file)
@@ -550,12 +550,10 @@ pub(crate) fn try_collect_active_jobs(
         }
 
         impl QueryEngine<'tcx> for Queries<'tcx> {
-            unsafe fn deadlock(&'tcx self, _tcx: TyCtxt<'tcx>, _registry: &rustc_rayon_core::Registry) {
-                #[cfg(parallel_compiler)]
-                {
-                    let tcx = QueryCtxt { tcx: _tcx, queries: self };
-                    rustc_query_system::query::deadlock(tcx, _registry)
-                }
+            #[cfg(parallel_compiler)]
+            unsafe fn deadlock(&'tcx self, tcx: TyCtxt<'tcx>, registry: &rustc_rayon_core::Registry) {
+                let tcx = QueryCtxt { tcx, queries: self };
+                rustc_query_system::query::deadlock(tcx, registry)
             }
 
             fn encode_query_results(
index 2e4c8d0565a63b198b07e24aec565c8c5194dcfe..011c2ceebb7148797d92260cc89576b8c5ccd0b5 100644 (file)
@@ -14,7 +14,7 @@ pub trait CacheSelector<K, V> {
     type Cache;
 }
 
-pub trait QueryStorage: Default {
+pub trait QueryStorage {
     type Value: Debug;
     type Stored: Clone;
 
@@ -23,7 +23,7 @@ pub trait QueryStorage: Default {
     fn store_nocache(&self, value: Self::Value) -> Self::Stored;
 }
 
-pub trait QueryCache: QueryStorage {
+pub trait QueryCache: QueryStorage + Sized {
     type Key: Hash + Eq + Clone + Debug;
     type Sharded: Default;
 
index 4e2515c3ac3fa209b3768815713ceb18a795e0b7..f2a6b6df4b9de3f75208a6eb5dadc5fa39a77363 100644 (file)
@@ -52,10 +52,6 @@ pub(crate) fn hash_result(
         (self.hash_result)(hcx, value)
     }
 
-    pub(crate) fn handle_cycle_error(&self, tcx: CTX, diag: DiagnosticBuilder<'_>) -> V {
-        (self.handle_cycle_error)(tcx, diag)
-    }
-
     pub(crate) fn cache_on_disk(&self, tcx: CTX, key: &K, value: Option<&V>) -> bool {
         (self.cache_on_disk)(tcx, key, value)
     }
index 21f580db04f282920a808ba01f686dbd79e3163e..a967670280ff2ec8990ad27481d0bef354a164ec 100644 (file)
@@ -9,7 +9,6 @@
 
 use std::convert::TryFrom;
 use std::hash::Hash;
-use std::marker::PhantomData;
 use std::num::NonZeroU32;
 
 #[cfg(parallel_compiler)]
@@ -100,8 +99,6 @@ pub struct QueryJob<D> {
     /// The latch that is used to wait on this job.
     #[cfg(parallel_compiler)]
     latch: Option<QueryLatch<D>>,
-
-    dummy: PhantomData<QueryLatch<D>>,
 }
 
 impl<D> QueryJob<D>
@@ -116,23 +113,17 @@ pub fn new(id: QueryShardJobId, span: Span, parent: Option<QueryJobId<D>>) -> Se
             parent,
             #[cfg(parallel_compiler)]
             latch: None,
-            dummy: PhantomData,
         }
     }
 
     #[cfg(parallel_compiler)]
-    pub(super) fn latch(&mut self, _id: QueryJobId<D>) -> QueryLatch<D> {
+    pub(super) fn latch(&mut self) -> QueryLatch<D> {
         if self.latch.is_none() {
             self.latch = Some(QueryLatch::new());
         }
         self.latch.as_ref().unwrap().clone()
     }
 
-    #[cfg(not(parallel_compiler))]
-    pub(super) fn latch(&mut self, id: QueryJobId<D>) -> QueryLatch<D> {
-        QueryLatch { id }
-    }
-
     /// Signals to waiters that the query is complete.
     ///
     /// This does nothing for single threaded rustc,
@@ -148,13 +139,7 @@ pub fn signal_complete(self) {
 }
 
 #[cfg(not(parallel_compiler))]
-#[derive(Clone)]
-pub(super) struct QueryLatch<D> {
-    id: QueryJobId<D>,
-}
-
-#[cfg(not(parallel_compiler))]
-impl<D> QueryLatch<D>
+impl<D> QueryJobId<D>
 where
     D: Copy + Clone + Eq + Hash,
 {
@@ -172,7 +157,7 @@ pub(super) fn find_cycle_in_stack(
             let info = query_map.get(&job).unwrap();
             cycle.push(info.info.clone());
 
-            if job == self.id {
+            if job == *self {
                 cycle.reverse();
 
                 // This is the end of the cycle
index c9125b3ffe94245520df95d72a799ae854169254..39dfdd78cc4f5613a85153303e9dcc3aae9bf01f 100644 (file)
 };
 use crate::query::{QueryContext, QueryMap, QueryStackFrame};
 
-#[cfg(not(parallel_compiler))]
-use rustc_data_structures::cold_path;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::{FxHashMap, FxHasher};
 use rustc_data_structures::sharded::{get_shard_index_by_hash, Sharded};
 use rustc_data_structures::sync::{Lock, LockGuard};
 use rustc_data_structures::thin_vec::ThinVec;
+#[cfg(not(parallel_compiler))]
+use rustc_errors::DiagnosticBuilder;
 use rustc_errors::{Diagnostic, FatalError};
 use rustc_span::Span;
 use std::collections::hash_map::Entry;
@@ -36,7 +36,7 @@ pub struct QueryCacheStore<C: QueryCache> {
     pub cache_hits: AtomicUsize,
 }
 
-impl<C: QueryCache> Default for QueryCacheStore<C> {
+impl<C: QueryCache + Default> Default for QueryCacheStore<C> {
     fn default() -> Self {
         Self {
             cache: C::default(),
@@ -158,6 +158,31 @@ struct JobOwner<'tcx, D, C>
     id: QueryJobId<D>,
 }
 
+#[cold]
+#[inline(never)]
+#[cfg(not(parallel_compiler))]
+fn mk_cycle<CTX, V, R>(
+    tcx: CTX,
+    root: QueryJobId<CTX::DepKind>,
+    span: Span,
+    handle_cycle_error: fn(CTX, DiagnosticBuilder<'_>) -> V,
+    cache: &dyn crate::query::QueryStorage<Value = V, Stored = R>,
+) -> R
+where
+    CTX: QueryContext,
+    V: std::fmt::Debug,
+    R: Clone,
+{
+    let error: CycleError = root.find_cycle_in_stack(
+        tcx.try_collect_active_jobs().unwrap(),
+        &tcx.current_query_job(),
+        span,
+    );
+    let error = report_cycle(tcx.dep_context().sess(), error);
+    let value = handle_cycle_error(tcx, error);
+    cache.store_nocache(value)
+}
+
 impl<'tcx, D, C> JobOwner<'tcx, D, C>
 where
     D: Copy + Clone + Eq + Hash,
@@ -177,7 +202,7 @@ fn try_start<'b, CTX>(
         state: &'b QueryState<CTX::DepKind, C::Key>,
         cache: &'b QueryCacheStore<C>,
         span: Span,
-        key: &C::Key,
+        key: C::Key,
         lookup: QueryLookup,
         query: &QueryVtable<CTX, C::Key, C::Value>,
     ) -> TryGetJob<'b, CTX::DepKind, C>
@@ -188,94 +213,86 @@ fn try_start<'b, CTX>(
         let mut state_lock = state.shards.get_shard_by_index(shard).lock();
         let lock = &mut *state_lock;
 
-        let (latch, mut _query_blocked_prof_timer) = match lock.active.entry((*key).clone()) {
-            Entry::Occupied(mut entry) => {
-                match entry.get_mut() {
-                    QueryResult::Started(job) => {
-                        // For parallel queries, we'll block and wait until the query running
-                        // in another thread has completed. Record how long we wait in the
-                        // self-profiler.
-                        let _query_blocked_prof_timer = if cfg!(parallel_compiler) {
-                            Some(tcx.dep_context().profiler().query_blocked())
-                        } else {
-                            None
-                        };
-
-                        // Create the id of the job we're waiting for
-                        let id = QueryJobId::new(job.id, shard, query.dep_kind);
-
-                        (job.latch(id), _query_blocked_prof_timer)
-                    }
-                    QueryResult::Poisoned => FatalError.raise(),
-                }
-            }
+        match lock.active.entry(key) {
             Entry::Vacant(entry) => {
-                // No job entry for this query. Return a new one to be started later.
-
                 // Generate an id unique within this shard.
                 let id = lock.jobs.checked_add(1).unwrap();
                 lock.jobs = id;
                 let id = QueryShardJobId(NonZeroU32::new(id).unwrap());
 
-                let global_id = QueryJobId::new(id, shard, query.dep_kind);
-
                 let job = tcx.current_query_job();
                 let job = QueryJob::new(id, span, job);
 
+                let key = entry.key().clone();
                 entry.insert(QueryResult::Started(job));
 
-                let owner = JobOwner { state, cache, id: global_id, key: (*key).clone() };
+                let global_id = QueryJobId::new(id, shard, query.dep_kind);
+                let owner = JobOwner { state, cache, id: global_id, key };
                 return TryGetJob::NotYetStarted(owner);
             }
-        };
-        mem::drop(state_lock);
-
-        // If we are single-threaded we know that we have cycle error,
-        // so we just return the error.
-        #[cfg(not(parallel_compiler))]
-        return TryGetJob::Cycle(cold_path(|| {
-            let error: CycleError = latch.find_cycle_in_stack(
-                tcx.try_collect_active_jobs().unwrap(),
-                &tcx.current_query_job(),
-                span,
-            );
-            let error = report_cycle(tcx.dep_context().sess(), error);
-            let value = query.handle_cycle_error(tcx, error);
-            cache.cache.store_nocache(value)
-        }));
-
-        // With parallel queries we might just have to wait on some other
-        // thread.
-        #[cfg(parallel_compiler)]
-        {
-            let result = latch.wait_on(tcx.current_query_job(), span);
-
-            if let Err(cycle) = result {
-                let cycle = report_cycle(tcx.dep_context().sess(), cycle);
-                let value = query.handle_cycle_error(tcx, cycle);
-                let value = cache.cache.store_nocache(value);
-                return TryGetJob::Cycle(value);
-            }
+            Entry::Occupied(mut entry) => {
+                match entry.get_mut() {
+                    #[cfg(not(parallel_compiler))]
+                    QueryResult::Started(job) => {
+                        let id = QueryJobId::new(job.id, shard, query.dep_kind);
 
-            let cached = cache
-                .cache
-                .lookup(cache, &key, |value, index| {
-                    if unlikely!(tcx.dep_context().profiler().enabled()) {
-                        tcx.dep_context().profiler().query_cache_hit(index.into());
+                        drop(state_lock);
+
+                        // If we are single-threaded we know that we have cycle error,
+                        // so we just return the error.
+                        return TryGetJob::Cycle(mk_cycle(
+                            tcx,
+                            id,
+                            span,
+                            query.handle_cycle_error,
+                            &cache.cache,
+                        ));
                     }
-                    #[cfg(debug_assertions)]
-                    {
-                        cache.cache_hits.fetch_add(1, Ordering::Relaxed);
+                    #[cfg(parallel_compiler)]
+                    QueryResult::Started(job) => {
+                        // For parallel queries, we'll block and wait until the query running
+                        // in another thread has completed. Record how long we wait in the
+                        // self-profiler.
+                        let query_blocked_prof_timer = tcx.dep_context().profiler().query_blocked();
+
+                        // Get the latch out
+                        let latch = job.latch();
+                        let key = entry.key().clone();
+
+                        drop(state_lock);
+
+                        // With parallel queries we might just have to wait on some other
+                        // thread.
+                        let result = latch.wait_on(tcx.current_query_job(), span);
+
+                        if let Err(cycle) = result {
+                            let cycle = report_cycle(tcx.dep_context().sess(), cycle);
+                            let value = (query.handle_cycle_error)(tcx, cycle);
+                            let value = cache.cache.store_nocache(value);
+                            return TryGetJob::Cycle(value);
+                        }
+
+                        let cached = cache
+                            .cache
+                            .lookup(cache, &key, |value, index| {
+                                if unlikely!(tcx.dep_context().profiler().enabled()) {
+                                    tcx.dep_context().profiler().query_cache_hit(index.into());
+                                }
+                                #[cfg(debug_assertions)]
+                                {
+                                    cache.cache_hits.fetch_add(1, Ordering::Relaxed);
+                                }
+                                (value.clone(), index)
+                            })
+                            .unwrap_or_else(|_| panic!("value must be in cache after waiting"));
+
+                        query_blocked_prof_timer.finish_with_query_invocation_id(cached.1.into());
+
+                        return TryGetJob::JobCompleted(cached);
                     }
-                    (value.clone(), index)
-                })
-                .unwrap_or_else(|_| panic!("value must be in cache after waiting"));
-
-            if let Some(prof_timer) = _query_blocked_prof_timer.take() {
-                prof_timer.finish_with_query_invocation_id(cached.1.into());
+                    QueryResult::Poisoned => FatalError.raise(),
+                }
             }
-
-            return TryGetJob::JobCompleted(cached);
         }
     }
 
@@ -418,7 +435,13 @@ fn try_execute_query<CTX, C>(
     CTX: QueryContext,
 {
     let job = match JobOwner::<'_, CTX::DepKind, C>::try_start(
-        tcx, state, cache, span, &key, lookup, query,
+        tcx,
+        state,
+        cache,
+        span,
+        key.clone(),
+        lookup,
+        query,
     ) {
         TryGetJob::NotYetStarted(job) => job,
         TryGetJob::Cycle(result) => return result,
@@ -582,13 +605,19 @@ fn incremental_verify_ich<CTX, K, V: Debug>(
 
     let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node);
 
-    assert_eq!(
-        Some(new_hash),
-        old_hash,
-        "found unstable fingerprints for {:?}: {:?}",
-        dep_node,
-        result
-    );
+    if Some(new_hash) != old_hash {
+        let run_cmd = if let Some(crate_name) = &tcx.sess().opts.crate_name {
+            format!("`cargo clean -p {}` or `cargo clean`", crate_name)
+        } else {
+            "`cargo clean`".to_string()
+        };
+        tcx.sess().struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node))
+            .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd))
+            .note(&format!("Please follow the instructions below to create a bug report with the provided information"))
+            .note(&format!("See <https://github.com/rust-lang/rust/issues/84970> for more information"))
+            .emit();
+        panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result);
+    }
 }
 
 fn force_query_with_job<C, CTX>(
@@ -741,7 +770,13 @@ fn force_query_impl<CTX, C>(
     };
 
     let job = match JobOwner::<'_, CTX::DepKind, C>::try_start(
-        tcx, state, cache, span, &key, lookup, query,
+        tcx,
+        state,
+        cache,
+        span,
+        key.clone(),
+        lookup,
+        query,
     ) {
         TryGetJob::NotYetStarted(job) => job,
         TryGetJob::Cycle(_) => return,
index 7561b3df3af70841d2b436f12115018614048950..fdde687d4866c57913ca31bcb8de7394f24b4ed8 100644 (file)
@@ -9,7 +9,7 @@
 use rustc_ast::{self as ast, Expr, ExprKind, Item, ItemKind, NodeId, Path, Ty, TyKind};
 use rustc_ast_pretty::pprust::path_segment_to_string;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, SuggestionStyle};
 use rustc_hir as hir;
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
@@ -1687,12 +1687,12 @@ fn suggest_using_enum_variant(
 impl<'tcx> LifetimeContext<'_, 'tcx> {
     crate fn report_missing_lifetime_specifiers(
         &self,
-        span: Span,
+        spans: Vec<Span>,
         count: usize,
     ) -> DiagnosticBuilder<'tcx> {
         struct_span_err!(
             self.tcx.sess,
-            span,
+            spans,
             E0106,
             "missing lifetime specifier{}",
             pluralize!(count)
@@ -1821,81 +1821,107 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
     crate fn add_missing_lifetime_specifiers_label(
         &self,
         err: &mut DiagnosticBuilder<'_>,
-        span: Span,
-        count: usize,
+        spans_with_counts: Vec<(Span, usize)>,
         lifetime_names: &FxHashSet<Symbol>,
         lifetime_spans: Vec<Span>,
         params: &[ElisionFailureInfo],
     ) {
-        let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
-
-        err.span_label(
-            span,
-            &format!(
-                "expected {} lifetime parameter{}",
-                if count == 1 { "named".to_string() } else { count.to_string() },
-                pluralize!(count)
-            ),
-        );
+        let snippets: Vec<Option<String>> = spans_with_counts
+            .iter()
+            .map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok())
+            .collect();
 
-        let suggest_existing = |err: &mut DiagnosticBuilder<'_>,
-                                name: &str,
-                                formatter: &dyn Fn(&str) -> String| {
-            if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
-                self.missing_named_lifetime_spots.iter().rev().next()
-            {
-                // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
-                // using `'a`, but also introduce the concept of HRLTs by suggesting
-                // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
-                let mut introduce_suggestion = vec![];
+        for (span, count) in &spans_with_counts {
+            err.span_label(
+                *span,
+                format!(
+                    "expected {} lifetime parameter{}",
+                    if *count == 1 { "named".to_string() } else { count.to_string() },
+                    pluralize!(*count),
+                ),
+            );
+        }
 
-                let a_to_z_repeat_n = |n| {
-                    (b'a'..=b'z').map(move |c| {
-                        let mut s = '\''.to_string();
-                        s.extend(std::iter::repeat(char::from(c)).take(n));
-                        s
-                    })
-                };
+        let suggest_existing =
+            |err: &mut DiagnosticBuilder<'_>,
+             name: &str,
+             formatters: Vec<Option<Box<dyn Fn(&str) -> String>>>| {
+                if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
+                    self.missing_named_lifetime_spots.iter().rev().next()
+                {
+                    // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
+                    // using `'a`, but also introduce the concept of HRLTs by suggesting
+                    // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
+                    let mut introduce_suggestion = vec![];
+
+                    let a_to_z_repeat_n = |n| {
+                        (b'a'..=b'z').map(move |c| {
+                            let mut s = '\''.to_string();
+                            s.extend(std::iter::repeat(char::from(c)).take(n));
+                            s
+                        })
+                    };
 
-                // If all single char lifetime names are present, we wrap around and double the chars.
-                let lt_name = (1..)
-                    .flat_map(a_to_z_repeat_n)
-                    .find(|lt| !lifetime_names.contains(&Symbol::intern(&lt)))
-                    .unwrap();
-                let msg = format!(
-                    "consider making the {} lifetime-generic with a new `{}` lifetime",
-                    span_type.descr(),
-                    lt_name,
-                );
-                err.note(
-                    "for more information on higher-ranked polymorphism, visit \
+                    // If all single char lifetime names are present, we wrap around and double the chars.
+                    let lt_name = (1..)
+                        .flat_map(a_to_z_repeat_n)
+                        .find(|lt| !lifetime_names.contains(&Symbol::intern(&lt)))
+                        .unwrap();
+                    let msg = format!(
+                        "consider making the {} lifetime-generic with a new `{}` lifetime",
+                        span_type.descr(),
+                        lt_name,
+                    );
+                    err.note(
+                        "for more information on higher-ranked polymorphism, visit \
                     https://doc.rust-lang.org/nomicon/hrtb.html",
-                );
-                let for_sugg = span_type.suggestion(&lt_name);
-                for param in params {
-                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) {
-                        if snippet.starts_with('&') && !snippet.starts_with("&'") {
-                            introduce_suggestion
-                                .push((param.span, format!("&{} {}", lt_name, &snippet[1..])));
-                        } else if let Some(stripped) = snippet.strip_prefix("&'_ ") {
-                            introduce_suggestion
-                                .push((param.span, format!("&{} {}", lt_name, stripped)));
+                    );
+                    let for_sugg = span_type.suggestion(&lt_name);
+                    for param in params {
+                        if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span)
+                        {
+                            if snippet.starts_with('&') && !snippet.starts_with("&'") {
+                                introduce_suggestion
+                                    .push((param.span, format!("&{} {}", lt_name, &snippet[1..])));
+                            } else if let Some(stripped) = snippet.strip_prefix("&'_ ") {
+                                introduce_suggestion
+                                    .push((param.span, format!("&{} {}", lt_name, stripped)));
+                            }
+                        }
+                    }
+                    introduce_suggestion.push((*for_span, for_sugg));
+                    for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) {
+                        if let Some(formatter) = formatter {
+                            introduce_suggestion.push((*span, formatter(&lt_name)));
                         }
                     }
+                    err.multipart_suggestion_with_style(
+                        &msg,
+                        introduce_suggestion,
+                        Applicability::MaybeIncorrect,
+                        SuggestionStyle::ShowAlways,
+                    );
                 }
-                introduce_suggestion.push((*for_span, for_sugg));
-                introduce_suggestion.push((span, formatter(&lt_name)));
-                err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
-            }
 
-            err.span_suggestion_verbose(
-                span,
-                &format!("consider using the `{}` lifetime", lifetime_names.iter().next().unwrap()),
-                formatter(name),
-                Applicability::MaybeIncorrect,
-            );
-        };
-        let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
+                let spans_suggs: Vec<_> = formatters
+                    .into_iter()
+                    .zip(spans_with_counts.iter())
+                    .filter_map(|(fmt, (span, _))| {
+                        if let Some(formatter) = fmt { Some((formatter, span)) } else { None }
+                    })
+                    .map(|(formatter, span)| (*span, formatter(name)))
+                    .collect();
+                err.multipart_suggestion_with_style(
+                    &format!(
+                        "consider using the `{}` lifetime",
+                        lifetime_names.iter().next().unwrap()
+                    ),
+                    spans_suggs,
+                    Applicability::MaybeIncorrect,
+                    SuggestionStyle::ShowAlways,
+                );
+            };
+        let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: Vec<Option<String>>| {
             for missing in self.missing_named_lifetime_spots.iter().rev() {
                 let mut introduce_suggestion = vec![];
                 let msg;
@@ -1940,38 +1966,44 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                         (*span, span_type.suggestion("'a"))
                     }
                     MissingLifetimeSpot::Static => {
-                        let (span, sugg) = match snippet.as_deref() {
-                            Some("&") => (span.shrink_to_hi(), "'static ".to_owned()),
-                            Some("'_") => (span, "'static".to_owned()),
-                            Some(snippet) if !snippet.ends_with('>') => {
-                                if snippet == "" {
-                                    (
-                                        span,
-                                        std::iter::repeat("'static")
-                                            .take(count)
-                                            .collect::<Vec<_>>()
-                                            .join(", "),
-                                    )
-                                } else {
-                                    (
-                                        span.shrink_to_hi(),
-                                        format!(
-                                            "<{}>",
+                        let mut spans_suggs = Vec::new();
+                        for ((span, count), snippet) in
+                            spans_with_counts.iter().copied().zip(snippets.iter())
+                        {
+                            let (span, sugg) = match snippet.as_deref() {
+                                Some("&") => (span.shrink_to_hi(), "'static ".to_owned()),
+                                Some("'_") => (span, "'static".to_owned()),
+                                Some(snippet) if !snippet.ends_with('>') => {
+                                    if snippet == "" {
+                                        (
+                                            span,
                                             std::iter::repeat("'static")
                                                 .take(count)
                                                 .collect::<Vec<_>>()
-                                                .join(", ")
-                                        ),
-                                    )
+                                                .join(", "),
+                                        )
+                                    } else {
+                                        (
+                                            span.shrink_to_hi(),
+                                            format!(
+                                                "<{}>",
+                                                std::iter::repeat("'static")
+                                                    .take(count)
+                                                    .collect::<Vec<_>>()
+                                                    .join(", ")
+                                            ),
+                                        )
+                                    }
                                 }
-                            }
-                            _ => continue,
-                        };
-                        err.span_suggestion_verbose(
-                            span,
+                                _ => continue,
+                            };
+                            spans_suggs.push((span, sugg.to_string()));
+                        }
+                        err.multipart_suggestion_with_style(
                             "consider using the `'static` lifetime",
-                            sugg.to_string(),
+                            spans_suggs,
                             Applicability::MaybeIncorrect,
+                            SuggestionStyle::ShowAlways,
                         );
                         continue;
                     }
@@ -1986,8 +2018,17 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                         }
                     }
                 }
-                introduce_suggestion.push((span, sugg.to_string()));
-                err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
+                for ((span, _), sugg) in spans_with_counts.iter().copied().zip(suggs.iter()) {
+                    if let Some(sugg) = sugg {
+                        introduce_suggestion.push((span, sugg.to_string()));
+                    }
+                }
+                err.multipart_suggestion_with_style(
+                    &msg,
+                    introduce_suggestion,
+                    Applicability::MaybeIncorrect,
+                    SuggestionStyle::ShowAlways,
+                );
                 if should_break {
                     break;
                 }
@@ -1995,68 +2036,75 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
         };
 
         let lifetime_names: Vec<_> = lifetime_names.iter().collect();
-        match (&lifetime_names[..], snippet.as_deref()) {
-            ([name], Some("&")) => {
-                suggest_existing(err, &name.as_str()[..], &|name| format!("&{} ", name));
-            }
-            ([name], Some("'_")) => {
-                suggest_existing(err, &name.as_str()[..], &|n| n.to_string());
-            }
-            ([name], Some("")) => {
-                suggest_existing(err, &name.as_str()[..], &|n| format!("{}, ", n).repeat(count));
-            }
-            ([name], Some(snippet)) if !snippet.ends_with('>') => {
-                let f = |name: &str| {
-                    format!(
-                        "{}<{}>",
-                        snippet,
-                        std::iter::repeat(name.to_string())
-                            .take(count)
-                            .collect::<Vec<_>>()
-                            .join(", ")
-                    )
-                };
-                suggest_existing(err, &name.as_str()[..], &f);
-            }
-            ([], Some("&")) if count == 1 => {
-                suggest_new(err, "&'a ");
-            }
-            ([], Some("'_")) if count == 1 => {
-                suggest_new(err, "'a");
+        match &lifetime_names[..] {
+            [name] => {
+                let mut suggs: Vec<Option<Box<dyn Fn(&str) -> String>>> = Vec::new();
+                for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied())
+                {
+                    suggs.push(match snippet.as_deref() {
+                        Some("&") => Some(Box::new(|name| format!("&{} ", name))),
+                        Some("'_") => Some(Box::new(|n| n.to_string())),
+                        Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))),
+                        Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| {
+                            format!(
+                                "{}<{}>",
+                                snippet,
+                                std::iter::repeat(name.to_string())
+                                    .take(count)
+                                    .collect::<Vec<_>>()
+                                    .join(", ")
+                            )
+                        })),
+                        _ => None,
+                    });
+                }
+                suggest_existing(err, &name.as_str()[..], suggs);
             }
-            ([], Some(snippet)) if !snippet.ends_with('>') => {
-                if snippet == "" {
-                    // This happens when we have `type Bar<'a> = Foo<T>` where we point at the space
-                    // before `T`. We will suggest `type Bar<'a> = Foo<'a, T>`.
-                    suggest_new(
-                        err,
-                        &std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""),
-                    );
-                } else {
-                    suggest_new(
-                        err,
-                        &format!(
+            [] => {
+                let mut suggs = Vec::new();
+                for (snippet, (_, count)) in
+                    snippets.iter().cloned().zip(spans_with_counts.iter().copied())
+                {
+                    suggs.push(match snippet.as_deref() {
+                        Some("&") => Some("&'a ".to_string()),
+                        Some("'_") => Some("'a".to_string()),
+                        Some("") => {
+                            Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""))
+                        }
+                        Some(snippet) => Some(format!(
                             "{}<{}>",
                             snippet,
-                            std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ")
-                        ),
-                    );
+                            std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "),
+                        )),
+                        None => None,
+                    });
                 }
+                suggest_new(err, suggs);
             }
-            (lts, ..) if lts.len() > 1 => {
+            lts if lts.len() > 1 => {
                 err.span_note(lifetime_spans, "these named lifetimes are available to use");
-                if Some("") == snippet.as_deref() {
+
+                let mut spans_suggs: Vec<_> = Vec::new();
+                for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) {
+                    match snippet.as_deref() {
+                        Some("") => spans_suggs.push((span, "'lifetime, ".to_string())),
+                        Some("&") => spans_suggs.push((span, "&'lifetime ".to_string())),
+                        _ => {}
+                    }
+                }
+
+                if spans_suggs.len() > 0 {
                     // This happens when we have `Foo<T>` where we point at the space before `T`,
                     // but this can be confusing so we give a suggestion with placeholders.
-                    err.span_suggestion_verbose(
-                        span,
+                    err.multipart_suggestion_with_style(
                         "consider using one of the available lifetimes here",
-                        "'lifetime, ".repeat(count),
+                        spans_suggs,
                         Applicability::HasPlaceholders,
+                        SuggestionStyle::ShowAlways,
                     );
                 }
             }
-            _ => {}
+            _ => unreachable!(),
         }
     }
 
index 174df09cbdbb2b5e4dcea4cb05da17b9f6974f5e..e8d21af435887ad907d5f6ebac045b0fdda371c8 100644 (file)
@@ -2956,7 +2956,6 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
             return;
         }
 
-        let span = lifetime_refs[0].span;
         let mut late_depth = 0;
         let mut scope = self.scope;
         let mut lifetime_names = FxHashSet::default();
@@ -3035,7 +3034,16 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
             }
         };
 
-        let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len());
+        let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
+        spans.sort();
+        let mut spans_dedup = spans.clone();
+        spans_dedup.dedup();
+        let spans_with_counts: Vec<_> = spans_dedup
+            .into_iter()
+            .map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
+            .collect();
+
+        let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len());
 
         if let Some(params) = error {
             // If there's no lifetime available, suggest `'static`.
@@ -3043,10 +3051,10 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
                 lifetime_names.insert(kw::StaticLifetime);
             }
         }
+
         self.add_missing_lifetime_specifiers_label(
             &mut err,
-            span,
-            lifetime_refs.len(),
+            spans_with_counts,
             &lifetime_names,
             lifetime_spans,
             error.unwrap_or(&[]),
index 0eec12aa03f2d72c4708b9d8800e3c01dfe41ed8..a9e14754334c1b8e6f3b45414543e8d822d240ef 100644 (file)
@@ -112,7 +112,7 @@ pub fn check_expected_reuse(&self, diag: &rustc_errors::Handler) {
                                        not recorded",
                         cgu_user_name, cgu_name
                     );
-                    diag.span_fatal(error_span.0, &msg).raise();
+                    diag.span_fatal(error_span.0, &msg)
                 }
             }
         }
index 08d6f4a46fefeafb9a1da67843cd635ef4d1ce3c..f517c483758d27b4127312335f3835b0cbd0efb6 100644 (file)
@@ -5,7 +5,7 @@
 
 use crate::lint;
 use crate::search_paths::SearchPath;
-use crate::utils::{CanonicalizedPath, NativeLibKind};
+use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use crate::{early_error, early_warn, Session};
 
 use rustc_data_structures::fx::FxHashSet;
@@ -685,10 +685,10 @@ fn default() -> Options {
             target_triple: TargetTriple::from_triple(host_triple()),
             test: false,
             incremental: None,
-            debugging_opts: basic_debugging_options(),
+            debugging_opts: Default::default(),
             prints: Vec::new(),
             borrowck_mode: BorrowckMode::Migrate,
-            cg: basic_codegen_options(),
+            cg: Default::default(),
             error_format: ErrorOutputType::default(),
             externs: Externs(BTreeMap::new()),
             extern_dep_specs: ExternDepSpecs(BTreeMap::new()),
@@ -893,8 +893,13 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo
     user_cfg
 }
 
-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);
+pub(super) fn build_target_config(
+    opts: &Options,
+    target_override: Option<Target>,
+    sysroot: &PathBuf,
+) -> Target {
+    let target_result =
+        target_override.map_or_else(|| Target::search(&opts.target_triple, sysroot), Ok);
     let target = target_result.unwrap_or_else(|e| {
         early_error(
             opts.error_format,
@@ -1027,8 +1032,11 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
             "",
             "Link the generated crate(s) to the specified native
                              library NAME. The optional KIND can be one of
-                             static, framework, or dylib (the default).",
-            "[KIND=]NAME",
+                             static, framework, or dylib (the default).
+                             Optional comma separated MODIFIERS (bundle|verbatim|whole-archive|as-needed)
+                             may be specified each with a prefix of either '+' to
+                             enable or '-' to disable.",
+            "[KIND[:MODIFIERS]=]NAME[:RENAME]",
         ),
         make_crate_type_option(),
         opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"),
@@ -1591,52 +1599,127 @@ fn select_debuginfo(
     }
 }
 
-fn parse_libs(
-    matches: &getopts::Matches,
+fn parse_native_lib_kind(kind: &str, error_format: ErrorOutputType) -> NativeLibKind {
+    match kind {
+        "dylib" => NativeLibKind::Dylib { as_needed: None },
+        "framework" => NativeLibKind::Framework { as_needed: None },
+        "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
+        "static-nobundle" => {
+            early_warn(
+                error_format,
+                "library kind `static-nobundle` has been superseded by specifying \
+                `-bundle` on library kind `static`. Try `static:-bundle`",
+            );
+            NativeLibKind::Static { bundle: Some(false), whole_archive: None }
+        }
+        s => early_error(
+            error_format,
+            &format!("unknown library kind `{}`, expected one of dylib, framework, or static", s),
+        ),
+    }
+}
+
+fn parse_native_lib_modifiers(
+    is_nightly: bool,
+    mut kind: NativeLibKind,
+    modifiers: &str,
     error_format: ErrorOutputType,
-) -> Vec<(String, Option<String>, NativeLibKind)> {
+) -> (NativeLibKind, Option<bool>) {
+    let mut verbatim = None;
+    for modifier in modifiers.split(',') {
+        let (modifier, value) = match modifier.strip_prefix(&['+', '-'][..]) {
+            Some(m) => (m, modifier.starts_with('+')),
+            None => early_error(
+                error_format,
+                "invalid linking modifier syntax, expected '+' or '-' prefix \
+                    before one of: bundle, verbatim, whole-archive, as-needed",
+            ),
+        };
+
+        if !is_nightly {
+            early_error(
+                error_format,
+                "linking modifiers are currently unstable and only accepted on \
+                the nightly compiler",
+            );
+        }
+
+        match (modifier, &mut kind) {
+            ("bundle", NativeLibKind::Static { bundle, .. }) => {
+                *bundle = Some(value);
+            }
+            ("bundle", _) => early_error(
+                error_format,
+                "bundle linking modifier is only compatible with \
+                    `static` linking kind",
+            ),
+
+            ("verbatim", _) => verbatim = Some(value),
+
+            ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
+                *whole_archive = Some(value);
+            }
+            ("whole-archive", _) => early_error(
+                error_format,
+                "whole-archive linking modifier is only compatible with \
+                    `static` linking kind",
+            ),
+
+            ("as-needed", NativeLibKind::Dylib { as_needed })
+            | ("as-needed", NativeLibKind::Framework { as_needed }) => {
+                *as_needed = Some(value);
+            }
+            ("as-needed", _) => early_error(
+                error_format,
+                "as-needed linking modifier is only compatible with \
+                    `dylib` and `framework` linking kinds",
+            ),
+
+            _ => early_error(
+                error_format,
+                &format!(
+                    "unrecognized linking modifier `{}`, expected one \
+                    of: bundle, verbatim, whole-archive, as-needed",
+                    modifier
+                ),
+            ),
+        }
+    }
+
+    (kind, verbatim)
+}
+
+fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<NativeLib> {
+    let is_nightly = nightly_options::match_is_nightly_build(matches);
     matches
         .opt_strs("l")
         .into_iter()
         .map(|s| {
-            // Parse string of the form "[KIND=]lib[:new_name]",
-            // where KIND is one of "dylib", "framework", "static".
-            let (name, kind) = match s.split_once('=') {
-                None => (s, NativeLibKind::Unspecified),
+            // Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]",
+            // where KIND is one of "dylib", "framework", "static" and
+            // where MODIFIERS are  a comma separated list of supported modifiers
+            // (bundle, verbatim, whole-archive, as-needed). Each modifier is prefixed
+            // with either + or - to indicate whether it is enabled or disabled.
+            // The last value specified for a given modifier wins.
+            let (name, kind, verbatim) = match s.split_once('=') {
+                None => (s, NativeLibKind::Unspecified, None),
                 Some((kind, name)) => {
-                    let kind = match kind {
-                        "dylib" => NativeLibKind::Dylib,
-                        "framework" => NativeLibKind::Framework,
-                        "static" => NativeLibKind::StaticBundle,
-                        "static-nobundle" => NativeLibKind::StaticNoBundle,
-                        s => {
-                            early_error(
-                                error_format,
-                                &format!(
-                                    "unknown library kind `{}`, expected \
-                                     one of dylib, framework, or static",
-                                    s
-                                ),
-                            );
+                    let (kind, verbatim) = match kind.split_once(':') {
+                        None => (parse_native_lib_kind(kind, error_format), None),
+                        Some((kind, modifiers)) => {
+                            let kind = parse_native_lib_kind(kind, error_format);
+                            parse_native_lib_modifiers(is_nightly, kind, modifiers, error_format)
                         }
                     };
-                    (name.to_string(), kind)
+                    (name.to_string(), kind, verbatim)
                 }
             };
-            if kind == NativeLibKind::StaticNoBundle
-                && !nightly_options::match_is_nightly_build(matches)
-            {
-                early_error(
-                    error_format,
-                    "the library kind 'static-nobundle' is only \
-                     accepted on the nightly compiler",
-                );
-            }
+
             let (name, new_name) = match name.split_once(':') {
                 None => (name, None),
                 Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
             };
-            (name, new_name, kind)
+            NativeLib { name, new_name, kind, verbatim }
         })
         .collect()
 }
@@ -1842,7 +1925,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
 
-    let mut debugging_opts = build_debugging_options(matches, error_format);
+    let mut debugging_opts = DebuggingOptions::build(matches, error_format);
     check_debug_option_stability(&debugging_opts, error_format, json_rendered);
 
     if !debugging_opts.unstable_options && json_unused_externs {
@@ -1855,7 +1938,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let output_types = parse_output_types(&debugging_opts, matches, error_format);
 
-    let mut cg = build_codegen_options(matches, error_format);
+    let mut cg = CodegenOptions::build(matches, error_format);
     let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto(
         &output_types,
         matches,
@@ -2316,7 +2399,7 @@ pub fn needs_analysis(&self) -> bool {
     };
     use crate::lint;
     use crate::options::WasiExecModel;
-    use crate::utils::NativeLibKind;
+    use crate::utils::{NativeLib, NativeLibKind};
     use rustc_feature::UnstableFeatures;
     use rustc_span::edition::Edition;
     use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
@@ -2391,6 +2474,7 @@ fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) {
         DebugInfo,
         UnstableFeatures,
         OutputTypes,
+        NativeLib,
         NativeLibKind,
         SanitizerSet,
         CFGuard,
@@ -2409,8 +2493,8 @@ fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) {
         PathBuf,
         (PathBuf, PathBuf),
         CrateType,
+        NativeLib,
         (String, lint::Level),
-        (String, Option<String>, NativeLibKind),
         (String, u64)
     );
 
index 2df326628e7872962571110dab50d0b109975970..6fe6a555f1af81ccf8f5c6b87039af4bf3ce5a65 100644 (file)
@@ -1,6 +1,5 @@
 pub use self::FileMatch::*;
 
-use std::borrow::Cow;
 use std::env;
 use std::fs;
 use std::path::{Path, PathBuf};
@@ -91,26 +90,21 @@ pub fn search_path_dirs(&self) -> Vec<PathBuf> {
 
     // Returns a list of directories where target-specific tool binaries are located.
     pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec<PathBuf> {
-        let mut p = PathBuf::from(self.sysroot);
-        p.push(find_libdir(self.sysroot).as_ref());
-        p.push(RUST_LIB_DIR);
-        p.push(&self.triple);
-        p.push("bin");
+        let rustlib_path = rustc_target::target_rustlib_path(self.sysroot, &self.triple);
+        let p = std::array::IntoIter::new([
+            Path::new(&self.sysroot),
+            Path::new(&rustlib_path),
+            Path::new("bin"),
+        ])
+        .collect::<PathBuf>();
         if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] }
     }
 }
 
-pub fn relative_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    let mut p = PathBuf::from(find_libdir(sysroot).as_ref());
-    assert!(p.is_relative());
-    p.push(RUST_LIB_DIR);
-    p.push(target_triple);
-    p.push("lib");
-    p
-}
-
 pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    sysroot.join(&relative_target_lib_path(sysroot, target_triple))
+    let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple);
+    std::array::IntoIter::new([sysroot, Path::new(&rustlib_path), Path::new("lib")])
+        .collect::<PathBuf>()
 }
 
 // This function checks if sysroot is found using env::args().next(), and if it
@@ -157,11 +151,13 @@ fn from_env_args_next() -> Option<PathBuf> {
                     return None;
                 }
 
+                // Pop off `bin/rustc`, obtaining the suspected sysroot.
                 p.pop();
                 p.pop();
-                let mut libdir = PathBuf::from(&p);
-                libdir.push(find_libdir(&p).as_ref());
-                if libdir.exists() { Some(p) } else { None }
+                // Look for the target rustlib directory in the suspected sysroot.
+                let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy");
+                rustlib_path.pop(); // pop off the dummy target.
+                if rustlib_path.exists() { Some(p) } else { None }
             }
             None => None,
         }
@@ -171,37 +167,3 @@ fn from_env_args_next() -> Option<PathBuf> {
     // use env::current_exe() to imply sysroot.
     from_env_args_next().unwrap_or_else(from_current_exe)
 }
-
-// The name of the directory rustc expects libraries to be located.
-fn find_libdir(sysroot: &Path) -> Cow<'static, str> {
-    // FIXME: This is a quick hack to make the rustc binary able to locate
-    // Rust libraries in Linux environments where libraries might be installed
-    // to lib64/lib32. This would be more foolproof by basing the sysroot off
-    // of the directory where `librustc_driver` is located, rather than
-    // where the rustc binary is.
-    // If --libdir is set during configuration to the value other than
-    // "lib" (i.e., non-default), this value is used (see issue #16552).
-
-    #[cfg(target_pointer_width = "64")]
-    const PRIMARY_LIB_DIR: &str = "lib64";
-
-    #[cfg(target_pointer_width = "32")]
-    const PRIMARY_LIB_DIR: &str = "lib32";
-
-    const SECONDARY_LIB_DIR: &str = "lib";
-
-    match option_env!("CFG_LIBDIR_RELATIVE") {
-        None | Some("lib") => {
-            if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() {
-                PRIMARY_LIB_DIR.into()
-            } else {
-                SECONDARY_LIB_DIR.into()
-            }
-        }
-        Some(libdir) => libdir.into(),
-    }
-}
-
-// The name of rustc's own place to organize libraries.
-// Used to be "rustc", now the default is "rustlib"
-const RUST_LIB_DIR: &str = "rustlib";
index cd28517bfbc3aadac8ba2b03984b668cfc5e2a99..10e195f4f2548e1e197edea4372e8f5491186ada 100644 (file)
@@ -3,7 +3,7 @@
 use crate::early_error;
 use crate::lint;
 use crate::search_paths::SearchPath;
-use crate::utils::NativeLibKind;
+use crate::utils::NativeLib;
 
 use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet};
 use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TargetTriple, TlsModel};
@@ -133,7 +133,7 @@ pub struct Options {
         describe_lints: bool [UNTRACKED],
         output_types: OutputTypes [TRACKED],
         search_paths: Vec<SearchPath> [UNTRACKED],
-        libs: Vec<(String, Option<String>, NativeLibKind)> [TRACKED],
+        libs: Vec<NativeLib> [TRACKED],
         maybe_sysroot: Option<PathBuf> [UNTRACKED],
 
         target_triple: TargetTriple [TRACKED],
@@ -210,9 +210,7 @@ pub struct Options {
 /// generated code to parse an option into its respective field in the struct. There are a few
 /// hand-written parsers for parsing specific types of values in this module.
 macro_rules! options {
-    ($struct_name:ident, $setter_name:ident, $defaultfn:ident,
-     $buildfn:ident, $prefix:expr, $outputname:expr,
-     $stat:ident,
+    ($struct_name:ident, $stat:ident, $prefix:expr, $outputname:expr,
      $($( #[$attr:meta] )* $opt:ident : $t:ty = (
         $init:expr,
         $parse:ident,
@@ -223,50 +221,20 @@ macro_rules! options {
     #[derive(Clone)]
     pub struct $struct_name { $(pub $opt: $t),* }
 
-    pub fn $defaultfn() -> $struct_name {
-        $struct_name { $( $( #[$attr] )* $opt: $init),* }
-    }
-
-    pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name
-    {
-        let mut op = $defaultfn();
-        for option in matches.opt_strs($prefix) {
-            let (key, value) = match option.split_once('=') {
-                None => (option, None),
-                Some((k, v)) => (k.to_string(), Some(v)),
-            };
-            let option_to_lookup = key.replace("-", "_");
-            let mut found = false;
-            for &(candidate, setter, type_desc, _) in $stat {
-                if option_to_lookup != candidate { continue }
-                if !setter(&mut op, value) {
-                    match value {
-                        None => {
-                            early_error(error_format, &format!("{0} option `{1}` requires \
-                                                                {2} ({3} {1}=<value>)",
-                                                               $outputname, key,
-                                                               type_desc, $prefix))
-                        }
-                        Some(value) => {
-                            early_error(error_format, &format!("incorrect value `{}` for {} \
-                                                                option `{}` - {} was expected",
-                                                               value, $outputname,
-                                                               key, type_desc))
-                        }
-                    }
-                }
-                found = true;
-                break;
-            }
-            if !found {
-                early_error(error_format, &format!("unknown {} option: `{}`",
-                                                   $outputname, key));
-            }
+    impl Default for $struct_name {
+        fn default() -> $struct_name {
+            $struct_name { $( $( #[$attr] )* $opt: $init),* }
         }
-        return op;
     }
 
     impl $struct_name {
+        pub fn build(
+            matches: &getopts::Matches,
+            error_format: ErrorOutputType,
+        ) -> $struct_name {
+            build_options(matches, $stat, $prefix, $outputname, error_format)
+        }
+
         fn dep_tracking_hash(&self, _for_crate_hash: bool, error_format: ErrorOutputType) -> u64 {
             let mut sub_hashes = BTreeMap::new();
             $({
@@ -284,26 +252,76 @@ fn dep_tracking_hash(&self, _for_crate_hash: bool, error_format: ErrorOutputType
         }
     }
 
-    pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool;
-    pub const $stat: &[(&str, $setter_name, &str, &str)] =
-        &[ $( (stringify!($opt), $crate::options::parse::$opt, $crate::options::desc::$parse, $desc) ),* ];
-
-    // Sometimes different options need to build a common structure.
-    // That structure can kept in one of the options' fields, the others become dummy.
-    macro_rules! redirect_field {
-        ($cg:ident.link_arg) => { $cg.link_args };
-        ($cg:ident.pre_link_arg) => { $cg.pre_link_args };
-        ($cg:ident.$field:ident) => { $cg.$field };
-    }
+    pub const $stat: OptionDescrs<$struct_name> =
+        &[ $( (stringify!($opt), $opt, desc::$parse, $desc) ),* ];
 
     $(
-        pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
-            $crate::options::parse::$parse(&mut redirect_field!(cg.$opt), v)
+        fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
+            parse::$parse(&mut redirect_field!(cg.$opt), v)
         }
     )*
 
 ) }
 
+// Sometimes different options need to build a common structure.
+// That structure can be kept in one of the options' fields, the others become dummy.
+macro_rules! redirect_field {
+    ($cg:ident.link_arg) => {
+        $cg.link_args
+    };
+    ($cg:ident.pre_link_arg) => {
+        $cg.pre_link_args
+    };
+    ($cg:ident.$field:ident) => {
+        $cg.$field
+    };
+}
+
+type OptionSetter<O> = fn(&mut O, v: Option<&str>) -> bool;
+type OptionDescrs<O> = &'static [(&'static str, OptionSetter<O>, &'static str, &'static str)];
+
+fn build_options<O: Default>(
+    matches: &getopts::Matches,
+    descrs: OptionDescrs<O>,
+    prefix: &str,
+    outputname: &str,
+    error_format: ErrorOutputType,
+) -> O {
+    let mut op = O::default();
+    for option in matches.opt_strs(prefix) {
+        let (key, value) = match option.split_once('=') {
+            None => (option, None),
+            Some((k, v)) => (k.to_string(), Some(v)),
+        };
+
+        let option_to_lookup = key.replace("-", "_");
+        match descrs.iter().find(|(name, ..)| *name == option_to_lookup) {
+            Some((_, setter, type_desc, _)) => {
+                if !setter(&mut op, value) {
+                    match value {
+                        None => early_error(
+                            error_format,
+                            &format!(
+                                "{0} option `{1}` requires {2} ({3} {1}=<value>)",
+                                outputname, key, type_desc, prefix
+                            ),
+                        ),
+                        Some(value) => early_error(
+                            error_format,
+                            &format!(
+                                "incorrect value `{}` for {} option `{}` - {} was expected",
+                                value, outputname, key, type_desc
+                            ),
+                        ),
+                    }
+                }
+            }
+            None => early_error(error_format, &format!("unknown {} option: `{}`", outputname, key)),
+        }
+    }
+    return op;
+}
+
 #[allow(non_upper_case_globals)]
 mod desc {
     pub const parse_no_flag: &str = "no value";
@@ -847,9 +865,8 @@ mod parse {
     }
 }
 
-options! {CodegenOptions, CodegenSetter, basic_codegen_options,
-          build_codegen_options, "C", "codegen",
-          CG_OPTIONS,
+options! {
+    CodegenOptions, CG_OPTIONS, "C", "codegen",
 
     // This list is in alphabetical order.
     //
@@ -957,9 +974,8 @@ mod parse {
     // - src/doc/rustc/src/codegen-options/index.md
 }
 
-options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
-          build_debugging_options, "Z", "debugging",
-          DB_OPTIONS,
+options! {
+    DebuggingOptions, DB_OPTIONS, "Z", "debugging",
 
     // This list is in alphabetical order.
     //
@@ -1080,12 +1096,12 @@ mod parse {
         "gather statistics about the input (default: no)"),
     instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
         "instrument the generated code to support LLVM source-based code coverage \
-        reports (note, the compiler build config must include `profiler = true`, \
-        and is mutually exclusive with `-C profile-generate`/`-C profile-use`); \
-        implies `-Z symbol-mangling-version=v0`; disables/overrides some Rust \
-        optimizations. Optional values are: `=all` (default coverage), \
-        `=except-unused-generics`, `=except-unused-functions`, or `=off` \
-        (default: instrument-coverage=off)"),
+        reports (note, the compiler build config must include `profiler = true`); \
+        implies `-Z symbol-mangling-version=v0`. Optional values are:
+        `=all` (implicit value)
+        `=except-unused-generics`
+        `=except-unused-functions`
+        `=off` (default)"),
     instrument_mcount: bool = (false, parse_bool, [TRACKED],
         "insert function instrument code for mcount-based tracing (default: no)"),
     keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
@@ -1112,7 +1128,7 @@ mod parse {
         "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
     mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "emit noalias metadata for mutable references (default: yes for LLVM >= 12, otherwise no)"),
-    new_llvm_pass_manager: bool = (false, parse_bool, [TRACKED],
+    new_llvm_pass_manager: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "use new LLVM pass manager (default: no)"),
     nll_facts: bool = (false, parse_bool, [UNTRACKED],
         "dump facts from NLL analysis into side files (default: no)"),
index e7dfc4b8c4128335042d63ae4fe2ca5ea380cc92..66b0f5c11be4f41f6a976b75b6b7027f67acff5f 100644 (file)
@@ -421,7 +421,7 @@ pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> {
     }
 
     pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
-        self.diagnostic().span_fatal(sp, msg).raise()
+        self.diagnostic().span_fatal(sp, msg)
     }
     pub fn span_fatal_with_code<S: Into<MultiSpan>>(
         &self,
@@ -429,7 +429,7 @@ pub fn span_fatal_with_code<S: Into<MultiSpan>>(
         msg: &str,
         code: DiagnosticId,
     ) -> ! {
-        self.diagnostic().span_fatal_with_code(sp, msg, code).raise()
+        self.diagnostic().span_fatal_with_code(sp, msg, code)
     }
     pub fn fatal(&self, msg: &str) -> ! {
         self.diagnostic().fatal(msg).raise()
@@ -492,12 +492,6 @@ pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: Di
     pub fn warn(&self, msg: &str) {
         self.diagnostic().warn(msg)
     }
-    pub fn opt_span_warn<S: Into<MultiSpan>>(&self, opt_sp: Option<S>, msg: &str) {
-        match opt_sp {
-            Some(sp) => self.span_warn(sp, msg),
-            None => self.warn(msg),
-        }
-    }
     /// Delay a span_bug() call until abort_if_errors()
     #[track_caller]
     pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
@@ -1282,9 +1276,14 @@ pub fn build_session(
         DiagnosticOutput::Raw(write) => Some(write),
     };
 
-    let target_cfg = config::build_target_config(&sopts, target_override);
+    let sysroot = match &sopts.maybe_sysroot {
+        Some(sysroot) => sysroot.clone(),
+        None => filesearch::get_or_default_sysroot(),
+    };
+
+    let target_cfg = config::build_target_config(&sopts, target_override, &sysroot);
     let host_triple = TargetTriple::from_triple(config::host_triple());
-    let host = Target::search(&host_triple).unwrap_or_else(|e| {
+    let host = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| {
         early_error(sopts.error_format, &format!("Error loading host specification: {}", e))
     });
 
@@ -1331,10 +1330,6 @@ pub fn build_session(
 
     let mut parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map);
     parse_sess.assume_incomplete_release = sopts.debugging_opts.assume_incomplete_release;
-    let sysroot = match &sopts.maybe_sysroot {
-        Some(sysroot) => sysroot.clone(),
-        None => filesearch::get_or_default_sysroot(),
-    };
 
     let host_triple = config::host_triple();
     let target_triple = sopts.target_triple.triple();
index e9d597d1ba65c322d95af9af7dac561b8b252f8a..1a044e677a02fedb4aef8be2057044c6dc05c244 100644 (file)
@@ -19,25 +19,42 @@ pub fn time<R>(&self, what: &'static str, f: impl FnOnce() -> R) -> R {
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
 pub enum NativeLibKind {
-    /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included
-    /// when linking a final binary, but not when archiving an rlib.
-    StaticNoBundle,
-    /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included
-    /// when linking a final binary, but also included when archiving an rlib.
-    StaticBundle,
+    /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC)
+    Static {
+        /// Whether to bundle objects from static library into produced rlib
+        bundle: Option<bool>,
+        /// Whether to link static library without throwing any object files away
+        whole_archive: Option<bool>,
+    },
     /// Dynamic library (e.g. `libfoo.so` on Linux)
     /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC).
-    Dylib,
+    Dylib {
+        /// Whether the dynamic library will be linked only if it satifies some undefined symbols
+        as_needed: Option<bool>,
+    },
     /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library.
     RawDylib,
     /// A macOS-specific kind of dynamic libraries.
-    Framework,
+    Framework {
+        /// Whether the framework will be linked only if it satifies some undefined symbols
+        as_needed: Option<bool>,
+    },
     /// The library kind wasn't specified, `Dylib` is currently used as a default.
     Unspecified,
 }
 
 rustc_data_structures::impl_stable_hash_via_hash!(NativeLibKind);
 
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+pub struct NativeLib {
+    pub name: String,
+    pub new_name: Option<String>,
+    pub kind: NativeLibKind,
+    pub verbatim: Option<bool>,
+}
+
+rustc_data_structures::impl_stable_hash_via_hash!(NativeLib);
+
 /// A path that has been canonicalized along with its original, non-canonicalized form
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub struct CanonicalizedPath {
index f612d1425b932b9e7b0f925750ba8621b8568672..e365844980b4b2f7eaf0d2cf6631bdad035f1520 100644 (file)
@@ -109,7 +109,7 @@ pub trait FileLoader {
 
 impl FileLoader for RealFileLoader {
     fn file_exists(&self, path: &Path) -> bool {
-        fs::metadata(path).is_ok()
+        path.exists()
     }
 
     fn read_file(&self, path: &Path) -> io::Result<String> {
index ceb9b59b13ad1609489eab9cd8ef03307a42b25f..5ea39b343b5c18910b39a23be1a3f04a34e6c076 100644 (file)
@@ -102,7 +102,7 @@ pub fn data(self) -> SpanData {
             // Interned format.
             debug_assert!(self.ctxt_or_zero == 0);
             let index = self.base_or_index;
-            with_span_interner(|interner| *interner.get(index))
+            with_span_interner(|interner| interner.spans[index as usize])
         }
     }
 }
@@ -117,11 +117,6 @@ fn intern(&mut self, span_data: &SpanData) -> u32 {
         let (index, _) = self.spans.insert_full(*span_data);
         index as u32
     }
-
-    #[inline]
-    fn get(&self, index: u32) -> &SpanData {
-        &self.spans[index as usize]
-    }
 }
 
 // If an interner exists, return it. Otherwise, prepare a fresh one.
index b2dac10c83face662b8d66db8d8b1dd7570b6bba..4c80b84e3d275be55b5bb25313739ff7e6eb5d21 100644 (file)
         minnumf64,
         mips_target_feature,
         misc,
+        modifiers,
         module,
         module_path,
         more_struct_aliases,
         naked,
         naked_functions,
         name,
+        native_link_modifiers,
+        native_link_modifiers_as_needed,
+        native_link_modifiers_bundle,
+        native_link_modifiers_verbatim,
+        native_link_modifiers_whole_archive,
         ne,
         nearbyintf32,
         nearbyintf64,
index 6702538874705df0ffe83ed820bf5453b2bc1223..48ace9b65b6787979655b871b4e63965abc75ef8 100644 (file)
@@ -15,6 +15,8 @@
 #![feature(associated_type_bounds)]
 #![feature(exhaustive_patterns)]
 
+use std::path::{Path, PathBuf};
+
 #[macro_use]
 extern crate rustc_macros;
 
 /// This is a hack to allow using the `HashStable_Generic` derive macro
 /// instead of implementing everything in `rustc_middle`.
 pub trait HashStableContext {}
+
+/// The name of rustc's own place to organize libraries.
+///
+/// Used to be `rustc`, now the default is `rustlib`.
+const RUST_LIB_DIR: &str = "rustlib";
+
+/// Returns a `rustlib` path for this particular target, relative to the provided sysroot.
+///
+/// For example: `target_sysroot_path("/usr", "x86_64-unknown-linux-gnu")` =>
+/// `"lib*/rustlib/x86_64-unknown-linux-gnu"`.
+pub fn target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
+    let libdir = find_libdir(sysroot);
+    std::array::IntoIter::new([
+        Path::new(libdir.as_ref()),
+        Path::new(RUST_LIB_DIR),
+        Path::new(target_triple),
+    ])
+    .collect::<PathBuf>()
+}
+
+/// The name of the directory rustc expects libraries to be located.
+fn find_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> {
+    // FIXME: This is a quick hack to make the rustc binary able to locate
+    // Rust libraries in Linux environments where libraries might be installed
+    // to lib64/lib32. This would be more foolproof by basing the sysroot off
+    // of the directory where `librustc_driver` is located, rather than
+    // where the rustc binary is.
+    // If --libdir is set during configuration to the value other than
+    // "lib" (i.e., non-default), this value is used (see issue #16552).
+
+    #[cfg(target_pointer_width = "64")]
+    const PRIMARY_LIB_DIR: &str = "lib64";
+
+    #[cfg(target_pointer_width = "32")]
+    const PRIMARY_LIB_DIR: &str = "lib32";
+
+    const SECONDARY_LIB_DIR: &str = "lib";
+
+    match option_env!("CFG_LIBDIR_RELATIVE") {
+        None | Some("lib") => {
+            if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() {
+                PRIMARY_LIB_DIR.into()
+            } else {
+                SECONDARY_LIB_DIR.into()
+            }
+        }
+        Some(libdir) => libdir.into(),
+    }
+}
index 2e365d210f3f62356498e666dd010bf286b75a04..2b8e046c46b0e7135ce9893b3eb4375e87760547 100644 (file)
@@ -6,6 +6,17 @@ pub fn opts() -> TargetOptions {
     late_link_args.insert(
         LinkerFlavor::Gcc,
         vec![
+            // The illumos libc contains a stack unwinding implementation, as
+            // does libgcc_s.  The latter implementation includes several
+            // additional symbols that are not always in base libc.  To force
+            // the consistent use of just one unwinder, we ensure libc appears
+            // after libgcc_s in the NEEDED list for the resultant binary by
+            // ignoring any attempts to add it as a dynamic dependency until the
+            // very end.
+            // FIXME: This should be replaced by a more complete and generic
+            // mechanism for controlling the order of library arguments passed
+            // to the linker.
+            "-lc".to_string(),
             // LLVM will insert calls to the stack protector functions
             // "__stack_chk_fail" and "__stack_chk_guard" into code in native
             // object files.  Some platforms include these symbols directly in
index dfc97447ce042bb9b11a887725cc7f9b6e305eae..4bffd6e8ddd354a7457e28c5321185b6b96e2db0 100644 (file)
@@ -1897,14 +1897,16 @@ macro_rules! key {
         Ok(base)
     }
 
-    /// Search RUST_TARGET_PATH for a JSON file specifying the given target
-    /// triple. Note that it could also just be a bare filename already, so also
-    /// check for that. If one of the hardcoded targets we know about, just
-    /// return it directly.
+    /// Search for a JSON file specifying the given target triple.
     ///
-    /// The error string could come from any of the APIs called, including
-    /// filesystem access and JSON decoding.
-    pub fn search(target_triple: &TargetTriple) -> Result<Target, String> {
+    /// If none is found in `$RUST_TARGET_PATH`, look for a file called `target.json` inside the
+    /// sysroot under the target-triple's `rustlib` directory.  Note that it could also just be a
+    /// bare filename already, so also check for that. If one of the hardcoded targets we know
+    /// about, just return it directly.
+    ///
+    /// The error string could come from any of the APIs called, including filesystem access and
+    /// JSON decoding.
+    pub fn search(target_triple: &TargetTriple, sysroot: &PathBuf) -> Result<Target, String> {
         use rustc_serialize::json;
         use std::env;
         use std::fs;
@@ -1931,14 +1933,26 @@ fn load_file(path: &Path) -> Result<Target, String> {
 
                 let target_path = env::var_os("RUST_TARGET_PATH").unwrap_or_default();
 
-                // FIXME 16351: add a sane default search path?
-
                 for dir in env::split_paths(&target_path) {
                     let p = dir.join(&path);
                     if p.is_file() {
                         return load_file(&p);
                     }
                 }
+
+                // Additionally look in the sysroot under `lib/rustlib/<triple>/target.json`
+                // as a fallback.
+                let rustlib_path = crate::target_rustlib_path(&sysroot, &target_triple);
+                let p = std::array::IntoIter::new([
+                    Path::new(sysroot),
+                    Path::new(&rustlib_path),
+                    Path::new("target.json"),
+                ])
+                .collect::<PathBuf>();
+                if p.is_file() {
+                    return load_file(&p);
+                }
+
                 Err(format!("Could not find specification for target {:?}", target_triple))
             }
             TargetTriple::TargetPath(ref target_path) => {
index 28d9801b78cb581cf835339b45c39171c6666234..359cb0f6881de502f3ec49bcafc5ce013bd104ce 100644 (file)
@@ -11,7 +11,7 @@ pub fn target() -> Target {
     base.stack_probes = StackProbeType::Call;
 
     Target {
-        llvm_target: "x86_64-unknown-none-elf".to_string(),
+        llvm_target: "x86_64-unknown-hermit".to_string(),
         pointer_width: 64,
         data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
             .to_string(),
index e4aabbdb7ede7be92a145e5850c5e4967db34243..08d452900c8e18a69b5fdbdf2f33fd9c7526e7b6 100644 (file)
@@ -1044,8 +1044,6 @@ fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<
     }
 
     /// Returns `true` if the global caches can be used.
-    /// Do note that if the type itself is not in the
-    /// global tcx, the local caches will be used.
     fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool {
         // If there are any inference variables in the `ParamEnv`, then we
         // always use a cache local to this particular scope. Otherwise, we
@@ -1361,7 +1359,17 @@ fn candidate_should_be_dropped_in_favor_of(
             ) => false,
 
             (ParamCandidate(other), ParamCandidate(victim)) => {
-                if other.value == victim.value && victim.constness == Constness::NotConst {
+                let value_same_except_bound_vars = other.value.skip_binder()
+                    == victim.value.skip_binder()
+                    && !other.value.skip_binder().has_escaping_bound_vars();
+                if value_same_except_bound_vars {
+                    // See issue #84398. In short, we can generate multiple ParamCandidates which are
+                    // the same except for unused bound vars. Just pick the one with the fewest bound vars
+                    // or the current one if tied (they should both evaluate to the same answer). This is
+                    // probably best characterized as a "hack", since we might prefer to just do our
+                    // best to *not* create essentially duplicate candidates in the first place.
+                    other.value.bound_vars().len() <= victim.value.bound_vars().len()
+                } else if other.value == victim.value && victim.constness == Constness::NotConst {
                     // Drop otherwise equivalent non-const candidates in favor of const candidates.
                     true
                 } else {
index ca001635a3dc29d0f3f6546e3db9babeb19d56a9..d3eb9fd95571400724496faf317b625e1dfa127f 100644 (file)
@@ -25,11 +25,26 @@ pub enum Representability {
 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).
+    // contains a different, structurally recursive type, maintain a stack of
+    // seen types and check recursion for each of them (issues #3008, #3779,
+    // #74224, #84611). `shadow_seen` contains the full stack and `seen` only
+    // the one for the current type (e.g. if we have structs A and B, B contains
+    // a field of type A, and we're currently looking at B, then `seen` will be
+    // cleared when recursing to check A, but `shadow_seen` won't, so that we
+    // can catch cases of mutual recursion where A also contains B).
     let mut seen: Vec<Ty<'_>> = Vec::new();
+    let mut shadow_seen: Vec<&'tcx ty::AdtDef> = Vec::new();
     let mut representable_cache = FxHashMap::default();
-    let r = is_type_structurally_recursive(tcx, sp, &mut seen, &mut representable_cache, ty);
+    let mut force_result = false;
+    let r = is_type_structurally_recursive(
+        tcx,
+        sp,
+        &mut seen,
+        &mut shadow_seen,
+        &mut representable_cache,
+        ty,
+        &mut force_result,
+    );
     debug!("is_type_representable: {:?} is {:?}", ty, r);
     r
 }
@@ -48,21 +63,38 @@ fn are_inner_types_recursive<'tcx>(
     tcx: TyCtxt<'tcx>,
     sp: Span,
     seen: &mut Vec<Ty<'tcx>>,
+    shadow_seen: &mut Vec<&'tcx ty::AdtDef>,
     representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
     ty: Ty<'tcx>,
+    force_result: &mut bool,
 ) -> Representability {
+    debug!("are_inner_types_recursive({:?}, {:?}, {:?})", ty, seen, shadow_seen);
     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)
-                }),
-            )
+            fold_repr(ty.tuple_fields().map(|ty| {
+                is_type_structurally_recursive(
+                    tcx,
+                    sp,
+                    seen,
+                    shadow_seen,
+                    representable_cache,
+                    ty,
+                    force_result,
+                )
+            }))
         }
         // 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::Array(ty, _) => is_type_structurally_recursive(
+            tcx,
+            sp,
+            seen,
+            shadow_seen,
+            representable_cache,
+            ty,
+            force_result,
+        ),
         ty::Adt(def, substs) => {
             // Find non representable fields with their spans
             fold_repr(def.all_fields().map(|field| {
@@ -76,12 +108,128 @@ fn are_inner_types_recursive<'tcx>(
                     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])
+
+                let mut result = None;
+
+                // First, we check whether the field type per se is representable.
+                // This catches cases as in #74224 and #84611. There is a special
+                // case related to mutual recursion, though; consider this example:
+                //
+                //   struct A<T> {
+                //       z: T,
+                //       x: B<T>,
+                //   }
+                //
+                //   struct B<T> {
+                //       y: A<T>
+                //   }
+                //
+                // Here, without the following special case, both A and B are
+                // ContainsRecursive, which is a problem because we only report
+                // errors for SelfRecursive. We fix this by detecting this special
+                // case (shadow_seen.first() is the type we are originally
+                // interested in, and if we ever encounter the same AdtDef again,
+                // we know that it must be SelfRecursive) and "forcibly" returning
+                // SelfRecursive (by setting force_result, which tells the calling
+                // invocations of are_inner_types_representable to forward the
+                // result without adjusting).
+                if shadow_seen.len() > seen.len() && shadow_seen.first() == Some(def) {
+                    *force_result = true;
+                    result = Some(Representability::SelfRecursive(vec![span]));
+                }
+
+                if result == None {
+                    result = Some(Representability::Representable);
+
+                    // Now, we check whether the field types per se are representable, e.g.
+                    // for struct Foo { x: Option<Foo> }, we first check whether Option<_>
+                    // by itself is representable (which it is), and the nesting of Foo
+                    // will be detected later. This is necessary for #74224 and #84611.
+
+                    // If we have encountered an ADT definition that we have not seen
+                    // before (no need to check them twice), recurse to see whether that
+                    // definition is SelfRecursive. If so, we must be ContainsRecursive.
+                    if shadow_seen.len() > 1
+                        && !shadow_seen
+                            .iter()
+                            .take(shadow_seen.len() - 1)
+                            .any(|seen_def| seen_def == def)
+                    {
+                        let adt_def_id = def.did;
+                        let raw_adt_ty = tcx.type_of(adt_def_id);
+                        debug!("are_inner_types_recursive: checking nested type: {:?}", raw_adt_ty);
+
+                        // Check independently whether the ADT is SelfRecursive. If so,
+                        // we must be ContainsRecursive (except for the special case
+                        // mentioned above).
+                        let mut nested_seen: Vec<Ty<'_>> = vec![];
+                        result = Some(
+                            match is_type_structurally_recursive(
+                                tcx,
+                                span,
+                                &mut nested_seen,
+                                shadow_seen,
+                                representable_cache,
+                                raw_adt_ty,
+                                force_result,
+                            ) {
+                                Representability::SelfRecursive(_) => {
+                                    if *force_result {
+                                        Representability::SelfRecursive(vec![span])
+                                    } else {
+                                        Representability::ContainsRecursive
+                                    }
+                                }
+                                x => x,
+                            },
+                        );
+                    }
+
+                    // We only enter the following block if the type looks representable
+                    // so far. This is necessary for cases such as this one (#74224):
+                    //
+                    //   struct A<T> {
+                    //       x: T,
+                    //       y: A<A<T>>,
+                    //   }
+                    //
+                    //   struct B {
+                    //       z: A<usize>
+                    //   }
+                    //
+                    // When checking B, we recurse into A and check field y of type
+                    // A<A<usize>>. We haven't seen this exact type before, so we recurse
+                    // into A<A<usize>>, which contains, A<A<A<usize>>>, and so forth,
+                    // ad infinitum. We can prevent this from happening by first checking
+                    // A separately (the code above) and only checking for nested Bs if
+                    // A actually looks representable (which it wouldn't in this example).
+                    if result == Some(Representability::Representable) {
+                        // Now, even if the type is representable (e.g. Option<_>),
+                        // it might still contribute to a recursive type, e.g.:
+                        //   struct Foo { x: Option<Option<Foo>> }
+                        // These cases are handled by passing the full `seen`
+                        // stack to is_type_structurally_recursive (instead of the
+                        // empty `nested_seen` above):
+                        result = Some(
+                            match is_type_structurally_recursive(
+                                tcx,
+                                span,
+                                seen,
+                                shadow_seen,
+                                representable_cache,
+                                ty,
+                                force_result,
+                            ) {
+                                Representability::SelfRecursive(_) => {
+                                    Representability::SelfRecursive(vec![span])
+                                }
+                                x => x,
+                            },
+                        );
                     }
-                    x => x,
                 }
+
+                result.unwrap()
             }))
         }
         ty::Closure(..) => {
@@ -106,8 +254,10 @@ fn is_type_structurally_recursive<'tcx>(
     tcx: TyCtxt<'tcx>,
     sp: Span,
     seen: &mut Vec<Ty<'tcx>>,
+    shadow_seen: &mut Vec<&'tcx ty::AdtDef>,
     representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
     ty: Ty<'tcx>,
+    force_result: &mut bool,
 ) -> Representability {
     debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp);
     if let Some(representability) = representable_cache.get(ty) {
@@ -118,8 +268,15 @@ fn is_type_structurally_recursive<'tcx>(
         return representability.clone();
     }
 
-    let representability =
-        is_type_structurally_recursive_inner(tcx, sp, seen, representable_cache, ty);
+    let representability = is_type_structurally_recursive_inner(
+        tcx,
+        sp,
+        seen,
+        shadow_seen,
+        representable_cache,
+        ty,
+        force_result,
+    );
 
     representable_cache.insert(ty, representability.clone());
     representability
@@ -129,12 +286,16 @@ fn is_type_structurally_recursive_inner<'tcx>(
     tcx: TyCtxt<'tcx>,
     sp: Span,
     seen: &mut Vec<Ty<'tcx>>,
+    shadow_seen: &mut Vec<&'tcx ty::AdtDef>,
     representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
     ty: Ty<'tcx>,
+    force_result: &mut bool,
 ) -> Representability {
     match ty.kind() {
         ty::Adt(def, _) => {
             {
+                debug!("is_type_structurally_recursive_inner: adt: {:?}, seen: {:?}", ty, seen);
+
                 // Iterate through stack of previously seen types.
                 let mut iter = seen.iter();
 
@@ -158,8 +319,10 @@ fn is_type_structurally_recursive_inner<'tcx>(
                 // 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:
+                // here, because nesting e.g. Options is allowed (as long as the
+                // definition of Option doesn't itself include an Option field, which
+                // would be a case of SelfRecursive above). The following, too, counts
+                // as SelfRecursive:
                 //
                 // struct Foo { Option<Option<Foo>> }
 
@@ -174,13 +337,31 @@ fn is_type_structurally_recursive_inner<'tcx>(
             // 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);
+            shadow_seen.push(def);
+            let out = are_inner_types_recursive(
+                tcx,
+                sp,
+                seen,
+                shadow_seen,
+                representable_cache,
+                ty,
+                force_result,
+            );
+            shadow_seen.pop();
             seen.pop();
             out
         }
         _ => {
             // No need to push in other cases.
-            are_inner_types_recursive(tcx, sp, seen, representable_cache, ty)
+            are_inner_types_recursive(
+                tcx,
+                sp,
+                seen,
+                shadow_seen,
+                representable_cache,
+                ty,
+                force_result,
+            )
         }
     }
 }
index 0c6a33b91cea5c24850faba477c03382f3db9161..4d6caf07236e29593db7c30d89fafea47c1b40c6 100644 (file)
@@ -720,11 +720,11 @@ pub(in super::super) fn select_all_obligations_or_error(&self) {
     pub(in super::super) fn select_obligations_where_possible(
         &self,
         fallback_has_occurred: bool,
-        mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
+        mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
     ) {
         let result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
         if let Err(mut errors) = result {
-            mutate_fullfillment_errors(&mut errors);
+            mutate_fulfillment_errors(&mut errors);
             self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
         }
     }
index 80b5a9d4e621a60a1d67c1f4ed8ff9a190dc7abd..3417bc0197253221379302eea24eb442b3952485 100644 (file)
@@ -986,7 +986,7 @@ fn point_at_type_arg_instead_of_call_if_possible(
                             error.obligation.predicate.kind().skip_binder()
                         {
                             // If any of the type arguments in this path segment caused the
-                            // `FullfillmentError`, point at its span (#61860).
+                            // `FulfillmentError`, point at its span (#61860).
                             for arg in path
                                 .segments
                                 .iter()
index 4914f196afbb5af1ed34a0c7dd7e5213aa493e1a..7436edccf84bb3a84361f4f34604d9f02e9ad090 100644 (file)
@@ -43,7 +43,7 @@ struct CheckWfFcxBuilder<'tcx> {
 impl<'tcx> CheckWfFcxBuilder<'tcx> {
     fn with_fcx<F>(&mut self, f: F)
     where
-        F: for<'b> FnOnce(&FnCtxt<'b, 'tcx>, TyCtxt<'tcx>) -> Vec<Ty<'tcx>>,
+        F: for<'b> FnOnce(&FnCtxt<'b, 'tcx>) -> Vec<Ty<'tcx>>,
     {
         let id = self.id;
         let span = self.span;
@@ -56,7 +56,7 @@ fn with_fcx<F>(&mut self, f: F)
                 // empty `param_env`.
                 check_false_global_bounds(&fcx, span, id);
             }
-            let wf_tys = f(&fcx, fcx.tcx);
+            let wf_tys = f(&fcx);
             fcx.select_all_obligations_or_error();
             fcx.regionck_item(id, span, &wf_tys);
         });
@@ -388,7 +388,7 @@ fn check_associated_item(
     debug!("check_associated_item: {:?}", item_id);
 
     let code = ObligationCauseCode::MiscObligation;
-    for_id(tcx, item_id, span).with_fcx(|fcx, tcx| {
+    for_id(tcx, item_id, span).with_fcx(|fcx| {
         let item = fcx.tcx.associated_item(fcx.tcx.hir().local_def_id(item_id));
 
         let (mut implied_bounds, self_ty) = match item.container {
@@ -409,7 +409,6 @@ fn check_associated_item(
                 let sig = fcx.normalize_associated_types_in(span, sig);
                 let hir_sig = sig_if_method.expect("bad signature for method");
                 check_fn_or_method(
-                    tcx,
                     fcx,
                     item.ident.span,
                     sig,
@@ -467,9 +466,9 @@ fn check_type_defn<'tcx, F>(
 ) where
     F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>) -> Vec<AdtVariant<'tcx>>,
 {
-    for_item(tcx, item).with_fcx(|fcx, fcx_tcx| {
+    for_item(tcx, item).with_fcx(|fcx| {
         let variants = lookup_fields(fcx);
-        let packed = fcx.tcx.adt_def(item.def_id).repr.packed();
+        let packed = tcx.adt_def(item.def_id).repr.packed();
 
         for variant in &variants {
             // For DST, or when drop needs to copy things around, all
@@ -477,15 +476,14 @@ fn check_type_defn<'tcx, F>(
             let needs_drop_copy = || {
                 packed && {
                     let ty = variant.fields.last().unwrap().ty;
-                    let ty = fcx.tcx.erase_regions(ty);
+                    let ty = tcx.erase_regions(ty);
                     if ty.needs_infer() {
-                        fcx_tcx
-                            .sess
+                        tcx.sess
                             .delay_span_bug(item.span, &format!("inference variables in {:?}", ty));
                         // Just treat unresolved type expression as if it needs drop.
                         true
                     } else {
-                        ty.needs_drop(fcx_tcx, fcx_tcx.param_env(item.def_id))
+                        ty.needs_drop(tcx, tcx.param_env(item.def_id))
                     }
                 }
             };
@@ -497,7 +495,7 @@ fn check_type_defn<'tcx, F>(
                 let last = idx == variant.fields.len() - 1;
                 fcx.register_bound(
                     field.ty,
-                    fcx.tcx.require_lang_item(LangItem::Sized, None),
+                    tcx.require_lang_item(LangItem::Sized, None),
                     traits::ObligationCause::new(
                         field.span,
                         fcx.body_id,
@@ -524,11 +522,10 @@ fn check_type_defn<'tcx, F>(
 
             // Explicit `enum` discriminant values must const-evaluate successfully.
             if let Some(discr_def_id) = variant.explicit_discr {
-                let discr_substs =
-                    InternalSubsts::identity_for_item(fcx.tcx, discr_def_id.to_def_id());
+                let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id());
 
                 let cause = traits::ObligationCause::new(
-                    fcx.tcx.def_span(discr_def_id),
+                    tcx.def_span(discr_def_id),
                     fcx.body_id,
                     traits::MiscObligation,
                 );
@@ -539,12 +536,12 @@ fn check_type_defn<'tcx, F>(
                         ty::WithOptConstParam::unknown(discr_def_id.to_def_id()),
                         discr_substs,
                     )
-                    .to_predicate(fcx.tcx),
+                    .to_predicate(tcx),
                 ));
             }
         }
 
-        check_where_clauses(tcx, fcx, item.span, item.def_id.to_def_id(), None);
+        check_where_clauses(fcx, item.span, item.def_id.to_def_id(), None);
 
         // No implied bounds in a struct definition.
         vec![]
@@ -569,8 +566,9 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
         }
     }
 
-    for_item(tcx, item).with_fcx(|fcx, _| {
-        check_where_clauses(tcx, fcx, item.span, item.def_id.to_def_id(), None);
+    // FIXME: this shouldn't use an `FnCtxt` at all.
+    for_item(tcx, item).with_fcx(|fcx| {
+        check_where_clauses(fcx, item.span, item.def_id.to_def_id(), None);
 
         vec![]
     });
@@ -610,20 +608,12 @@ fn check_item_fn(
     span: Span,
     decl: &hir::FnDecl<'_>,
 ) {
-    for_id(tcx, item_id, span).with_fcx(|fcx, tcx| {
-        let def_id = fcx.tcx.hir().local_def_id(item_id);
-        let sig = fcx.tcx.fn_sig(def_id);
+    for_id(tcx, item_id, span).with_fcx(|fcx| {
+        let def_id = tcx.hir().local_def_id(item_id);
+        let sig = tcx.fn_sig(def_id);
         let sig = fcx.normalize_associated_types_in(span, sig);
         let mut implied_bounds = vec![];
-        check_fn_or_method(
-            tcx,
-            fcx,
-            ident.span,
-            sig,
-            decl,
-            def_id.to_def_id(),
-            &mut implied_bounds,
-        );
+        check_fn_or_method(fcx, ident.span, sig, decl, def_id.to_def_id(), &mut implied_bounds);
         implied_bounds
     })
 }
@@ -631,7 +621,7 @@ fn check_item_fn(
 fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_foreign_ty: bool) {
     debug!("check_item_type: {:?}", item_id);
 
-    for_id(tcx, item_id, ty_span).with_fcx(|fcx, tcx| {
+    for_id(tcx, item_id, ty_span).with_fcx(|fcx| {
         let ty = tcx.type_of(tcx.hir().local_def_id(item_id));
         let item_ty = fcx.normalize_associated_types_in(ty_span, ty);
 
@@ -647,7 +637,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo
         if forbid_unsized {
             fcx.register_bound(
                 item_ty,
-                fcx.tcx.require_lang_item(LangItem::Sized, None),
+                tcx.require_lang_item(LangItem::Sized, None),
                 traits::ObligationCause::new(ty_span, fcx.body_id, traits::MiscObligation),
             );
         }
@@ -665,13 +655,13 @@ fn check_impl<'tcx>(
 ) {
     debug!("check_impl: {:?}", item);
 
-    for_item(tcx, item).with_fcx(|fcx, tcx| {
+    for_item(tcx, item).with_fcx(|fcx| {
         match *ast_trait_ref {
             Some(ref ast_trait_ref) => {
                 // `#[rustc_reservation_impl]` impls are not real impls and
                 // therefore don't need to be WF (the trait's `Self: Trait` predicate
                 // won't hold).
-                let trait_ref = fcx.tcx.impl_trait_ref(item.def_id).unwrap();
+                let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap();
                 let trait_ref =
                     fcx.normalize_associated_types_in(ast_trait_ref.path.span, trait_ref);
                 let obligations = traits::wf::trait_obligations(
@@ -687,7 +677,7 @@ fn check_impl<'tcx>(
                 }
             }
             None => {
-                let self_ty = fcx.tcx.type_of(item.def_id);
+                let self_ty = tcx.type_of(item.def_id);
                 let self_ty = fcx.normalize_associated_types_in(item.span, self_ty);
                 fcx.register_wf_obligation(
                     self_ty.into(),
@@ -697,7 +687,7 @@ fn check_impl<'tcx>(
             }
         }
 
-        check_where_clauses(tcx, fcx, item.span, item.def_id.to_def_id(), None);
+        check_where_clauses(fcx, item.span, item.def_id.to_def_id(), None);
 
         fcx.impl_implied_bounds(item.def_id.to_def_id(), item.span)
     });
@@ -705,15 +695,15 @@ fn check_impl<'tcx>(
 
 /// Checks where-clauses and inline bounds that are declared on `def_id`.
 fn check_where_clauses<'tcx, 'fcx>(
-    tcx: TyCtxt<'tcx>,
     fcx: &FnCtxt<'fcx, 'tcx>,
     span: Span,
     def_id: DefId,
     return_ty: Option<(Ty<'tcx>, Span)>,
 ) {
     debug!("check_where_clauses(def_id={:?}, return_ty={:?})", def_id, return_ty);
+    let tcx = fcx.tcx;
 
-    let predicates = fcx.tcx.predicates_of(def_id);
+    let predicates = tcx.predicates_of(def_id);
     let generics = tcx.generics_of(def_id);
 
     let is_our_default = |def: &ty::GenericParamDef| match def.kind {
@@ -734,14 +724,14 @@ fn check_where_clauses<'tcx, 'fcx>(
         match param.kind {
             GenericParamDefKind::Type { .. } => {
                 if is_our_default(&param) {
-                    let ty = fcx.tcx.type_of(param.def_id);
+                    let ty = 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),
+                            tcx.def_span(param.def_id),
                             ObligationCauseCode::MiscObligation,
                         );
                     }
@@ -754,7 +744,7 @@ fn check_where_clauses<'tcx, 'fcx>(
                     let default_ct = tcx.const_param_default(param.def_id);
                     fcx.register_wf_obligation(
                         default_ct.into(),
-                        fcx.tcx.def_span(param.def_id),
+                        tcx.def_span(param.def_id),
                         ObligationCauseCode::MiscObligation,
                     );
                 }
@@ -772,17 +762,17 @@ fn check_where_clauses<'tcx, 'fcx>(
     // For more examples see tests `defaults-well-formedness.rs` and `type-check-defaults.rs`.
     //
     // First we build the defaulted substitution.
-    let substs = InternalSubsts::for_item(fcx.tcx, def_id, |param, _| {
+    let substs = InternalSubsts::for_item(tcx, def_id, |param, _| {
         match param.kind {
             GenericParamDefKind::Lifetime => {
                 // All regions are identity.
-                fcx.tcx.mk_param_from_def(param)
+                tcx.mk_param_from_def(param)
             }
 
             GenericParamDefKind::Type { .. } => {
                 // If the param has a default, ...
                 if is_our_default(param) {
-                    let default_ty = fcx.tcx.type_of(param.def_id);
+                    let default_ty = tcx.type_of(param.def_id);
                     // ... and it's not a dependent default, ...
                     if !default_ty.needs_subst() {
                         // ... then substitute it with the default.
@@ -790,7 +780,7 @@ fn check_where_clauses<'tcx, 'fcx>(
                     }
                 }
 
-                fcx.tcx.mk_param_from_def(param)
+                tcx.mk_param_from_def(param)
             }
             GenericParamDefKind::Const { .. } => {
                 // FIXME(const_generics_defaults): I(@lcnr) feel like always
@@ -811,7 +801,7 @@ fn check_where_clauses<'tcx, 'fcx>(
                     }
                 }
 
-                fcx.tcx.mk_param_from_def(param)
+                tcx.mk_param_from_def(param)
             }
         }
     });
@@ -848,7 +838,7 @@ fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy
             }
             let mut param_count = CountParams::default();
             let has_region = pred.visit_with(&mut param_count).is_break();
-            let substituted_pred = pred.subst(fcx.tcx, substs);
+            let substituted_pred = pred.subst(tcx, substs);
             // Don't check non-defaulted params, dependent defaults (including lifetimes)
             // or preds with multiple params.
             if substituted_pred.has_param_types_or_consts()
@@ -879,14 +869,14 @@ fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy
             traits::Obligation::new(cause, fcx.param_env, pred)
         });
 
-    let predicates = predicates.instantiate_identity(fcx.tcx);
+    let predicates = predicates.instantiate_identity(tcx);
 
     if let Some((mut return_ty, span)) = return_ty {
         if return_ty.has_infer_types_or_consts() {
             fcx.select_obligations_where_possible(false, |_| {});
             return_ty = fcx.resolve_vars_if_possible(return_ty);
         }
-        check_opaque_types(tcx, fcx, def_id.expect_local(), span, return_ty);
+        check_opaque_types(fcx, def_id.expect_local(), span, return_ty);
     }
 
     let predicates = fcx.normalize_associated_types_in(span, predicates);
@@ -905,7 +895,6 @@ fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy
 }
 
 fn check_fn_or_method<'fcx, 'tcx>(
-    tcx: TyCtxt<'tcx>,
     fcx: &FnCtxt<'fcx, 'tcx>,
     span: Span,
     sig: ty::PolyFnSig<'tcx>,
@@ -930,7 +919,7 @@ fn check_fn_or_method<'fcx, 'tcx>(
     // FIXME(#25759) return types should not be implied bounds
     implied_bounds.push(sig.output());
 
-    check_where_clauses(tcx, fcx, span, def_id, Some((sig.output(), hir_decl.output.span())));
+    check_where_clauses(fcx, span, def_id, Some((sig.output(), hir_decl.output.span())));
 }
 
 /// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions
@@ -953,15 +942,16 @@ fn check_fn_or_method<'fcx, 'tcx>(
 /// ```
 ///
 fn check_opaque_types<'fcx, 'tcx>(
-    tcx: TyCtxt<'tcx>,
     fcx: &FnCtxt<'fcx, 'tcx>,
     fn_def_id: LocalDefId,
     span: Span,
     ty: Ty<'tcx>,
 ) {
-    trace!("check_opaque_types(ty={:?})", ty);
+    trace!("check_opaque_types(fn_def_id={:?}, ty={:?})", fn_def_id, ty);
+    let tcx = fcx.tcx;
+
     ty.fold_with(&mut ty::fold::BottomUpFolder {
-        tcx: fcx.tcx,
+        tcx,
         ty_op: |ty| {
             if let ty::Opaque(def_id, substs) = *ty.kind() {
                 trace!("check_opaque_types: opaque_ty, {:?}, {:?}", def_id, substs);
index 190c9d35934f93219450ec50d3ded0a41850f282..0528f8812f920bac21aa6d70b04badd0294c459f 100644 (file)
@@ -2661,8 +2661,6 @@ 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;
@@ -2726,15 +2724,8 @@ 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);
+            codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
         } 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) {
@@ -2945,23 +2936,6 @@ 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 6e5584797b559da998633f0cf8585fc2b0c268c3..16952a5ced83e5d2c508770e3c9e45a837c096d2 100644 (file)
@@ -372,8 +372,6 @@ changelog-seen = 2
 # This is mostly useful for tools; if you have changes to `compiler/` they will be ignored.
 #
 # You can set this to "if-unchanged" to only download if `compiler/` has not been modified.
-#
-# FIXME(#82739): currently, this also uses the downloaded compiler for stage0, but that causes unnecessary rebuilds.
 #download-rustc = false
 
 # Number of codegen units to use for each compiler invocation. A value of 0
index 971244718b4adb30bba476fadef14e9d1a6be2b9..0a46387c34e9c09fae28bd7628f8825605b93cbf 100644 (file)
@@ -398,12 +398,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 /// See its documentation for more.
 ///
 /// [`into_keys`]: BTreeMap::into_keys
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 pub struct IntoKeys<K, V> {
     inner: IntoIter<K, V>,
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K: fmt::Debug, V> fmt::Debug for IntoKeys<K, V> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_list().entries(self.inner.iter().map(|(key, _)| key)).finish()
@@ -416,12 +416,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 /// See its documentation for more.
 ///
 /// [`into_values`]: BTreeMap::into_values
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 pub struct IntoValues<K, V> {
     inner: IntoIter<K, V>,
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V: fmt::Debug> fmt::Debug for IntoValues<K, V> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_list().entries(self.inner.iter().map(|(_, val)| val)).finish()
@@ -1242,7 +1242,6 @@ pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V>
     /// # Examples
     ///
     /// ```
-    /// #![feature(map_into_keys_values)]
     /// use std::collections::BTreeMap;
     ///
     /// let mut a = BTreeMap::new();
@@ -1253,7 +1252,7 @@ pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V>
     /// assert_eq!(keys, [1, 2]);
     /// ```
     #[inline]
-    #[unstable(feature = "map_into_keys_values", issue = "75294")]
+    #[stable(feature = "map_into_keys_values", since = "1.54.0")]
     pub fn into_keys(self) -> IntoKeys<K, V> {
         IntoKeys { inner: self.into_iter() }
     }
@@ -1265,7 +1264,6 @@ pub fn into_keys(self) -> IntoKeys<K, V> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(map_into_keys_values)]
     /// use std::collections::BTreeMap;
     ///
     /// let mut a = BTreeMap::new();
@@ -1276,7 +1274,7 @@ pub fn into_keys(self) -> IntoKeys<K, V> {
     /// assert_eq!(values, ["hello", "goodbye"]);
     /// ```
     #[inline]
-    #[unstable(feature = "map_into_keys_values", issue = "75294")]
+    #[stable(feature = "map_into_keys_values", since = "1.54.0")]
     pub fn into_values(self) -> IntoValues<K, V> {
         IntoValues { inner: self.into_iter() }
     }
@@ -1776,7 +1774,7 @@ unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) {
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> Iterator for IntoKeys<K, V> {
     type Item = K;
 
@@ -1801,24 +1799,24 @@ fn max(mut self) -> Option<K> {
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> DoubleEndedIterator for IntoKeys<K, V> {
     fn next_back(&mut self) -> Option<K> {
         self.inner.next_back().map(|(k, _)| k)
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> ExactSizeIterator for IntoKeys<K, V> {
     fn len(&self) -> usize {
         self.inner.len()
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> FusedIterator for IntoKeys<K, V> {}
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> Iterator for IntoValues<K, V> {
     type Item = V;
 
@@ -1835,21 +1833,21 @@ fn last(mut self) -> Option<V> {
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> DoubleEndedIterator for IntoValues<K, V> {
     fn next_back(&mut self) -> Option<V> {
         self.inner.next_back().map(|(_, v)| v)
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> ExactSizeIterator for IntoValues<K, V> {
     fn len(&self) -> usize {
         self.inner.len()
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> FusedIterator for IntoValues<K, V> {}
 
 #[stable(feature = "btree_range", since = "1.17.0")]
index 964169a227f6497d8b94c40e191e64ca4a3a2d02..800952f7a5eced39724f32dd3fc2f7675c8e6a61 100644 (file)
@@ -1733,6 +1733,19 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 #[stable(feature = "from_for_ptrs", since = "1.6.0")]
 impl<T> From<T> for Rc<T> {
+    /// Converts a generic type `T` into a `Rc<T>`
+    ///
+    /// The conversion allocates on the heap and moves `t`
+    /// from the stack into it.
+    ///
+    /// # Example
+    /// ```rust
+    /// # use std::rc::Rc;
+    /// let x = 5;
+    /// let rc = Rc::new(5);
+    ///
+    /// assert_eq!(Rc::from(x), rc);
+    /// ```
     fn from(t: T) -> Self {
         Rc::new(t)
     }
index 0a3e5789e8bedc4dc7def36dfe27763d5d3d6ce2..f8b16b6f9275c6fbf613cd761342123b374f29c6 100644 (file)
@@ -274,8 +274,7 @@ 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)]
+    #[cfg_attr(not(bootstrap), no_coverage)] // rust-lang/rust#84605
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn assert_receiver_is_total_eq(&self) {}
@@ -284,7 +283,7 @@ fn assert_receiver_is_total_eq(&self) {}
 /// Derive macro generating an impl of the trait `Eq`.
 #[rustc_builtin_macro]
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
-#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)]
+#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match, no_coverage)]
 pub macro Eq($item:item) {
     /* compiler built-in */
 }
index d7dd7ee02c1f43fbd633a037f49216cc4ff62bf1..0034de9ad1bfa79712870e1f2201084df0367e29 100644 (file)
@@ -723,7 +723,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
     /// macro, which panics when it is executed, it is *undefined behavior* to
     /// reach code marked with this function.
     ///
-    /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`](crate::hint::unreachable_unchecked).
+    /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`].
     #[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")]
     pub fn unreachable() -> !;
 
@@ -768,13 +768,13 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
     /// More specifically, this is the offset in bytes between successive
     /// items of the same type, including alignment padding.
     ///
-    /// The stabilized version of this intrinsic is [`core::mem::size_of`](crate::mem::size_of).
+    /// The stabilized version of this intrinsic is [`core::mem::size_of`].
     #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")]
     pub fn size_of<T>() -> usize;
 
     /// The minimum alignment of a type.
     ///
-    /// The stabilized version of this intrinsic is [`core::mem::align_of`](crate::mem::align_of).
+    /// The stabilized version of this intrinsic is [`core::mem::align_of`].
     #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")]
     pub fn min_align_of<T>() -> usize;
     /// The preferred alignment of a type.
@@ -790,13 +790,13 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
     pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
     /// The required alignment of the referenced value.
     ///
-    /// The stabilized version of this intrinsic is [`core::mem::align_of_val`](crate::mem::align_of_val).
+    /// The stabilized version of this intrinsic is [`core::mem::align_of_val`].
     #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")]
     pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
 
     /// Gets a static string slice containing the name of a type.
     ///
-    /// The stabilized version of this intrinsic is [`core::any::type_name`](crate::any::type_name).
+    /// The stabilized version of this intrinsic is [`core::any::type_name`].
     #[rustc_const_unstable(feature = "const_type_name", issue = "63084")]
     pub fn type_name<T: ?Sized>() -> &'static str;
 
@@ -804,7 +804,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
     /// function will return the same value for a type regardless of whichever
     /// crate it is invoked in.
     ///
-    /// The stabilized version of this intrinsic is [`core::any::TypeId::of`](crate::any::TypeId::of).
+    /// The stabilized version of this intrinsic is [`core::any::TypeId::of`].
     #[rustc_const_unstable(feature = "const_type_id", issue = "77125")]
     pub fn type_id<T: ?Sized + 'static>() -> u64;
 
@@ -829,7 +829,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
 
     /// Gets a reference to a static `Location` indicating where it was called.
     ///
-    /// Consider using [`core::panic::Location::caller`](crate::panic::Location::caller) instead.
+    /// Consider using [`core::panic::Location::caller`] instead.
     #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")]
     pub fn caller_location() -> &'static crate::panic::Location<'static>;
 
@@ -1158,11 +1158,11 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
 
     /// Performs a volatile load from the `src` pointer.
     ///
-    /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`](crate::ptr::read_volatile).
+    /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`].
     pub fn volatile_load<T>(src: *const T) -> T;
     /// Performs a volatile store to the `dst` pointer.
     ///
-    /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`](crate::ptr::write_volatile).
+    /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`].
     pub fn volatile_store<T>(dst: *mut T, val: T);
 
     /// Performs a volatile load from the `src` pointer
@@ -1703,7 +1703,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
     /// Returns the value of the discriminant for the variant in 'v';
     /// if `T` has no discriminant, returns `0`.
     ///
-    /// The stabilized version of this intrinsic is [`core::mem::discriminant`](crate::mem::discriminant).
+    /// The stabilized version of this intrinsic is [`core::mem::discriminant`].
     #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
     pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
 
index 4b293c596e7af6e702bacff670c27a5475e8c097..5e39e71252f5a1833a5f3e95a08489f560c6377b 100644 (file)
@@ -106,7 +106,7 @@ fn forward(start: Self, count: usize) -> Self {
     /// For any `a` and `n`, where no overflow occurs:
     ///
     /// * `Step::forward_unchecked(a, n)` is equivalent to `Step::forward(a, n)`
-    #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")]
+    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
     unsafe fn forward_unchecked(start: Self, count: usize) -> Self {
         Step::forward(start, count)
     }
@@ -178,7 +178,7 @@ fn backward(start: Self, count: usize) -> Self {
     /// For any `a` and `n`, where no overflow occurs:
     ///
     /// * `Step::backward_unchecked(a, n)` is equivalent to `Step::backward(a, n)`
-    #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")]
+    #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")]
     unsafe fn backward_unchecked(start: Self, count: usize) -> Self {
         Step::backward(start, count)
     }
index 0e2c140c367a92ce6a89879fb5ed26fa2d7bd52c..6a4f2d5a544c1607630b4f8bcdd960fd084df1de 100644 (file)
@@ -77,6 +77,7 @@
 #![feature(const_float_classify)]
 #![feature(const_float_bits_conv)]
 #![feature(const_int_unchecked_arith)]
+#![feature(const_inherent_unchecked_arith)]
 #![feature(const_mut_refs)]
 #![feature(const_refs_to_cell)]
 #![feature(const_panic)]
 #![feature(const_caller_location)]
 #![feature(slice_ptr_get)]
 #![feature(no_niche)] // rust-lang/rust#68303
+#![cfg_attr(not(bootstrap), feature(no_coverage))] // rust-lang/rust#84605
 #![feature(int_error_matching)]
 #![deny(unsafe_op_in_unsafe_fn)]
 
+// allow using `core::` in intra-doc links
+#[allow(unused_extern_crates)]
+extern crate self as core;
+
 #[prelude_import]
 #[allow(unused)]
 use prelude::v1::*;
index f77acdd618072484dc0816e25c4c09350ebcb1e4..10219201a40d33131b0c6831958cfbb8fd5f4e75 100644 (file)
@@ -870,7 +870,7 @@ pub unsafe fn assume_init_drop(&mut self) {
         // SAFETY:
         // * The caller guarantees that all elements of the array are initialized
         // * `MaybeUninit<T>` and T are guaranteed to have the same layout
-        // * MaybeUnint does not drop, so there are no double-frees
+        // * `MaybeUninit` does not drop, so there are no double-frees
         // And thus the conversion is safe
         unsafe {
             intrinsics::assert_inhabited::<[T; N]>();
index 4b341132e31eca21058cef4eaef116fb647e9fec..47b2b30563c3a10c2332429be63da74ec6aad550 100644 (file)
@@ -412,12 +412,13 @@ pub const fn checked_add(self, rhs: Self) -> Option<Self> {
         #[unstable(
             feature = "unchecked_math",
             reason = "niche optimization path",
-            issue = "none",
+            issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
+        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
         #[inline(always)]
-        pub unsafe fn unchecked_add(self, rhs: Self) -> Self {
+        pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_add`.
             unsafe { intrinsics::unchecked_add(self, rhs) }
@@ -450,12 +451,13 @@ pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
         #[unstable(
             feature = "unchecked_math",
             reason = "niche optimization path",
-            issue = "none",
+            issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
+        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
         #[inline(always)]
-        pub unsafe fn unchecked_sub(self, rhs: Self) -> Self {
+        pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_sub`.
             unsafe { intrinsics::unchecked_sub(self, rhs) }
@@ -488,12 +490,13 @@ pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
         #[unstable(
             feature = "unchecked_math",
             reason = "niche optimization path",
-            issue = "none",
+            issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
+        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
         #[inline(always)]
-        pub unsafe fn unchecked_mul(self, rhs: Self) -> Self {
+        pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_mul`.
             unsafe { intrinsics::unchecked_mul(self, rhs) }
index 08d9161eff112851bccfbc2e281d4b8e13677fbf..f9fd28b6a8c2487ab9c4e2f999c5c511644f5baa 100644 (file)
@@ -422,12 +422,13 @@ pub const fn checked_add(self, rhs: Self) -> Option<Self> {
         #[unstable(
             feature = "unchecked_math",
             reason = "niche optimization path",
-            issue = "none",
+            issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
+        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
         #[inline(always)]
-        pub unsafe fn unchecked_add(self, rhs: Self) -> Self {
+        pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_add`.
             unsafe { intrinsics::unchecked_add(self, rhs) }
@@ -460,12 +461,13 @@ pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
         #[unstable(
             feature = "unchecked_math",
             reason = "niche optimization path",
-            issue = "none",
+            issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
+        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
         #[inline(always)]
-        pub unsafe fn unchecked_sub(self, rhs: Self) -> Self {
+        pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_sub`.
             unsafe { intrinsics::unchecked_sub(self, rhs) }
@@ -498,12 +500,13 @@ pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
         #[unstable(
             feature = "unchecked_math",
             reason = "niche optimization path",
-            issue = "none",
+            issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
+        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
         #[inline(always)]
-        pub unsafe fn unchecked_mul(self, rhs: Self) -> Self {
+        pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
             // SAFETY: the caller must uphold the safety contract for
             // `unchecked_mul`.
             unsafe { intrinsics::unchecked_mul(self, rhs) }
index bdf559847cc8511761d675be1de794e0e947ebe0..a1f52a9c2e88019534c64fe6355dfb791d1ad9c0 100644 (file)
@@ -962,7 +962,6 @@ pub fn retain<F>(&mut self, f: F)
     /// # Examples
     ///
     /// ```
-    /// #![feature(map_into_keys_values)]
     /// use std::collections::HashMap;
     ///
     /// let mut map = HashMap::new();
@@ -973,7 +972,7 @@ pub fn retain<F>(&mut self, f: F)
     /// let vec: Vec<&str> = map.into_keys().collect();
     /// ```
     #[inline]
-    #[unstable(feature = "map_into_keys_values", issue = "75294")]
+    #[stable(feature = "map_into_keys_values", since = "1.54.0")]
     pub fn into_keys(self) -> IntoKeys<K, V> {
         IntoKeys { inner: self.into_iter() }
     }
@@ -985,7 +984,6 @@ pub fn into_keys(self) -> IntoKeys<K, V> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(map_into_keys_values)]
     /// use std::collections::HashMap;
     ///
     /// let mut map = HashMap::new();
@@ -996,7 +994,7 @@ pub fn into_keys(self) -> IntoKeys<K, V> {
     /// let vec: Vec<i32> = map.into_values().collect();
     /// ```
     #[inline]
-    #[unstable(feature = "map_into_keys_values", issue = "75294")]
+    #[stable(feature = "map_into_keys_values", since = "1.54.0")]
     pub fn into_values(self) -> IntoValues<K, V> {
         IntoValues { inner: self.into_iter() }
     }
@@ -1405,15 +1403,13 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> {
 /// # Example
 ///
 /// ```
-/// #![feature(map_into_keys_values)]
-///
 /// use std::collections::HashMap;
 ///
 /// let mut map = HashMap::new();
 /// map.insert("a", 1);
 /// let iter_keys = map.into_keys();
 /// ```
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 pub struct IntoKeys<K, V> {
     inner: IntoIter<K, V>,
 }
@@ -1428,15 +1424,13 @@ pub struct IntoKeys<K, V> {
 /// # Example
 ///
 /// ```
-/// #![feature(map_into_keys_values)]
-///
 /// use std::collections::HashMap;
 ///
 /// let mut map = HashMap::new();
 /// map.insert("a", 1);
 /// let iter_keys = map.into_values();
 /// ```
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 pub struct IntoValues<K, V> {
     inner: IntoIter<K, V>,
 }
@@ -2137,7 +2131,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> Iterator for IntoKeys<K, V> {
     type Item = K;
 
@@ -2150,24 +2144,24 @@ fn size_hint(&self) -> (usize, Option<usize>) {
         self.inner.size_hint()
     }
 }
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> ExactSizeIterator for IntoKeys<K, V> {
     #[inline]
     fn len(&self) -> usize {
         self.inner.len()
     }
 }
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> FusedIterator for IntoKeys<K, V> {}
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K: Debug, V> fmt::Debug for IntoKeys<K, V> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish()
     }
 }
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> Iterator for IntoValues<K, V> {
     type Item = V;
 
@@ -2180,17 +2174,17 @@ fn size_hint(&self) -> (usize, Option<usize>) {
         self.inner.size_hint()
     }
 }
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> ExactSizeIterator for IntoValues<K, V> {
     #[inline]
     fn len(&self) -> usize {
         self.inner.len()
     }
 }
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V> FusedIterator for IntoValues<K, V> {}
 
-#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[stable(feature = "map_into_keys_values", since = "1.54.0")]
 impl<K, V: Debug> fmt::Debug for IntoValues<K, V> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish()
index 80f98bbbad3665c00fbd2828e545a8d92f2d7ddd..ef2769d431fbbbd9bba5246059d3d3918cc7303d 100644 (file)
@@ -4,6 +4,7 @@
     self, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE,
 };
 use crate::mem;
+use crate::ptr;
 
 /// Wraps a writer and buffers its output.
 ///
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct BufWriter<W: Write> {
     inner: Option<W>,
+    // The buffer. Avoid using this like a normal `Vec` in common code paths.
+    // That is, don't use `buf.push`, `buf.extend_from_slice`, or any other
+    // methods that require bounds checking or the like. This makes an enormous
+    // difference to performance (we may want to stop using a `Vec` entirely).
     buf: Vec<u8>,
     // #30888: If the inner writer panics in a call to write, we don't want to
     // write the buffered data a second time in BufWriter's destructor. This
@@ -181,9 +186,14 @@ fn drop(&mut self) {
     /// data. Writes as much as possible without exceeding capacity. Returns
     /// the number of bytes written.
     pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize {
-        let available = self.buf.capacity() - self.buf.len();
+        let available = self.spare_capacity();
         let amt_to_buffer = available.min(buf.len());
-        self.buf.extend_from_slice(&buf[..amt_to_buffer]);
+
+        // SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction.
+        unsafe {
+            self.write_to_buffer_unchecked(&buf[..amt_to_buffer]);
+        }
+
         amt_to_buffer
     }
 
@@ -331,6 +341,103 @@ pub fn into_raw_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) {
         let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
         (self.inner.take().unwrap(), buf)
     }
+
+    // Ensure this function does not get inlined into `write`, so that it
+    // remains inlineable and its common path remains as short as possible.
+    // If this function ends up being called frequently relative to `write`,
+    // it's likely a sign that the client is using an improperly sized buffer
+    // or their write patterns are somewhat pathological.
+    #[cold]
+    #[inline(never)]
+    fn write_cold(&mut self, buf: &[u8]) -> io::Result<usize> {
+        if buf.len() > self.spare_capacity() {
+            self.flush_buf()?;
+        }
+
+        // Why not len > capacity? To avoid a needless trip through the buffer when the input
+        // exactly fills it. We'd just need to flush it to the underlying writer anyway.
+        if buf.len() >= self.buf.capacity() {
+            self.panicked = true;
+            let r = self.get_mut().write(buf);
+            self.panicked = false;
+            r
+        } else {
+            // Write to the buffer. In this case, we write to the buffer even if it fills it
+            // exactly. Doing otherwise would mean flushing the buffer, then writing this
+            // input to the inner writer, which in many cases would be a worse strategy.
+
+            // SAFETY: There was either enough spare capacity already, or there wasn't and we
+            // flushed the buffer to ensure that there is. In the latter case, we know that there
+            // is because flushing ensured that our entire buffer is spare capacity, and we entered
+            // this block because the input buffer length is less than that capacity. In either
+            // case, it's safe to write the input buffer to our buffer.
+            unsafe {
+                self.write_to_buffer_unchecked(buf);
+            }
+
+            Ok(buf.len())
+        }
+    }
+
+    // Ensure this function does not get inlined into `write_all`, so that it
+    // remains inlineable and its common path remains as short as possible.
+    // If this function ends up being called frequently relative to `write_all`,
+    // it's likely a sign that the client is using an improperly sized buffer
+    // or their write patterns are somewhat pathological.
+    #[cold]
+    #[inline(never)]
+    fn write_all_cold(&mut self, buf: &[u8]) -> io::Result<()> {
+        // Normally, `write_all` just calls `write` in a loop. We can do better
+        // by calling `self.get_mut().write_all()` directly, which avoids
+        // round trips through the buffer in the event of a series of partial
+        // writes in some circumstances.
+
+        if buf.len() > self.spare_capacity() {
+            self.flush_buf()?;
+        }
+
+        // Why not len > capacity? To avoid a needless trip through the buffer when the input
+        // exactly fills it. We'd just need to flush it to the underlying writer anyway.
+        if buf.len() >= self.buf.capacity() {
+            self.panicked = true;
+            let r = self.get_mut().write_all(buf);
+            self.panicked = false;
+            r
+        } else {
+            // Write to the buffer. In this case, we write to the buffer even if it fills it
+            // exactly. Doing otherwise would mean flushing the buffer, then writing this
+            // input to the inner writer, which in many cases would be a worse strategy.
+
+            // SAFETY: There was either enough spare capacity already, or there wasn't and we
+            // flushed the buffer to ensure that there is. In the latter case, we know that there
+            // is because flushing ensured that our entire buffer is spare capacity, and we entered
+            // this block because the input buffer length is less than that capacity. In either
+            // case, it's safe to write the input buffer to our buffer.
+            unsafe {
+                self.write_to_buffer_unchecked(buf);
+            }
+
+            Ok(())
+        }
+    }
+
+    // SAFETY: Requires `buf.len() <= self.buf.capacity() - self.buf.len()`,
+    // i.e., that input buffer length is less than or equal to spare capacity.
+    #[inline]
+    unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) {
+        debug_assert!(buf.len() <= self.spare_capacity());
+        let old_len = self.buf.len();
+        let buf_len = buf.len();
+        let src = buf.as_ptr();
+        let dst = self.buf.as_mut_ptr().add(old_len);
+        ptr::copy_nonoverlapping(src, dst, buf_len);
+        self.buf.set_len(old_len + buf_len);
+    }
+
+    #[inline]
+    fn spare_capacity(&self) -> usize {
+        self.buf.capacity() - self.buf.len()
+    }
 }
 
 #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")]
@@ -402,63 +509,82 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<W: Write> Write for BufWriter<W> {
+    #[inline]
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        if self.buf.len() + buf.len() > self.buf.capacity() {
-            self.flush_buf()?;
-        }
-        // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
-        if buf.len() >= self.buf.capacity() {
-            self.panicked = true;
-            let r = self.get_mut().write(buf);
-            self.panicked = false;
-            r
-        } else {
-            self.buf.extend_from_slice(buf);
+        // Use < instead of <= to avoid a needless trip through the buffer in some cases.
+        // See `write_cold` for details.
+        if buf.len() < self.spare_capacity() {
+            // SAFETY: safe by above conditional.
+            unsafe {
+                self.write_to_buffer_unchecked(buf);
+            }
+
             Ok(buf.len())
+        } else {
+            self.write_cold(buf)
         }
     }
 
+    #[inline]
     fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
-        // Normally, `write_all` just calls `write` in a loop. We can do better
-        // by calling `self.get_mut().write_all()` directly, which avoids
-        // round trips through the buffer in the event of a series of partial
-        // writes in some circumstances.
-        if self.buf.len() + buf.len() > self.buf.capacity() {
-            self.flush_buf()?;
-        }
-        // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
-        if buf.len() >= self.buf.capacity() {
-            self.panicked = true;
-            let r = self.get_mut().write_all(buf);
-            self.panicked = false;
-            r
-        } else {
-            self.buf.extend_from_slice(buf);
+        // Use < instead of <= to avoid a needless trip through the buffer in some cases.
+        // See `write_all_cold` for details.
+        if buf.len() < self.spare_capacity() {
+            // SAFETY: safe by above conditional.
+            unsafe {
+                self.write_to_buffer_unchecked(buf);
+            }
+
             Ok(())
+        } else {
+            self.write_all_cold(buf)
         }
     }
 
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        // FIXME: Consider applying `#[inline]` / `#[inline(never)]` optimizations already applied
+        // to `write` and `write_all`. The performance benefits can be significant. See #79930.
         if self.get_ref().is_write_vectored() {
-            let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
-            if self.buf.len() + total_len > self.buf.capacity() {
+            // We have to handle the possibility that the total length of the buffers overflows
+            // `usize` (even though this can only happen if multiple `IoSlice`s reference the
+            // same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the
+            // computation overflows, then surely the input cannot fit in our buffer, so we forward
+            // to the inner writer's `write_vectored` method to let it handle it appropriately.
+            let saturated_total_len =
+                bufs.iter().fold(0usize, |acc, b| acc.saturating_add(b.len()));
+
+            if saturated_total_len > self.spare_capacity() {
+                // Flush if the total length of the input exceeds our buffer's spare capacity.
+                // If we would have overflowed, this condition also holds, and we need to flush.
                 self.flush_buf()?;
             }
-            if total_len >= self.buf.capacity() {
+
+            if saturated_total_len >= self.buf.capacity() {
+                // Forward to our inner writer if the total length of the input is greater than or
+                // equal to our buffer capacity. If we would have overflowed, this condition also
+                // holds, and we punt to the inner writer.
                 self.panicked = true;
                 let r = self.get_mut().write_vectored(bufs);
                 self.panicked = false;
                 r
             } else {
-                bufs.iter().for_each(|b| self.buf.extend_from_slice(b));
-                Ok(total_len)
+                // `saturated_total_len < self.buf.capacity()` implies that we did not saturate.
+
+                // SAFETY: We checked whether or not the spare capacity was large enough above. If
+                // it was, then we're safe already. If it wasn't, we flushed, making sufficient
+                // room for any input <= the buffer size, which includes this input.
+                unsafe {
+                    bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b));
+                };
+
+                Ok(saturated_total_len)
             }
         } else {
             let mut iter = bufs.iter();
             let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) {
                 // This is the first non-empty slice to write, so if it does
                 // not fit in the buffer, we still get to flush and proceed.
-                if self.buf.len() + buf.len() > self.buf.capacity() {
+                if buf.len() > self.spare_capacity() {
                     self.flush_buf()?;
                 }
                 if buf.len() >= self.buf.capacity() {
@@ -469,7 +595,13 @@ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
                     self.panicked = false;
                     return r;
                 } else {
-                    self.buf.extend_from_slice(buf);
+                    // SAFETY: We checked whether or not the spare capacity was large enough above.
+                    // If it was, then we're safe already. If it wasn't, we flushed, making
+                    // sufficient room for any input <= the buffer size, which includes this input.
+                    unsafe {
+                        self.write_to_buffer_unchecked(buf);
+                    }
+
                     buf.len()
                 }
             } else {
@@ -477,11 +609,18 @@ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
             };
             debug_assert!(total_written != 0);
             for buf in iter {
-                if self.buf.len() + buf.len() > self.buf.capacity() {
-                    break;
-                } else {
-                    self.buf.extend_from_slice(buf);
+                if buf.len() <= self.spare_capacity() {
+                    // SAFETY: safe by above conditional.
+                    unsafe {
+                        self.write_to_buffer_unchecked(buf);
+                    }
+
+                    // This cannot overflow `usize`. If we are here, we've written all of the bytes
+                    // so far to our buffer, and we've ensured that we never exceed the buffer's
+                    // capacity. Therefore, `total_written` <= `self.buf.capacity()` <= `usize::MAX`.
                     total_written += buf.len();
+                } else {
+                    break;
                 }
             }
             Ok(total_written)
index 94c70c4f267b16a82892d0221428040c52c8a729..9f43379aff7878ab64a636f8f0411e8788120def 100644 (file)
@@ -1663,10 +1663,47 @@ pub trait Seek {
     ///
     /// # Errors
     ///
+    /// Seeking can fail, for example becaue it might involve flushing a buffer.
+    ///
     /// Seeking to a negative offset is considered an error.
     #[stable(feature = "rust1", since = "1.0.0")]
     fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
 
+    /// Rewind to the beginning of a stream.
+    ///
+    /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`.
+    ///
+    /// # Errors
+    ///
+    /// Rewinding can fail, for example becaue it might involve flushing a buffer.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// #![feature(seek_rewind)]
+    /// use std::io::{Read, Seek, Write};
+    /// use std::fs::OpenOptions;
+    ///
+    /// let mut f = OpenOptions::new()
+    ///     .write(true)
+    ///     .read(true)
+    ///     .create(true)
+    ///     .open("foo.txt").unwrap();
+    ///
+    /// let hello = "Hello!\n";
+    /// write!(f, "{}", hello).unwrap();
+    /// f.rewind().unwrap();
+    ///
+    /// let mut buf = String::new();
+    /// f.read_to_string(&mut buf).unwrap();
+    /// assert_eq!(&buf, hello);
+    /// ```
+    #[unstable(feature = "seek_rewind", issue = "85149")]
+    fn rewind(&mut self) -> Result<()> {
+        self.seek(SeekFrom::Start(0))?;
+        Ok(())
+    }
+
     /// Returns the length of this stream (in bytes).
     ///
     /// This method is implemented using up to three seek operations. If this
index 0ab9f490fd4202b4f2e339fd612c99c06bdcdfad..5f89ac059fd2d9194c7a909a2ee3445a08cc7725 100644 (file)
 #![feature(const_cstr_unchecked)]
 #![feature(const_fn_floating_point_arithmetic)]
 #![feature(const_fn_transmute)]
-#![feature(const_fn)]
 #![feature(const_fn_fn_ptr_basics)]
 #![feature(const_io_structs)]
 #![feature(const_ip)]
index dda7c82525debc637e2d5ecd82e126fc927fb3ef..503645c08ce36568815682962f39117eef6a95db 100644 (file)
@@ -22,7 +22,6 @@
 #[stable(feature = "pthread_t", since = "1.8.0")]
 pub type pthread_t = c_ulong;
 
-#[doc(inline)]
 #[stable(feature = "raw_ext", since = "1.1.0")]
 pub type blkcnt_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")]
index a5e453034762c16d5d9dac37d898f41319a8df92..f9536c4203df2928c9e65c25cac6686e964c9e5d 100644 (file)
 extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn {
     // FIXME: how to support TLS in library mode?
     let tls = Box::new(tls::Tls::new());
-    let _tls_guard = unsafe { tls.activate() };
+    let tls_guard = unsafe { tls.activate() };
 
     if secondary {
-        super::thread::Thread::entry();
+        let join_notifier = super::thread::Thread::entry();
+        drop(tls_guard);
+        drop(join_notifier);
 
         EntryReturn(0, 0)
     } else {
diff --git a/library/std/src/sys/sgx/abi/tls.rs b/library/std/src/sys/sgx/abi/tls.rs
deleted file mode 100644 (file)
index 13d96e9..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-mod sync_bitset;
-
-use self::sync_bitset::*;
-use crate::cell::Cell;
-use crate::mem;
-use crate::num::NonZeroUsize;
-use crate::ptr;
-use crate::sync::atomic::{AtomicUsize, Ordering};
-
-#[cfg(target_pointer_width = "64")]
-const USIZE_BITS: usize = 64;
-const TLS_KEYS: usize = 128; // Same as POSIX minimum
-const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS;
-
-#[cfg_attr(test, linkage = "available_externally")]
-#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE"]
-static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT;
-macro_rules! dup {
-    ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* ));
-    (() $($val:tt)*) => ([$($val),*])
-}
-#[cfg_attr(test, linkage = "available_externally")]
-#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"]
-static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0)));
-
-extern "C" {
-    fn get_tls_ptr() -> *const u8;
-    fn set_tls_ptr(tls: *const u8);
-}
-
-#[derive(Copy, Clone)]
-#[repr(C)]
-pub struct Key(NonZeroUsize);
-
-impl Key {
-    fn to_index(self) -> usize {
-        self.0.get() - 1
-    }
-
-    fn from_index(index: usize) -> Self {
-        Key(NonZeroUsize::new(index + 1).unwrap())
-    }
-
-    pub fn as_usize(self) -> usize {
-        self.0.get()
-    }
-
-    pub fn from_usize(index: usize) -> Self {
-        Key(NonZeroUsize::new(index).unwrap())
-    }
-}
-
-#[repr(C)]
-pub struct Tls {
-    data: [Cell<*mut u8>; TLS_KEYS],
-}
-
-pub struct ActiveTls<'a> {
-    tls: &'a Tls,
-}
-
-impl<'a> Drop for ActiveTls<'a> {
-    fn drop(&mut self) {
-        let value_with_destructor = |key: usize| {
-            let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed);
-            unsafe { mem::transmute::<_, Option<unsafe extern "C" fn(*mut u8)>>(ptr) }
-                .map(|dtor| (&self.tls.data[key], dtor))
-        };
-
-        let mut any_non_null_dtor = true;
-        while any_non_null_dtor {
-            any_non_null_dtor = false;
-            for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) {
-                let value = value.replace(ptr::null_mut());
-                if !value.is_null() {
-                    any_non_null_dtor = true;
-                    unsafe { dtor(value) }
-                }
-            }
-        }
-    }
-}
-
-impl Tls {
-    pub fn new() -> Tls {
-        Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) }
-    }
-
-    pub unsafe fn activate(&self) -> ActiveTls<'_> {
-        // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
-        unsafe { set_tls_ptr(self as *const Tls as _) };
-        ActiveTls { tls: self }
-    }
-
-    #[allow(unused)]
-    pub unsafe fn activate_persistent(self: Box<Self>) {
-        // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
-        unsafe { set_tls_ptr((&*self) as *const Tls as _) };
-        mem::forget(self);
-    }
-
-    unsafe fn current<'a>() -> &'a Tls {
-        // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
-        unsafe { &*(get_tls_ptr() as *const Tls) }
-    }
-
-    pub fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
-        let index = if let Some(index) = TLS_KEY_IN_USE.set() {
-            index
-        } else {
-            rtabort!("TLS limit exceeded")
-        };
-        TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed);
-        Key::from_index(index)
-    }
-
-    pub fn set(key: Key, value: *mut u8) {
-        let index = key.to_index();
-        rtassert!(TLS_KEY_IN_USE.get(index));
-        unsafe { Self::current() }.data[index].set(value);
-    }
-
-    pub fn get(key: Key) -> *mut u8 {
-        let index = key.to_index();
-        rtassert!(TLS_KEY_IN_USE.get(index));
-        unsafe { Self::current() }.data[index].get()
-    }
-
-    pub fn destroy(key: Key) {
-        TLS_KEY_IN_USE.clear(key.to_index());
-    }
-}
diff --git a/library/std/src/sys/sgx/abi/tls/mod.rs b/library/std/src/sys/sgx/abi/tls/mod.rs
new file mode 100644 (file)
index 0000000..13d96e9
--- /dev/null
@@ -0,0 +1,132 @@
+mod sync_bitset;
+
+use self::sync_bitset::*;
+use crate::cell::Cell;
+use crate::mem;
+use crate::num::NonZeroUsize;
+use crate::ptr;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+
+#[cfg(target_pointer_width = "64")]
+const USIZE_BITS: usize = 64;
+const TLS_KEYS: usize = 128; // Same as POSIX minimum
+const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS;
+
+#[cfg_attr(test, linkage = "available_externally")]
+#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE"]
+static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT;
+macro_rules! dup {
+    ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* ));
+    (() $($val:tt)*) => ([$($val),*])
+}
+#[cfg_attr(test, linkage = "available_externally")]
+#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"]
+static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0)));
+
+extern "C" {
+    fn get_tls_ptr() -> *const u8;
+    fn set_tls_ptr(tls: *const u8);
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct Key(NonZeroUsize);
+
+impl Key {
+    fn to_index(self) -> usize {
+        self.0.get() - 1
+    }
+
+    fn from_index(index: usize) -> Self {
+        Key(NonZeroUsize::new(index + 1).unwrap())
+    }
+
+    pub fn as_usize(self) -> usize {
+        self.0.get()
+    }
+
+    pub fn from_usize(index: usize) -> Self {
+        Key(NonZeroUsize::new(index).unwrap())
+    }
+}
+
+#[repr(C)]
+pub struct Tls {
+    data: [Cell<*mut u8>; TLS_KEYS],
+}
+
+pub struct ActiveTls<'a> {
+    tls: &'a Tls,
+}
+
+impl<'a> Drop for ActiveTls<'a> {
+    fn drop(&mut self) {
+        let value_with_destructor = |key: usize| {
+            let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed);
+            unsafe { mem::transmute::<_, Option<unsafe extern "C" fn(*mut u8)>>(ptr) }
+                .map(|dtor| (&self.tls.data[key], dtor))
+        };
+
+        let mut any_non_null_dtor = true;
+        while any_non_null_dtor {
+            any_non_null_dtor = false;
+            for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) {
+                let value = value.replace(ptr::null_mut());
+                if !value.is_null() {
+                    any_non_null_dtor = true;
+                    unsafe { dtor(value) }
+                }
+            }
+        }
+    }
+}
+
+impl Tls {
+    pub fn new() -> Tls {
+        Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) }
+    }
+
+    pub unsafe fn activate(&self) -> ActiveTls<'_> {
+        // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
+        unsafe { set_tls_ptr(self as *const Tls as _) };
+        ActiveTls { tls: self }
+    }
+
+    #[allow(unused)]
+    pub unsafe fn activate_persistent(self: Box<Self>) {
+        // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
+        unsafe { set_tls_ptr((&*self) as *const Tls as _) };
+        mem::forget(self);
+    }
+
+    unsafe fn current<'a>() -> &'a Tls {
+        // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
+        unsafe { &*(get_tls_ptr() as *const Tls) }
+    }
+
+    pub fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
+        let index = if let Some(index) = TLS_KEY_IN_USE.set() {
+            index
+        } else {
+            rtabort!("TLS limit exceeded")
+        };
+        TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed);
+        Key::from_index(index)
+    }
+
+    pub fn set(key: Key, value: *mut u8) {
+        let index = key.to_index();
+        rtassert!(TLS_KEY_IN_USE.get(index));
+        unsafe { Self::current() }.data[index].set(value);
+    }
+
+    pub fn get(key: Key) -> *mut u8 {
+        let index = key.to_index();
+        rtassert!(TLS_KEY_IN_USE.get(index));
+        unsafe { Self::current() }.data[index].get()
+    }
+
+    pub fn destroy(key: Key) {
+        TLS_KEY_IN_USE.clear(key.to_index());
+    }
+}
index 8874517dac60cf542b10e0ee8f7f8071c755beeb..1b5ced4178f6abd5aff9b1272623b0ec67abab69 100644 (file)
@@ -8,7 +8,7 @@ pub struct Mutex {
     inner: SpinMutex<WaitVariable<bool>>,
 }
 
-pub type MovableMutex = Box<Mutex>;
+pub type MovableMutex = Mutex;
 
 // Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
 impl Mutex {
index 55ef460cc90c55116acb170c0f992e9d98f12526..67e2e8b59d397d8e14ce783c414fff0d39b8f07c 100644 (file)
@@ -9,26 +9,37 @@
 
 pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
 
+pub use self::task_queue::JoinNotifier;
+
 mod task_queue {
-    use crate::sync::mpsc;
+    use super::wait_notify;
     use crate::sync::{Mutex, MutexGuard, Once};
 
-    pub type JoinHandle = mpsc::Receiver<()>;
+    pub type JoinHandle = wait_notify::Waiter;
+
+    pub struct JoinNotifier(Option<wait_notify::Notifier>);
+
+    impl Drop for JoinNotifier {
+        fn drop(&mut self) {
+            self.0.take().unwrap().notify();
+        }
+    }
 
     pub(super) struct Task {
         p: Box<dyn FnOnce()>,
-        done: mpsc::Sender<()>,
+        done: JoinNotifier,
     }
 
     impl Task {
         pub(super) fn new(p: Box<dyn FnOnce()>) -> (Task, JoinHandle) {
-            let (done, recv) = mpsc::channel();
+            let (done, recv) = wait_notify::new();
+            let done = JoinNotifier(Some(done));
             (Task { p, done }, recv)
         }
 
-        pub(super) fn run(self) {
+        pub(super) fn run(self) -> JoinNotifier {
             (self.p)();
-            let _ = self.done.send(());
+            self.done
         }
     }
 
@@ -47,6 +58,48 @@ pub(super) fn lock() -> MutexGuard<'static, Vec<Task>> {
     }
 }
 
+/// This module provides a synchronization primitive that does not use thread
+/// local variables. This is needed for signaling that a thread has finished
+/// execution. The signal is sent once all TLS destructors have finished at
+/// which point no new thread locals should be created.
+pub mod wait_notify {
+    use super::super::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
+    use crate::sync::Arc;
+
+    pub struct Notifier(Arc<SpinMutex<WaitVariable<bool>>>);
+
+    impl Notifier {
+        /// Notify the waiter. The waiter is either notified right away (if
+        /// currently blocked in `Waiter::wait()`) or later when it calls the
+        /// `Waiter::wait()` method.
+        pub fn notify(self) {
+            let mut guard = self.0.lock();
+            *guard.lock_var_mut() = true;
+            let _ = WaitQueue::notify_one(guard);
+        }
+    }
+
+    pub struct Waiter(Arc<SpinMutex<WaitVariable<bool>>>);
+
+    impl Waiter {
+        /// Wait for a notification. If `Notifier::notify()` has already been
+        /// called, this will return immediately, otherwise the current thread
+        /// is blocked until notified.
+        pub fn wait(self) {
+            let guard = self.0.lock();
+            if *guard.lock_var() {
+                return;
+            }
+            WaitQueue::wait(guard, || {});
+        }
+    }
+
+    pub fn new() -> (Notifier, Waiter) {
+        let inner = Arc::new(SpinMutex::new(WaitVariable::new(false)));
+        (Notifier(inner.clone()), Waiter(inner))
+    }
+}
+
 impl Thread {
     // unsafe: see thread::Builder::spawn_unchecked for safety requirements
     pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
@@ -57,7 +110,7 @@ pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
         Ok(Thread(handle))
     }
 
-    pub(super) fn entry() {
+    pub(super) fn entry() -> JoinNotifier {
         let mut pending_tasks = task_queue::lock();
         let task = rtunwrap!(Some, pending_tasks.pop());
         drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary
@@ -78,7 +131,7 @@ pub fn sleep(dur: Duration) {
     }
 
     pub fn join(self) {
-        let _ = self.0.recv();
+        self.0.wait();
     }
 }
 
diff --git a/library/std/src/sys/sgx/waitqueue.rs b/library/std/src/sys/sgx/waitqueue.rs
deleted file mode 100644 (file)
index e464dc3..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-//! A simple queue implementation for synchronization primitives.
-//!
-//! This queue is used to implement condition variable and mutexes.
-//!
-//! Users of this API are expected to use the `WaitVariable<T>` type. Since
-//! that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to
-//! allow shared access.
-//!
-//! Since userspace may send spurious wake-ups, the wakeup event state is
-//! recorded in the enclave. The wakeup event state is protected by a spinlock.
-//! The queue and associated wait state are stored in a `WaitVariable`.
-
-#[cfg(test)]
-mod tests;
-
-/// A doubly-linked list where callers are in charge of memory allocation
-/// of the nodes in the list.
-mod unsafe_list;
-
-/// Trivial spinlock-based implementation of `sync::Mutex`.
-// FIXME: Perhaps use Intel TSX to avoid locking?
-mod spin_mutex;
-
-use crate::num::NonZeroUsize;
-use crate::ops::{Deref, DerefMut};
-use crate::time::Duration;
-
-use super::abi::thread;
-use super::abi::usercalls;
-use fortanix_sgx_abi::{Tcs, EV_UNPARK, WAIT_INDEFINITE};
-
-pub use self::spin_mutex::{try_lock_or_false, SpinMutex, SpinMutexGuard};
-use self::unsafe_list::{UnsafeList, UnsafeListEntry};
-
-/// An queue entry in a `WaitQueue`.
-struct WaitEntry {
-    /// TCS address of the thread that is waiting
-    tcs: Tcs,
-    /// Whether this thread has been notified to be awoken
-    wake: bool,
-}
-
-/// Data stored with a `WaitQueue` alongside it. This ensures accesses to the
-/// queue and the data are synchronized, since the type itself is not `Sync`.
-///
-/// Consumers of this API should use a synchronization primitive for shared
-/// access, such as `SpinMutex`.
-#[derive(Default)]
-pub struct WaitVariable<T> {
-    queue: WaitQueue,
-    lock: T,
-}
-
-impl<T> WaitVariable<T> {
-    pub const fn new(var: T) -> Self {
-        WaitVariable { queue: WaitQueue::new(), lock: var }
-    }
-
-    pub fn queue_empty(&self) -> bool {
-        self.queue.is_empty()
-    }
-
-    pub fn lock_var(&self) -> &T {
-        &self.lock
-    }
-
-    pub fn lock_var_mut(&mut self) -> &mut T {
-        &mut self.lock
-    }
-}
-
-#[derive(Copy, Clone)]
-pub enum NotifiedTcs {
-    Single(Tcs),
-    All { count: NonZeroUsize },
-}
-
-/// An RAII guard that will notify a set of target threads as well as unlock
-/// a mutex on drop.
-pub struct WaitGuard<'a, T: 'a> {
-    mutex_guard: Option<SpinMutexGuard<'a, WaitVariable<T>>>,
-    notified_tcs: NotifiedTcs,
-}
-
-/// A queue of threads that are waiting on some synchronization primitive.
-///
-/// `UnsafeList` entries are allocated on the waiting thread's stack. This
-/// avoids any global locking that might happen in the heap allocator. This is
-/// safe because the waiting thread will not return from that stack frame until
-/// after it is notified. The notifying thread ensures to clean up any
-/// references to the list entries before sending the wakeup event.
-pub struct WaitQueue {
-    // We use an inner Mutex here to protect the data in the face of spurious
-    // wakeups.
-    inner: UnsafeList<SpinMutex<WaitEntry>>,
-}
-unsafe impl Send for WaitQueue {}
-
-impl Default for WaitQueue {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl<'a, T> WaitGuard<'a, T> {
-    /// Returns which TCSes will be notified when this guard drops.
-    pub fn notified_tcs(&self) -> NotifiedTcs {
-        self.notified_tcs
-    }
-
-    /// Drop this `WaitGuard`, after dropping another `guard`.
-    pub fn drop_after<U>(self, guard: U) {
-        drop(guard);
-        drop(self);
-    }
-}
-
-impl<'a, T> Deref for WaitGuard<'a, T> {
-    type Target = SpinMutexGuard<'a, WaitVariable<T>>;
-
-    fn deref(&self) -> &Self::Target {
-        self.mutex_guard.as_ref().unwrap()
-    }
-}
-
-impl<'a, T> DerefMut for WaitGuard<'a, T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        self.mutex_guard.as_mut().unwrap()
-    }
-}
-
-impl<'a, T> Drop for WaitGuard<'a, T> {
-    fn drop(&mut self) {
-        drop(self.mutex_guard.take());
-        let target_tcs = match self.notified_tcs {
-            NotifiedTcs::Single(tcs) => Some(tcs),
-            NotifiedTcs::All { .. } => None,
-        };
-        rtunwrap!(Ok, usercalls::send(EV_UNPARK, target_tcs));
-    }
-}
-
-impl WaitQueue {
-    pub const fn new() -> Self {
-        WaitQueue { inner: UnsafeList::new() }
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.inner.is_empty()
-    }
-
-    /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
-    /// until a wakeup event.
-    ///
-    /// This function does not return until this thread has been awoken.
-    pub fn wait<T, F: FnOnce()>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>, before_wait: F) {
-        // very unsafe: check requirements of UnsafeList::push
-        unsafe {
-            let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
-                tcs: thread::current(),
-                wake: false,
-            }));
-            let entry = guard.queue.inner.push(&mut entry);
-            drop(guard);
-            before_wait();
-            while !entry.lock().wake {
-                // don't panic, this would invalidate `entry` during unwinding
-                let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE));
-                rtassert!(eventset & EV_UNPARK == EV_UNPARK);
-            }
-        }
-    }
-
-    /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
-    /// until a wakeup event or timeout. If event was observed, returns true.
-    /// If not, it will remove the calling thread from the wait queue.
-    pub fn wait_timeout<T, F: FnOnce()>(
-        lock: &SpinMutex<WaitVariable<T>>,
-        timeout: Duration,
-        before_wait: F,
-    ) -> bool {
-        // very unsafe: check requirements of UnsafeList::push
-        unsafe {
-            let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
-                tcs: thread::current(),
-                wake: false,
-            }));
-            let entry_lock = lock.lock().queue.inner.push(&mut entry);
-            before_wait();
-            usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake);
-            // acquire the wait queue's lock first to avoid deadlock.
-            let mut guard = lock.lock();
-            let success = entry_lock.lock().wake;
-            if !success {
-                // nobody is waking us up, so remove our entry from the wait queue.
-                guard.queue.inner.remove(&mut entry);
-            }
-            success
-        }
-    }
-
-    /// Either find the next waiter on the wait queue, or return the mutex
-    /// guard unchanged.
-    ///
-    /// If a waiter is found, a `WaitGuard` is returned which will notify the
-    /// waiter when it is dropped.
-    pub fn notify_one<T>(
-        mut guard: SpinMutexGuard<'_, WaitVariable<T>>,
-    ) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> {
-        unsafe {
-            if let Some(entry) = guard.queue.inner.pop() {
-                let mut entry_guard = entry.lock();
-                let tcs = entry_guard.tcs;
-                entry_guard.wake = true;
-                drop(entry);
-                Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::Single(tcs) })
-            } else {
-                Err(guard)
-            }
-        }
-    }
-
-    /// Either find any and all waiters on the wait queue, or return the mutex
-    /// guard unchanged.
-    ///
-    /// If at least one waiter is found, a `WaitGuard` is returned which will
-    /// notify all waiters when it is dropped.
-    pub fn notify_all<T>(
-        mut guard: SpinMutexGuard<'_, WaitVariable<T>>,
-    ) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> {
-        unsafe {
-            let mut count = 0;
-            while let Some(entry) = guard.queue.inner.pop() {
-                count += 1;
-                let mut entry_guard = entry.lock();
-                entry_guard.wake = true;
-            }
-            if let Some(count) = NonZeroUsize::new(count) {
-                Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::All { count } })
-            } else {
-                Err(guard)
-            }
-        }
-    }
-}
diff --git a/library/std/src/sys/sgx/waitqueue/mod.rs b/library/std/src/sys/sgx/waitqueue/mod.rs
new file mode 100644 (file)
index 0000000..61bb11d
--- /dev/null
@@ -0,0 +1,240 @@
+//! A simple queue implementation for synchronization primitives.
+//!
+//! This queue is used to implement condition variable and mutexes.
+//!
+//! Users of this API are expected to use the `WaitVariable<T>` type. Since
+//! that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to
+//! allow shared access.
+//!
+//! Since userspace may send spurious wake-ups, the wakeup event state is
+//! recorded in the enclave. The wakeup event state is protected by a spinlock.
+//! The queue and associated wait state are stored in a `WaitVariable`.
+
+#[cfg(test)]
+mod tests;
+
+mod spin_mutex;
+mod unsafe_list;
+
+use crate::num::NonZeroUsize;
+use crate::ops::{Deref, DerefMut};
+use crate::time::Duration;
+
+use super::abi::thread;
+use super::abi::usercalls;
+use fortanix_sgx_abi::{Tcs, EV_UNPARK, WAIT_INDEFINITE};
+
+pub use self::spin_mutex::{try_lock_or_false, SpinMutex, SpinMutexGuard};
+use self::unsafe_list::{UnsafeList, UnsafeListEntry};
+
+/// An queue entry in a `WaitQueue`.
+struct WaitEntry {
+    /// TCS address of the thread that is waiting
+    tcs: Tcs,
+    /// Whether this thread has been notified to be awoken
+    wake: bool,
+}
+
+/// Data stored with a `WaitQueue` alongside it. This ensures accesses to the
+/// queue and the data are synchronized, since the type itself is not `Sync`.
+///
+/// Consumers of this API should use a synchronization primitive for shared
+/// access, such as `SpinMutex`.
+#[derive(Default)]
+pub struct WaitVariable<T> {
+    queue: WaitQueue,
+    lock: T,
+}
+
+impl<T> WaitVariable<T> {
+    pub const fn new(var: T) -> Self {
+        WaitVariable { queue: WaitQueue::new(), lock: var }
+    }
+
+    pub fn queue_empty(&self) -> bool {
+        self.queue.is_empty()
+    }
+
+    pub fn lock_var(&self) -> &T {
+        &self.lock
+    }
+
+    pub fn lock_var_mut(&mut self) -> &mut T {
+        &mut self.lock
+    }
+}
+
+#[derive(Copy, Clone)]
+pub enum NotifiedTcs {
+    Single(Tcs),
+    All { count: NonZeroUsize },
+}
+
+/// An RAII guard that will notify a set of target threads as well as unlock
+/// a mutex on drop.
+pub struct WaitGuard<'a, T: 'a> {
+    mutex_guard: Option<SpinMutexGuard<'a, WaitVariable<T>>>,
+    notified_tcs: NotifiedTcs,
+}
+
+/// A queue of threads that are waiting on some synchronization primitive.
+///
+/// `UnsafeList` entries are allocated on the waiting thread's stack. This
+/// avoids any global locking that might happen in the heap allocator. This is
+/// safe because the waiting thread will not return from that stack frame until
+/// after it is notified. The notifying thread ensures to clean up any
+/// references to the list entries before sending the wakeup event.
+pub struct WaitQueue {
+    // We use an inner Mutex here to protect the data in the face of spurious
+    // wakeups.
+    inner: UnsafeList<SpinMutex<WaitEntry>>,
+}
+unsafe impl Send for WaitQueue {}
+
+impl Default for WaitQueue {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<'a, T> WaitGuard<'a, T> {
+    /// Returns which TCSes will be notified when this guard drops.
+    pub fn notified_tcs(&self) -> NotifiedTcs {
+        self.notified_tcs
+    }
+
+    /// Drop this `WaitGuard`, after dropping another `guard`.
+    pub fn drop_after<U>(self, guard: U) {
+        drop(guard);
+        drop(self);
+    }
+}
+
+impl<'a, T> Deref for WaitGuard<'a, T> {
+    type Target = SpinMutexGuard<'a, WaitVariable<T>>;
+
+    fn deref(&self) -> &Self::Target {
+        self.mutex_guard.as_ref().unwrap()
+    }
+}
+
+impl<'a, T> DerefMut for WaitGuard<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.mutex_guard.as_mut().unwrap()
+    }
+}
+
+impl<'a, T> Drop for WaitGuard<'a, T> {
+    fn drop(&mut self) {
+        drop(self.mutex_guard.take());
+        let target_tcs = match self.notified_tcs {
+            NotifiedTcs::Single(tcs) => Some(tcs),
+            NotifiedTcs::All { .. } => None,
+        };
+        rtunwrap!(Ok, usercalls::send(EV_UNPARK, target_tcs));
+    }
+}
+
+impl WaitQueue {
+    pub const fn new() -> Self {
+        WaitQueue { inner: UnsafeList::new() }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.inner.is_empty()
+    }
+
+    /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
+    /// until a wakeup event.
+    ///
+    /// This function does not return until this thread has been awoken.
+    pub fn wait<T, F: FnOnce()>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>, before_wait: F) {
+        // very unsafe: check requirements of UnsafeList::push
+        unsafe {
+            let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
+                tcs: thread::current(),
+                wake: false,
+            }));
+            let entry = guard.queue.inner.push(&mut entry);
+            drop(guard);
+            before_wait();
+            while !entry.lock().wake {
+                // don't panic, this would invalidate `entry` during unwinding
+                let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE));
+                rtassert!(eventset & EV_UNPARK == EV_UNPARK);
+            }
+        }
+    }
+
+    /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
+    /// until a wakeup event or timeout. If event was observed, returns true.
+    /// If not, it will remove the calling thread from the wait queue.
+    pub fn wait_timeout<T, F: FnOnce()>(
+        lock: &SpinMutex<WaitVariable<T>>,
+        timeout: Duration,
+        before_wait: F,
+    ) -> bool {
+        // very unsafe: check requirements of UnsafeList::push
+        unsafe {
+            let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
+                tcs: thread::current(),
+                wake: false,
+            }));
+            let entry_lock = lock.lock().queue.inner.push(&mut entry);
+            before_wait();
+            usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake);
+            // acquire the wait queue's lock first to avoid deadlock.
+            let mut guard = lock.lock();
+            let success = entry_lock.lock().wake;
+            if !success {
+                // nobody is waking us up, so remove our entry from the wait queue.
+                guard.queue.inner.remove(&mut entry);
+            }
+            success
+        }
+    }
+
+    /// Either find the next waiter on the wait queue, or return the mutex
+    /// guard unchanged.
+    ///
+    /// If a waiter is found, a `WaitGuard` is returned which will notify the
+    /// waiter when it is dropped.
+    pub fn notify_one<T>(
+        mut guard: SpinMutexGuard<'_, WaitVariable<T>>,
+    ) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> {
+        unsafe {
+            if let Some(entry) = guard.queue.inner.pop() {
+                let mut entry_guard = entry.lock();
+                let tcs = entry_guard.tcs;
+                entry_guard.wake = true;
+                drop(entry);
+                Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::Single(tcs) })
+            } else {
+                Err(guard)
+            }
+        }
+    }
+
+    /// Either find any and all waiters on the wait queue, or return the mutex
+    /// guard unchanged.
+    ///
+    /// If at least one waiter is found, a `WaitGuard` is returned which will
+    /// notify all waiters when it is dropped.
+    pub fn notify_all<T>(
+        mut guard: SpinMutexGuard<'_, WaitVariable<T>>,
+    ) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> {
+        unsafe {
+            let mut count = 0;
+            while let Some(entry) = guard.queue.inner.pop() {
+                count += 1;
+                let mut entry_guard = entry.lock();
+                entry_guard.wake = true;
+            }
+            if let Some(count) = NonZeroUsize::new(count) {
+                Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::All { count } })
+            } else {
+                Err(guard)
+            }
+        }
+    }
+}
index 7f1a671bab4eb73bccb31da09d1f3e4558c94ea8..f6e851ccaddfaed51f9bf3b3be1a31309cfa920d 100644 (file)
@@ -1,3 +1,6 @@
+//! Trivial spinlock-based implementation of `sync::Mutex`.
+// FIXME: Perhaps use Intel TSX to avoid locking?
+
 #[cfg(test)]
 mod tests;
 
index 0834d2593fc320735564aea28667e4ccdc7f0447..cf2f0886c15464773fb1de1313db8913207eb4a4 100644 (file)
@@ -1,3 +1,6 @@
+//! A doubly-linked list where callers are in charge of memory allocation
+//! of the nodes in the list.
+
 #[cfg(test)]
 mod tests;
 
index 984c08c2ad53171d5741f1ea4b923a7a9490ac32..51c3e5d175cca86a0b9c351c19a6ccf459378b1d 100644 (file)
@@ -155,12 +155,10 @@ pub fn getcwd() -> io::Result<PathBuf> {
 pub fn chdir(p: &path::Path) -> io::Result<()> {
     let p: &OsStr = p.as_ref();
     let p = CString::new(p.as_bytes())?;
-    unsafe {
-        match libc::chdir(p.as_ptr()) == (0 as c_int) {
-            true => Ok(()),
-            false => Err(io::Error::last_os_error()),
-        }
+    if unsafe { libc::chdir(p.as_ptr()) } != 0 {
+        return Err(io::Error::last_os_error());
     }
+    Ok(())
 }
 
 pub struct SplitPaths<'a> {
index c924a7d8a26723043424a4e64914619919bf2245..a2d75a61976337f691b7bfacf3a33eae425bb29e 100644 (file)
@@ -1,4 +1,5 @@
 use crate::ffi::OsString;
+use crate::fmt;
 
 pub struct Args {}
 
diff --git a/library/std/src/sys/wasm/args.rs b/library/std/src/sys/wasm/args.rs
deleted file mode 100644 (file)
index fde1ab7..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-use crate::ffi::OsString;
-use crate::fmt;
-use crate::vec;
-
-pub fn args() -> Args {
-    Args { iter: Vec::new().into_iter() }
-}
-
-pub struct Args {
-    iter: vec::IntoIter<OsString>,
-}
-
-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)
-    }
-}
-
-impl Iterator for Args {
-    type Item = OsString;
-    fn next(&mut self) -> Option<OsString> {
-        self.iter.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-impl ExactSizeIterator for Args {
-    fn len(&self) -> usize {
-        self.iter.len()
-    }
-}
-
-impl DoubleEndedIterator for Args {
-    fn next_back(&mut self) -> Option<OsString> {
-        self.iter.next_back()
-    }
-}
diff --git a/library/std/src/sys/wasm/atomics/condvar.rs b/library/std/src/sys/wasm/atomics/condvar.rs
new file mode 100644 (file)
index 0000000..0c1c076
--- /dev/null
@@ -0,0 +1,102 @@
+use crate::arch::wasm32;
+use crate::cmp;
+use crate::mem;
+use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+use crate::sys::mutex::Mutex;
+use crate::time::Duration;
+
+pub struct Condvar {
+    cnt: AtomicUsize,
+}
+
+pub type MovableCondvar = Condvar;
+
+// Condition variables are implemented with a simple counter internally that is
+// likely to cause spurious wakeups. Blocking on a condition variable will first
+// read the value of the internal counter, unlock the given mutex, and then
+// block if and only if the counter's value is still the same. Notifying a
+// condition variable will modify the counter (add one for now) and then wake up
+// a thread waiting on the address of the counter.
+//
+// A thread waiting on the condition variable will as a result avoid going to
+// sleep if it's notified after the lock is unlocked but before it fully goes to
+// sleep. A sleeping thread is guaranteed to be woken up at some point as it can
+// only be woken up with a call to `wake`.
+//
+// Note that it's possible for 2 or more threads to be woken up by a call to
+// `notify_one` with this implementation. That can happen where the modification
+// of `cnt` causes any threads in the middle of `wait` to avoid going to sleep,
+// and the subsequent `wake` may wake up a thread that's actually blocking. We
+// consider this a spurious wakeup, though, which all users of condition
+// variables must already be prepared to handle. As a result, this source of
+// spurious wakeups is currently though to be ok, although it may be problematic
+// later on if it causes too many spurious wakeups.
+
+impl Condvar {
+    pub const fn new() -> Condvar {
+        Condvar { cnt: AtomicUsize::new(0) }
+    }
+
+    #[inline]
+    pub unsafe fn init(&mut self) {
+        // nothing to do
+    }
+
+    pub unsafe fn notify_one(&self) {
+        self.cnt.fetch_add(1, SeqCst);
+        // SAFETY: ptr() is always valid
+        unsafe {
+            wasm32::memory_atomic_notify(self.ptr(), 1);
+        }
+    }
+
+    #[inline]
+    pub unsafe fn notify_all(&self) {
+        self.cnt.fetch_add(1, SeqCst);
+        // SAFETY: ptr() is always valid
+        unsafe {
+            wasm32::memory_atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone"
+        }
+    }
+
+    pub unsafe fn wait(&self, mutex: &Mutex) {
+        // "atomically block and unlock" implemented by loading our current
+        // counter's value, unlocking the mutex, and blocking if the counter
+        // still has the same value.
+        //
+        // Notifications happen by incrementing the counter and then waking a
+        // thread. Incrementing the counter after we unlock the mutex will
+        // prevent us from sleeping and otherwise the call to `wake` will
+        // wake us up once we're asleep.
+        let ticket = self.cnt.load(SeqCst) as i32;
+        mutex.unlock();
+        let val = wasm32::memory_atomic_wait32(self.ptr(), ticket, -1);
+        // 0 == woken, 1 == not equal to `ticket`, 2 == timeout (shouldn't happen)
+        debug_assert!(val == 0 || val == 1);
+        mutex.lock();
+    }
+
+    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+        let ticket = self.cnt.load(SeqCst) as i32;
+        mutex.unlock();
+        let nanos = dur.as_nanos();
+        let nanos = cmp::min(i64::MAX as u128, nanos);
+
+        // If the return value is 2 then a timeout happened, so we return
+        // `false` as we weren't actually notified.
+        let ret = wasm32::memory_atomic_wait32(self.ptr(), ticket, nanos as i64) != 2;
+        mutex.lock();
+        return ret;
+    }
+
+    #[inline]
+    pub unsafe fn destroy(&self) {
+        // nothing to do
+    }
+
+    #[inline]
+    fn ptr(&self) -> *mut i32 {
+        assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>());
+        self.cnt.as_mut_ptr() as *mut i32
+    }
+}
diff --git a/library/std/src/sys/wasm/atomics/futex.rs b/library/std/src/sys/wasm/atomics/futex.rs
new file mode 100644 (file)
index 0000000..3d8bf42
--- /dev/null
@@ -0,0 +1,17 @@
+use crate::arch::wasm32;
+use crate::convert::TryInto;
+use crate::sync::atomic::AtomicI32;
+use crate::time::Duration;
+
+pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) {
+    let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1);
+    unsafe {
+        wasm32::memory_atomic_wait32(futex as *const AtomicI32 as *mut i32, expected, timeout);
+    }
+}
+
+pub fn futex_wake(futex: &AtomicI32) {
+    unsafe {
+        wasm32::memory_atomic_notify(futex as *const AtomicI32 as *mut i32, 1);
+    }
+}
diff --git a/library/std/src/sys/wasm/atomics/mutex.rs b/library/std/src/sys/wasm/atomics/mutex.rs
new file mode 100644 (file)
index 0000000..5ff0ec0
--- /dev/null
@@ -0,0 +1,156 @@
+use crate::arch::wasm32;
+use crate::cell::UnsafeCell;
+use crate::mem;
+use crate::sync::atomic::{AtomicU32, AtomicUsize, Ordering::SeqCst};
+use crate::sys::thread;
+
+pub struct Mutex {
+    locked: AtomicUsize,
+}
+
+pub type MovableMutex = Mutex;
+
+// Mutexes have a pretty simple implementation where they contain an `i32`
+// internally that is 0 when unlocked and 1 when the mutex is locked.
+// Acquisition has a fast path where it attempts to cmpxchg the 0 to a 1, and
+// if it fails it then waits for a notification. Releasing a lock is then done
+// by swapping in 0 and then notifying any waiters, if present.
+
+impl Mutex {
+    pub const fn new() -> Mutex {
+        Mutex { locked: AtomicUsize::new(0) }
+    }
+
+    #[inline]
+    pub unsafe fn init(&mut self) {
+        // nothing to do
+    }
+
+    pub unsafe fn lock(&self) {
+        while !self.try_lock() {
+            // SAFETY: the caller must uphold the safety contract for `memory_atomic_wait32`.
+            let val = unsafe {
+                wasm32::memory_atomic_wait32(
+                    self.ptr(),
+                    1,  // we expect our mutex is locked
+                    -1, // wait infinitely
+                )
+            };
+            // we should have either woke up (0) or got a not-equal due to a
+            // race (1). We should never time out (2)
+            debug_assert!(val == 0 || val == 1);
+        }
+    }
+
+    pub unsafe fn unlock(&self) {
+        let prev = self.locked.swap(0, SeqCst);
+        debug_assert_eq!(prev, 1);
+        wasm32::memory_atomic_notify(self.ptr(), 1); // wake up one waiter, if any
+    }
+
+    #[inline]
+    pub unsafe fn try_lock(&self) -> bool {
+        self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok()
+    }
+
+    #[inline]
+    pub unsafe fn destroy(&self) {
+        // nothing to do
+    }
+
+    #[inline]
+    fn ptr(&self) -> *mut i32 {
+        assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>());
+        self.locked.as_mut_ptr() as *mut i32
+    }
+}
+
+pub struct ReentrantMutex {
+    owner: AtomicU32,
+    recursions: UnsafeCell<u32>,
+}
+
+unsafe impl Send for ReentrantMutex {}
+unsafe impl Sync for ReentrantMutex {}
+
+// Reentrant mutexes are similarly implemented to mutexs above except that
+// instead of "1" meaning unlocked we use the id of a thread to represent
+// whether it has locked a mutex. That way we have an atomic counter which
+// always holds the id of the thread that currently holds the lock (or 0 if the
+// lock is unlocked).
+//
+// Once a thread acquires a lock recursively, which it detects by looking at
+// the value that's already there, it will update a local `recursions` counter
+// in a nonatomic fashion (as we hold the lock). The lock is then fully
+// released when this recursion counter reaches 0.
+
+impl ReentrantMutex {
+    pub const unsafe fn uninitialized() -> ReentrantMutex {
+        ReentrantMutex { owner: AtomicU32::new(0), recursions: UnsafeCell::new(0) }
+    }
+
+    pub unsafe fn init(&self) {
+        // nothing to do...
+    }
+
+    pub unsafe fn lock(&self) {
+        let me = thread::my_id();
+        while let Err(owner) = self._try_lock(me) {
+            // SAFETY: the caller must gurantee that `self.ptr()` and `owner` are valid i32.
+            let val = unsafe { wasm32::memory_atomic_wait32(self.ptr(), owner as i32, -1) };
+            debug_assert!(val == 0 || val == 1);
+        }
+    }
+
+    #[inline]
+    pub unsafe fn try_lock(&self) -> bool {
+        unsafe { self._try_lock(thread::my_id()).is_ok() }
+    }
+
+    #[inline]
+    unsafe fn _try_lock(&self, id: u32) -> Result<(), u32> {
+        let id = id.checked_add(1).unwrap();
+        match self.owner.compare_exchange(0, id, SeqCst, SeqCst) {
+            // we transitioned from unlocked to locked
+            Ok(_) => {
+                debug_assert_eq!(*self.recursions.get(), 0);
+                Ok(())
+            }
+
+            // we currently own this lock, so let's update our count and return
+            // true.
+            Err(n) if n == id => {
+                *self.recursions.get() += 1;
+                Ok(())
+            }
+
+            // Someone else owns the lock, let our caller take care of it
+            Err(other) => Err(other),
+        }
+    }
+
+    pub unsafe fn unlock(&self) {
+        // If we didn't ever recursively lock the lock then we fully unlock the
+        // mutex and wake up a waiter, if any. Otherwise we decrement our
+        // recursive counter and let some one else take care of the zero.
+        match *self.recursions.get() {
+            0 => {
+                self.owner.swap(0, SeqCst);
+                // SAFETY: the caller must gurantee that `self.ptr()` is valid i32.
+                unsafe {
+                    wasm32::memory_atomic_notify(self.ptr() as *mut i32, 1);
+                } // wake up one waiter, if any
+            }
+            ref mut n => *n -= 1,
+        }
+    }
+
+    pub unsafe fn destroy(&self) {
+        // nothing to do...
+    }
+
+    #[inline]
+    fn ptr(&self) -> *mut i32 {
+        self.owner.as_mut_ptr() as *mut i32
+    }
+}
diff --git a/library/std/src/sys/wasm/atomics/rwlock.rs b/library/std/src/sys/wasm/atomics/rwlock.rs
new file mode 100644 (file)
index 0000000..06442e9
--- /dev/null
@@ -0,0 +1,144 @@
+use crate::cell::UnsafeCell;
+use crate::sys::condvar::Condvar;
+use crate::sys::mutex::Mutex;
+
+pub struct RWLock {
+    lock: Mutex,
+    cond: Condvar,
+    state: UnsafeCell<State>,
+}
+
+enum State {
+    Unlocked,
+    Reading(usize),
+    Writing,
+}
+
+unsafe impl Send for RWLock {}
+unsafe impl Sync for RWLock {}
+
+// This rwlock implementation is a relatively simple implementation which has a
+// condition variable for readers/writers as well as a mutex protecting the
+// internal state of the lock. A current downside of the implementation is that
+// unlocking the lock will notify *all* waiters rather than just readers or just
+// writers. This can cause lots of "thundering stampede" problems. While
+// hopefully correct this implementation is very likely to want to be changed in
+// the future.
+
+impl RWLock {
+    pub const fn new() -> RWLock {
+        RWLock { lock: Mutex::new(), cond: Condvar::new(), state: UnsafeCell::new(State::Unlocked) }
+    }
+
+    #[inline]
+    pub unsafe fn read(&self) {
+        self.lock.lock();
+        while !(*self.state.get()).inc_readers() {
+            self.cond.wait(&self.lock);
+        }
+        self.lock.unlock();
+    }
+
+    #[inline]
+    pub unsafe fn try_read(&self) -> bool {
+        self.lock.lock();
+        let ok = (*self.state.get()).inc_readers();
+        self.lock.unlock();
+        return ok;
+    }
+
+    #[inline]
+    pub unsafe fn write(&self) {
+        self.lock.lock();
+        while !(*self.state.get()).inc_writers() {
+            self.cond.wait(&self.lock);
+        }
+        self.lock.unlock();
+    }
+
+    #[inline]
+    pub unsafe fn try_write(&self) -> bool {
+        self.lock.lock();
+        let ok = (*self.state.get()).inc_writers();
+        self.lock.unlock();
+        return ok;
+    }
+
+    #[inline]
+    pub unsafe fn read_unlock(&self) {
+        self.lock.lock();
+        let notify = (*self.state.get()).dec_readers();
+        self.lock.unlock();
+        if notify {
+            // FIXME: should only wake up one of these some of the time
+            self.cond.notify_all();
+        }
+    }
+
+    #[inline]
+    pub unsafe fn write_unlock(&self) {
+        self.lock.lock();
+        (*self.state.get()).dec_writers();
+        self.lock.unlock();
+        // FIXME: should only wake up one of these some of the time
+        self.cond.notify_all();
+    }
+
+    #[inline]
+    pub unsafe fn destroy(&self) {
+        self.lock.destroy();
+        self.cond.destroy();
+    }
+}
+
+impl State {
+    fn inc_readers(&mut self) -> bool {
+        match *self {
+            State::Unlocked => {
+                *self = State::Reading(1);
+                true
+            }
+            State::Reading(ref mut cnt) => {
+                *cnt += 1;
+                true
+            }
+            State::Writing => false,
+        }
+    }
+
+    fn inc_writers(&mut self) -> bool {
+        match *self {
+            State::Unlocked => {
+                *self = State::Writing;
+                true
+            }
+            State::Reading(_) | State::Writing => false,
+        }
+    }
+
+    fn dec_readers(&mut self) -> bool {
+        let zero = match *self {
+            State::Reading(ref mut cnt) => {
+                *cnt -= 1;
+                *cnt == 0
+            }
+            State::Unlocked | State::Writing => invalid(),
+        };
+        if zero {
+            *self = State::Unlocked;
+        }
+        zero
+    }
+
+    fn dec_writers(&mut self) {
+        match *self {
+            State::Writing => {}
+            State::Unlocked | State::Reading(_) => invalid(),
+        }
+        *self = State::Unlocked;
+    }
+}
+
+fn invalid() -> ! {
+    panic!("inconsistent rwlock");
+}
diff --git a/library/std/src/sys/wasm/atomics/thread.rs b/library/std/src/sys/wasm/atomics/thread.rs
new file mode 100644 (file)
index 0000000..54bc877
--- /dev/null
@@ -0,0 +1,84 @@
+use crate::ffi::CStr;
+use crate::io;
+use crate::sys::unsupported;
+use crate::time::Duration;
+
+pub struct Thread(!);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+impl Thread {
+    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+    pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+        unsupported()
+    }
+
+    pub fn yield_now() {}
+
+    pub fn set_name(_name: &CStr) {}
+
+    pub fn sleep(dur: Duration) {
+        use crate::arch::wasm32;
+        use crate::cmp;
+
+        // Use an atomic wait to block the current thread artificially with a
+        // timeout listed. Note that we should never be notified (return value
+        // of 0) or our comparison should never fail (return value of 1) so we
+        // should always only resume execution through a timeout (return value
+        // 2).
+        let mut nanos = dur.as_nanos();
+        while nanos > 0 {
+            let amt = cmp::min(i64::MAX as u128, nanos);
+            let mut x = 0;
+            let val = unsafe { wasm32::memory_atomic_wait32(&mut x, 0, amt as i64) };
+            debug_assert_eq!(val, 2);
+            nanos -= amt;
+        }
+    }
+
+    pub fn join(self) {}
+}
+
+pub mod guard {
+    pub type Guard = !;
+    pub unsafe fn current() -> Option<Guard> {
+        None
+    }
+    pub unsafe fn init() -> Option<Guard> {
+        None
+    }
+}
+
+// We currently just use our own thread-local to store our
+// current thread's ID, and then we lazily initialize it to something allocated
+// from a global counter.
+pub fn my_id() -> u32 {
+    use crate::sync::atomic::{AtomicU32, Ordering::SeqCst};
+
+    static NEXT_ID: AtomicU32 = AtomicU32::new(0);
+
+    #[thread_local]
+    static mut MY_ID: u32 = 0;
+
+    unsafe {
+        // If our thread ID isn't set yet then we need to allocate one. Do so
+        // with with a simple "atomically add to a global counter" strategy.
+        // This strategy doesn't handled what happens when the counter
+        // overflows, however, so just abort everything once the counter
+        // overflows and eventually we could have some sort of recycling scheme
+        // (or maybe this is all totally irrelevant by that point!). In any case
+        // though we're using a CAS loop instead of a `fetch_add` to ensure that
+        // the global counter never overflows.
+        if MY_ID == 0 {
+            let mut cur = NEXT_ID.load(SeqCst);
+            MY_ID = loop {
+                let next = cur.checked_add(1).unwrap_or_else(|| crate::process::abort());
+                match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) {
+                    Ok(_) => break next,
+                    Err(i) => cur = i,
+                }
+            };
+        }
+        MY_ID
+    }
+}
diff --git a/library/std/src/sys/wasm/condvar_atomics.rs b/library/std/src/sys/wasm/condvar_atomics.rs
deleted file mode 100644 (file)
index 0c1c076..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-use crate::arch::wasm32;
-use crate::cmp;
-use crate::mem;
-use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
-use crate::sys::mutex::Mutex;
-use crate::time::Duration;
-
-pub struct Condvar {
-    cnt: AtomicUsize,
-}
-
-pub type MovableCondvar = Condvar;
-
-// Condition variables are implemented with a simple counter internally that is
-// likely to cause spurious wakeups. Blocking on a condition variable will first
-// read the value of the internal counter, unlock the given mutex, and then
-// block if and only if the counter's value is still the same. Notifying a
-// condition variable will modify the counter (add one for now) and then wake up
-// a thread waiting on the address of the counter.
-//
-// A thread waiting on the condition variable will as a result avoid going to
-// sleep if it's notified after the lock is unlocked but before it fully goes to
-// sleep. A sleeping thread is guaranteed to be woken up at some point as it can
-// only be woken up with a call to `wake`.
-//
-// Note that it's possible for 2 or more threads to be woken up by a call to
-// `notify_one` with this implementation. That can happen where the modification
-// of `cnt` causes any threads in the middle of `wait` to avoid going to sleep,
-// and the subsequent `wake` may wake up a thread that's actually blocking. We
-// consider this a spurious wakeup, though, which all users of condition
-// variables must already be prepared to handle. As a result, this source of
-// spurious wakeups is currently though to be ok, although it may be problematic
-// later on if it causes too many spurious wakeups.
-
-impl Condvar {
-    pub const fn new() -> Condvar {
-        Condvar { cnt: AtomicUsize::new(0) }
-    }
-
-    #[inline]
-    pub unsafe fn init(&mut self) {
-        // nothing to do
-    }
-
-    pub unsafe fn notify_one(&self) {
-        self.cnt.fetch_add(1, SeqCst);
-        // SAFETY: ptr() is always valid
-        unsafe {
-            wasm32::memory_atomic_notify(self.ptr(), 1);
-        }
-    }
-
-    #[inline]
-    pub unsafe fn notify_all(&self) {
-        self.cnt.fetch_add(1, SeqCst);
-        // SAFETY: ptr() is always valid
-        unsafe {
-            wasm32::memory_atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone"
-        }
-    }
-
-    pub unsafe fn wait(&self, mutex: &Mutex) {
-        // "atomically block and unlock" implemented by loading our current
-        // counter's value, unlocking the mutex, and blocking if the counter
-        // still has the same value.
-        //
-        // Notifications happen by incrementing the counter and then waking a
-        // thread. Incrementing the counter after we unlock the mutex will
-        // prevent us from sleeping and otherwise the call to `wake` will
-        // wake us up once we're asleep.
-        let ticket = self.cnt.load(SeqCst) as i32;
-        mutex.unlock();
-        let val = wasm32::memory_atomic_wait32(self.ptr(), ticket, -1);
-        // 0 == woken, 1 == not equal to `ticket`, 2 == timeout (shouldn't happen)
-        debug_assert!(val == 0 || val == 1);
-        mutex.lock();
-    }
-
-    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
-        let ticket = self.cnt.load(SeqCst) as i32;
-        mutex.unlock();
-        let nanos = dur.as_nanos();
-        let nanos = cmp::min(i64::MAX as u128, nanos);
-
-        // If the return value is 2 then a timeout happened, so we return
-        // `false` as we weren't actually notified.
-        let ret = wasm32::memory_atomic_wait32(self.ptr(), ticket, nanos as i64) != 2;
-        mutex.lock();
-        return ret;
-    }
-
-    #[inline]
-    pub unsafe fn destroy(&self) {
-        // nothing to do
-    }
-
-    #[inline]
-    fn ptr(&self) -> *mut i32 {
-        assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>());
-        self.cnt.as_mut_ptr() as *mut i32
-    }
-}
diff --git a/library/std/src/sys/wasm/futex_atomics.rs b/library/std/src/sys/wasm/futex_atomics.rs
deleted file mode 100644 (file)
index 3d8bf42..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-use crate::arch::wasm32;
-use crate::convert::TryInto;
-use crate::sync::atomic::AtomicI32;
-use crate::time::Duration;
-
-pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) {
-    let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1);
-    unsafe {
-        wasm32::memory_atomic_wait32(futex as *const AtomicI32 as *mut i32, expected, timeout);
-    }
-}
-
-pub fn futex_wake(futex: &AtomicI32) {
-    unsafe {
-        wasm32::memory_atomic_notify(futex as *const AtomicI32 as *mut i32, 1);
-    }
-}
index afcc5ca9286d338e0633a920ce53a414658684bb..cd701a333f84cb7a9f0168770f76bc6d35e1760a 100644 (file)
@@ -17,6 +17,7 @@
 #![deny(unsafe_op_in_unsafe_fn)]
 
 pub mod alloc;
+#[path = "../unsupported/args.rs"]
 pub mod args;
 #[path = "../unix/cmath.rs"]
 pub mod cmath;
@@ -37,7 +38,6 @@
 pub mod process;
 #[path = "../unsupported/stdio.rs"]
 pub mod stdio;
-pub mod thread;
 #[path = "../unsupported/thread_local_dtor.rs"]
 pub mod thread_local_dtor;
 #[path = "../unsupported/thread_local_key.rs"]
 
 cfg_if::cfg_if! {
     if #[cfg(target_feature = "atomics")] {
-        #[path = "condvar_atomics.rs"]
+        #[path = "atomics/condvar.rs"]
         pub mod condvar;
-        #[path = "mutex_atomics.rs"]
+        #[path = "atomics/mutex.rs"]
         pub mod mutex;
-        #[path = "rwlock_atomics.rs"]
+        #[path = "atomics/rwlock.rs"]
         pub mod rwlock;
-        #[path = "futex_atomics.rs"]
+        #[path = "atomics/futex.rs"]
         pub mod futex;
+        #[path = "atomics/thread.rs"]
+        pub mod thread;
     } else {
         #[path = "../unsupported/condvar.rs"]
         pub mod condvar;
@@ -64,6 +66,8 @@
         pub mod mutex;
         #[path = "../unsupported/rwlock.rs"]
         pub mod rwlock;
+        #[path = "../unsupported/thread.rs"]
+        pub mod thread;
     }
 }
 
diff --git a/library/std/src/sys/wasm/mutex_atomics.rs b/library/std/src/sys/wasm/mutex_atomics.rs
deleted file mode 100644 (file)
index 5ff0ec0..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-use crate::arch::wasm32;
-use crate::cell::UnsafeCell;
-use crate::mem;
-use crate::sync::atomic::{AtomicU32, AtomicUsize, Ordering::SeqCst};
-use crate::sys::thread;
-
-pub struct Mutex {
-    locked: AtomicUsize,
-}
-
-pub type MovableMutex = Mutex;
-
-// Mutexes have a pretty simple implementation where they contain an `i32`
-// internally that is 0 when unlocked and 1 when the mutex is locked.
-// Acquisition has a fast path where it attempts to cmpxchg the 0 to a 1, and
-// if it fails it then waits for a notification. Releasing a lock is then done
-// by swapping in 0 and then notifying any waiters, if present.
-
-impl Mutex {
-    pub const fn new() -> Mutex {
-        Mutex { locked: AtomicUsize::new(0) }
-    }
-
-    #[inline]
-    pub unsafe fn init(&mut self) {
-        // nothing to do
-    }
-
-    pub unsafe fn lock(&self) {
-        while !self.try_lock() {
-            // SAFETY: the caller must uphold the safety contract for `memory_atomic_wait32`.
-            let val = unsafe {
-                wasm32::memory_atomic_wait32(
-                    self.ptr(),
-                    1,  // we expect our mutex is locked
-                    -1, // wait infinitely
-                )
-            };
-            // we should have either woke up (0) or got a not-equal due to a
-            // race (1). We should never time out (2)
-            debug_assert!(val == 0 || val == 1);
-        }
-    }
-
-    pub unsafe fn unlock(&self) {
-        let prev = self.locked.swap(0, SeqCst);
-        debug_assert_eq!(prev, 1);
-        wasm32::memory_atomic_notify(self.ptr(), 1); // wake up one waiter, if any
-    }
-
-    #[inline]
-    pub unsafe fn try_lock(&self) -> bool {
-        self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok()
-    }
-
-    #[inline]
-    pub unsafe fn destroy(&self) {
-        // nothing to do
-    }
-
-    #[inline]
-    fn ptr(&self) -> *mut i32 {
-        assert_eq!(mem::size_of::<usize>(), mem::size_of::<i32>());
-        self.locked.as_mut_ptr() as *mut i32
-    }
-}
-
-pub struct ReentrantMutex {
-    owner: AtomicU32,
-    recursions: UnsafeCell<u32>,
-}
-
-unsafe impl Send for ReentrantMutex {}
-unsafe impl Sync for ReentrantMutex {}
-
-// Reentrant mutexes are similarly implemented to mutexs above except that
-// instead of "1" meaning unlocked we use the id of a thread to represent
-// whether it has locked a mutex. That way we have an atomic counter which
-// always holds the id of the thread that currently holds the lock (or 0 if the
-// lock is unlocked).
-//
-// Once a thread acquires a lock recursively, which it detects by looking at
-// the value that's already there, it will update a local `recursions` counter
-// in a nonatomic fashion (as we hold the lock). The lock is then fully
-// released when this recursion counter reaches 0.
-
-impl ReentrantMutex {
-    pub const unsafe fn uninitialized() -> ReentrantMutex {
-        ReentrantMutex { owner: AtomicU32::new(0), recursions: UnsafeCell::new(0) }
-    }
-
-    pub unsafe fn init(&self) {
-        // nothing to do...
-    }
-
-    pub unsafe fn lock(&self) {
-        let me = thread::my_id();
-        while let Err(owner) = self._try_lock(me) {
-            // SAFETY: the caller must gurantee that `self.ptr()` and `owner` are valid i32.
-            let val = unsafe { wasm32::memory_atomic_wait32(self.ptr(), owner as i32, -1) };
-            debug_assert!(val == 0 || val == 1);
-        }
-    }
-
-    #[inline]
-    pub unsafe fn try_lock(&self) -> bool {
-        unsafe { self._try_lock(thread::my_id()).is_ok() }
-    }
-
-    #[inline]
-    unsafe fn _try_lock(&self, id: u32) -> Result<(), u32> {
-        let id = id.checked_add(1).unwrap();
-        match self.owner.compare_exchange(0, id, SeqCst, SeqCst) {
-            // we transitioned from unlocked to locked
-            Ok(_) => {
-                debug_assert_eq!(*self.recursions.get(), 0);
-                Ok(())
-            }
-
-            // we currently own this lock, so let's update our count and return
-            // true.
-            Err(n) if n == id => {
-                *self.recursions.get() += 1;
-                Ok(())
-            }
-
-            // Someone else owns the lock, let our caller take care of it
-            Err(other) => Err(other),
-        }
-    }
-
-    pub unsafe fn unlock(&self) {
-        // If we didn't ever recursively lock the lock then we fully unlock the
-        // mutex and wake up a waiter, if any. Otherwise we decrement our
-        // recursive counter and let some one else take care of the zero.
-        match *self.recursions.get() {
-            0 => {
-                self.owner.swap(0, SeqCst);
-                // SAFETY: the caller must gurantee that `self.ptr()` is valid i32.
-                unsafe {
-                    wasm32::memory_atomic_notify(self.ptr() as *mut i32, 1);
-                } // wake up one waiter, if any
-            }
-            ref mut n => *n -= 1,
-        }
-    }
-
-    pub unsafe fn destroy(&self) {
-        // nothing to do...
-    }
-
-    #[inline]
-    fn ptr(&self) -> *mut i32 {
-        self.owner.as_mut_ptr() as *mut i32
-    }
-}
diff --git a/library/std/src/sys/wasm/rwlock_atomics.rs b/library/std/src/sys/wasm/rwlock_atomics.rs
deleted file mode 100644 (file)
index 06442e9..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-use crate::cell::UnsafeCell;
-use crate::sys::condvar::Condvar;
-use crate::sys::mutex::Mutex;
-
-pub struct RWLock {
-    lock: Mutex,
-    cond: Condvar,
-    state: UnsafeCell<State>,
-}
-
-enum State {
-    Unlocked,
-    Reading(usize),
-    Writing,
-}
-
-unsafe impl Send for RWLock {}
-unsafe impl Sync for RWLock {}
-
-// This rwlock implementation is a relatively simple implementation which has a
-// condition variable for readers/writers as well as a mutex protecting the
-// internal state of the lock. A current downside of the implementation is that
-// unlocking the lock will notify *all* waiters rather than just readers or just
-// writers. This can cause lots of "thundering stampede" problems. While
-// hopefully correct this implementation is very likely to want to be changed in
-// the future.
-
-impl RWLock {
-    pub const fn new() -> RWLock {
-        RWLock { lock: Mutex::new(), cond: Condvar::new(), state: UnsafeCell::new(State::Unlocked) }
-    }
-
-    #[inline]
-    pub unsafe fn read(&self) {
-        self.lock.lock();
-        while !(*self.state.get()).inc_readers() {
-            self.cond.wait(&self.lock);
-        }
-        self.lock.unlock();
-    }
-
-    #[inline]
-    pub unsafe fn try_read(&self) -> bool {
-        self.lock.lock();
-        let ok = (*self.state.get()).inc_readers();
-        self.lock.unlock();
-        return ok;
-    }
-
-    #[inline]
-    pub unsafe fn write(&self) {
-        self.lock.lock();
-        while !(*self.state.get()).inc_writers() {
-            self.cond.wait(&self.lock);
-        }
-        self.lock.unlock();
-    }
-
-    #[inline]
-    pub unsafe fn try_write(&self) -> bool {
-        self.lock.lock();
-        let ok = (*self.state.get()).inc_writers();
-        self.lock.unlock();
-        return ok;
-    }
-
-    #[inline]
-    pub unsafe fn read_unlock(&self) {
-        self.lock.lock();
-        let notify = (*self.state.get()).dec_readers();
-        self.lock.unlock();
-        if notify {
-            // FIXME: should only wake up one of these some of the time
-            self.cond.notify_all();
-        }
-    }
-
-    #[inline]
-    pub unsafe fn write_unlock(&self) {
-        self.lock.lock();
-        (*self.state.get()).dec_writers();
-        self.lock.unlock();
-        // FIXME: should only wake up one of these some of the time
-        self.cond.notify_all();
-    }
-
-    #[inline]
-    pub unsafe fn destroy(&self) {
-        self.lock.destroy();
-        self.cond.destroy();
-    }
-}
-
-impl State {
-    fn inc_readers(&mut self) -> bool {
-        match *self {
-            State::Unlocked => {
-                *self = State::Reading(1);
-                true
-            }
-            State::Reading(ref mut cnt) => {
-                *cnt += 1;
-                true
-            }
-            State::Writing => false,
-        }
-    }
-
-    fn inc_writers(&mut self) -> bool {
-        match *self {
-            State::Unlocked => {
-                *self = State::Writing;
-                true
-            }
-            State::Reading(_) | State::Writing => false,
-        }
-    }
-
-    fn dec_readers(&mut self) -> bool {
-        let zero = match *self {
-            State::Reading(ref mut cnt) => {
-                *cnt -= 1;
-                *cnt == 0
-            }
-            State::Unlocked | State::Writing => invalid(),
-        };
-        if zero {
-            *self = State::Unlocked;
-        }
-        zero
-    }
-
-    fn dec_writers(&mut self) {
-        match *self {
-            State::Writing => {}
-            State::Unlocked | State::Reading(_) => invalid(),
-        }
-        *self = State::Unlocked;
-    }
-}
-
-fn invalid() -> ! {
-    panic!("inconsistent rwlock");
-}
diff --git a/library/std/src/sys/wasm/thread.rs b/library/std/src/sys/wasm/thread.rs
deleted file mode 100644 (file)
index b7bf95c..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-use crate::ffi::CStr;
-use crate::io;
-use crate::sys::unsupported;
-use crate::time::Duration;
-
-pub struct Thread(!);
-
-pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
-
-impl Thread {
-    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
-    pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
-        unsupported()
-    }
-
-    pub fn yield_now() {
-        // do nothing
-    }
-
-    pub fn set_name(_name: &CStr) {
-        // nope
-    }
-
-    #[cfg(not(target_feature = "atomics"))]
-    pub fn sleep(_dur: Duration) {
-        panic!("can't sleep");
-    }
-
-    #[cfg(target_feature = "atomics")]
-    pub fn sleep(dur: Duration) {
-        use crate::arch::wasm32;
-        use crate::cmp;
-
-        // Use an atomic wait to block the current thread artificially with a
-        // timeout listed. Note that we should never be notified (return value
-        // of 0) or our comparison should never fail (return value of 1) so we
-        // should always only resume execution through a timeout (return value
-        // 2).
-        let mut nanos = dur.as_nanos();
-        while nanos > 0 {
-            let amt = cmp::min(i64::MAX as u128, nanos);
-            let mut x = 0;
-            let val = unsafe { wasm32::memory_atomic_wait32(&mut x, 0, amt as i64) };
-            debug_assert_eq!(val, 2);
-            nanos -= amt;
-        }
-    }
-
-    pub fn join(self) {
-        self.0
-    }
-}
-
-pub mod guard {
-    pub type Guard = !;
-    pub unsafe fn current() -> Option<Guard> {
-        None
-    }
-    pub unsafe fn init() -> Option<Guard> {
-        None
-    }
-}
-
-// This is only used by atomics primitives when the `atomics` feature is
-// enabled. In that mode we currently just use our own thread-local to store our
-// current thread's ID, and then we lazily initialize it to something allocated
-// from a global counter.
-#[cfg(target_feature = "atomics")]
-pub fn my_id() -> u32 {
-    use crate::sync::atomic::{AtomicU32, Ordering::SeqCst};
-
-    static NEXT_ID: AtomicU32 = AtomicU32::new(0);
-
-    #[thread_local]
-    static mut MY_ID: u32 = 0;
-
-    unsafe {
-        // If our thread ID isn't set yet then we need to allocate one. Do so
-        // with with a simple "atomically add to a global counter" strategy.
-        // This strategy doesn't handled what happens when the counter
-        // overflows, however, so just abort everything once the counter
-        // overflows and eventually we could have some sort of recycling scheme
-        // (or maybe this is all totally irrelevant by that point!). In any case
-        // though we're using a CAS loop instead of a `fetch_add` to ensure that
-        // the global counter never overflows.
-        if MY_ID == 0 {
-            let mut cur = NEXT_ID.load(SeqCst);
-            MY_ID = loop {
-                let next = cur.checked_add(1).unwrap_or_else(|| crate::process::abort());
-                match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) {
-                    Ok(_) => break next,
-                    Err(i) => cur = i,
-                }
-            };
-        }
-        MY_ID
-    }
-}
index 80e6798d847b1d2faed5b2bbbb1493357d60e835..f33d6129619318e8774303d9b77b930de34093f5 100644 (file)
@@ -1,4 +1,5 @@
 use crate::cell::{Cell, UnsafeCell};
+use crate::sync::atomic::{AtomicU8, Ordering};
 use crate::sync::mpsc::{channel, Sender};
 use crate::thread::{self, LocalKey};
 use crate::thread_local;
@@ -207,3 +208,110 @@ fn drop(&mut self) {
     });
     rx.recv().unwrap();
 }
+
+// This test tests that TLS destructors have run before the thread joins. The
+// test has no false positives (meaning: if the test fails, there's actually
+// an ordering problem). It may have false negatives, where the test passes but
+// join is not guaranteed to be after the TLS destructors. However, false
+// negatives should be exceedingly rare due to judicious use of
+// thread::yield_now and running the test several times.
+#[test]
+fn join_orders_after_tls_destructors() {
+    // We emulate a synchronous MPSC rendezvous channel using only atomics and
+    // thread::yield_now. We can't use std::mpsc as the implementation itself
+    // may rely on thread locals.
+    //
+    // The basic state machine for an SPSC rendezvous channel is:
+    //           FRESH -> THREAD1_WAITING -> MAIN_THREAD_RENDEZVOUS
+    // where the first transition is done by the “receiving” thread and the 2nd
+    // transition is done by the “sending” thread.
+    //
+    // We add an additional state `THREAD2_LAUNCHED` between `FRESH` and
+    // `THREAD1_WAITING` to block until all threads are actually running.
+    //
+    // A thread that joins on the “receiving” thread completion should never
+    // observe the channel in the `THREAD1_WAITING` state. If this does occur,
+    // we switch to the “poison” state `THREAD2_JOINED` and panic all around.
+    // (This is equivalent to “sending” from an alternate producer thread.)
+    const FRESH: u8 = 0;
+    const THREAD2_LAUNCHED: u8 = 1;
+    const THREAD1_WAITING: u8 = 2;
+    const MAIN_THREAD_RENDEZVOUS: u8 = 3;
+    const THREAD2_JOINED: u8 = 4;
+    static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH);
+
+    for _ in 0..10 {
+        SYNC_STATE.store(FRESH, Ordering::SeqCst);
+
+        let jh = thread::Builder::new()
+            .name("thread1".into())
+            .spawn(move || {
+                struct TlDrop;
+
+                impl Drop for TlDrop {
+                    fn drop(&mut self) {
+                        let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::SeqCst);
+                        loop {
+                            match sync_state {
+                                THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(),
+                                MAIN_THREAD_RENDEZVOUS => break,
+                                THREAD2_JOINED => panic!(
+                                    "Thread 1 still running after thread 2 joined on thread 1"
+                                ),
+                                v => unreachable!("sync state: {}", v),
+                            }
+                            sync_state = SYNC_STATE.load(Ordering::SeqCst);
+                        }
+                    }
+                }
+
+                thread_local! {
+                    static TL_DROP: TlDrop = TlDrop;
+                }
+
+                TL_DROP.with(|_| {});
+
+                loop {
+                    match SYNC_STATE.load(Ordering::SeqCst) {
+                        FRESH => thread::yield_now(),
+                        THREAD2_LAUNCHED => break,
+                        v => unreachable!("sync state: {}", v),
+                    }
+                }
+            })
+            .unwrap();
+
+        let jh2 = thread::Builder::new()
+            .name("thread2".into())
+            .spawn(move || {
+                assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::SeqCst), FRESH);
+                jh.join().unwrap();
+                match SYNC_STATE.swap(THREAD2_JOINED, Ordering::SeqCst) {
+                    MAIN_THREAD_RENDEZVOUS => return,
+                    THREAD2_LAUNCHED | THREAD1_WAITING => {
+                        panic!("Thread 2 running after thread 1 join before main thread rendezvous")
+                    }
+                    v => unreachable!("sync state: {:?}", v),
+                }
+            })
+            .unwrap();
+
+        loop {
+            match SYNC_STATE.compare_exchange_weak(
+                THREAD1_WAITING,
+                MAIN_THREAD_RENDEZVOUS,
+                Ordering::SeqCst,
+                Ordering::SeqCst,
+            ) {
+                Ok(_) => break,
+                Err(FRESH) => thread::yield_now(),
+                Err(THREAD2_LAUNCHED) => thread::yield_now(),
+                Err(THREAD2_JOINED) => {
+                    panic!("Main thread rendezvous after thread 2 joined thread 1")
+                }
+                v => unreachable!("sync state: {:?}", v),
+            }
+        }
+        jh2.join().unwrap();
+    }
+}
index 19f5459dd0f89e466b7bcaa0f69ecca90f21a4d1..6c4f4e1990b76be8a07bde1956d2e3452fd55ee4 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 19f5459dd0f89e466b7bcaa0f69ecca90f21a4d1
+Subproject commit 6c4f4e1990b76be8a07bde1956d2e3452fd55ee4
index 356d9f5d1ff27eb16b382a2be1b6371083ec41fc..149a899cef7a0cf615c88dfa94c6e3443af4d26d 100644 (file)
@@ -900,8 +900,8 @@ class RustBuild(object):
         target_linker = self.get_toml("linker", build_section)
         if target_linker is not None:
             env["RUSTFLAGS"] += " -C linker=" + target_linker
-        # cfg(bootstrap): Add `-Wsemicolon_in_expressions_from_macros` after the next beta bump
         env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
+        env["RUSTFLAGS"] += " -Wsemicolon_in_expressions_from_macros"
         if self.get_toml("deny-warnings", "rust") != "false":
             env["RUSTFLAGS"] += " -Dwarnings"
 
index 62a3a87eeb850a2b425e18a31fabfddc310cc901..789267aa880fa5415c8aed425cb50a9798f21069 100644 (file)
@@ -377,6 +377,8 @@ macro_rules! describe {
                 check::Rustdoc,
                 check::CodegenBackend,
                 check::Clippy,
+                check::Miri,
+                check::Rls,
                 check::Bootstrap
             ),
             Kind::Test => describe!(
@@ -1270,12 +1272,7 @@ pub fn cargo(
             // some code doesn't go through this `rustc` wrapper.
             lint_flags.push("-Wrust_2018_idioms");
             lint_flags.push("-Wunused_lifetimes");
-            // cfg(bootstrap): unconditionally enable this warning after the next beta bump
-            // This is currently disabled for the stage1 libstd, since build scripts
-            // will end up using the bootstrap compiler (which doesn't yet support this lint)
-            if compiler.stage != 0 && mode != Mode::Std {
-                lint_flags.push("-Wsemicolon_in_expressions_from_macros");
-            }
+            lint_flags.push("-Wsemicolon_in_expressions_from_macros");
 
             if self.config.deny_warnings {
                 lint_flags.push("-Dwarnings");
index a881512e988ed67dd4e3cb4fee96675529296259..4d7c207e3ab8b3cfe60b8c33b52e8e1c3101531b 100644 (file)
@@ -489,6 +489,7 @@ fn test_with_no_doc_stage0() {
             compare_mode: None,
             rustfix_coverage: false,
             pass: None,
+            run: None,
         };
 
         let build = Build::new(config);
@@ -529,6 +530,7 @@ fn test_exclude() {
             compare_mode: None,
             rustfix_coverage: false,
             pass: None,
+            run: None,
         };
 
         let build = Build::new(config);
@@ -584,6 +586,7 @@ fn test_docs() {
             compare_mode: None,
             rustfix_coverage: false,
             pass: None,
+            run: None,
         };
         // Make sure rustfmt binary not being found isn't an error.
         config.channel = "beta".to_string();
index 9b76c8b9a2d2643bf6f3bbf13197ecd63fc2d62c..3e9d921d0f52616b35b776cc5bcf00bf09389c89 100644 (file)
@@ -280,7 +280,7 @@ fn run(self, builder: &Builder<'_>) {
 }
 
 macro_rules! tool_check_step {
-    ($name:ident, $path:literal, $($alias:literal, )* $source_type:path) => {
+    ($name:ident, $path:literal, $($alias:literal, )* $source_type:path $(, $default:literal )?) => {
         #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
         pub struct $name {
             pub target: TargetSelection,
@@ -289,7 +289,8 @@ pub struct $name {
         impl Step for $name {
             type Output = ();
             const ONLY_HOSTS: bool = true;
-            const DEFAULT: bool = true;
+            // don't ever check out-of-tree tools by default, they'll fail when toolstate is broken
+            const DEFAULT: bool = matches!($source_type, SourceType::InTree) $( && $default )?;
 
             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                 run.paths(&[ $path, $($alias),* ])
@@ -367,8 +368,10 @@ fn stamp(
 // behavior, treat it as in-tree so that any new warnings in clippy will be
 // rejected.
 tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree);
+tool_check_step!(Miri, "src/tools/miri", SourceType::Submodule);
+tool_check_step!(Rls, "src/tools/rls", SourceType::Submodule);
 
-tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree);
+tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree, false);
 
 /// Cargo's output path for the standard library in a given stage, compiled
 /// by a particular compiler for the specified target.
index 66a88e85fea39e7152ca79ba75cfe0d53457f316..2676b3bf8e0059e467fa00c9779711ec2c368f84 100644 (file)
@@ -648,6 +648,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
     }
     if builder.config.rustc_parallel {
         cargo.rustflag("--cfg=parallel_compiler");
+        cargo.rustdocflag("--cfg=parallel_compiler");
     }
     if builder.config.rust_verify_llvm_ir {
         cargo.env("RUSTC_VERIFY_LLVM_IR", "1");
index 6044899c237e22b0bdd027258e2ba353a59793e8..80a60c79edfe4c0801e54eb59a6fd2ce8d3e8d5c 100644 (file)
@@ -91,6 +91,7 @@ pub enum Subcommand {
         paths: Vec<PathBuf>,
     },
     Format {
+        paths: Vec<PathBuf>,
         check: bool,
     },
     Doc {
@@ -103,6 +104,7 @@ pub enum Subcommand {
         bless: bool,
         compare_mode: Option<String>,
         pass: Option<String>,
+        run: Option<String>,
         test_args: Vec<String>,
         rustc_args: Vec<String>,
         fail_fast: bool,
@@ -222,8 +224,8 @@ pub fn parse(args: &[String]) -> Flags {
              VALUE overrides the skip-rebuild option in config.toml.",
             "VALUE",
         );
-        opts.optopt("", "rust-profile-generate", "rustc error format", "FORMAT");
-        opts.optopt("", "rust-profile-use", "rustc error format", "FORMAT");
+        opts.optopt("", "rust-profile-generate", "generate PGO profile with rustc build", "FORMAT");
+        opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "FORMAT");
 
         // We can't use getopt to parse the options until we have completed specifying which
         // options are valid, but under the current implementation, some options are conditional on
@@ -293,6 +295,7 @@ pub fn parse(args: &[String]) -> Flags {
                     "force {check,build,run}-pass tests to this mode.",
                     "check | build | run",
                 );
+                opts.optopt("", "run", "whether to execute run-* tests", "auto | always | never");
                 opts.optflag(
                     "",
                     "rustfix-coverage",
@@ -556,6 +559,7 @@ pub fn parse(args: &[String]) -> Flags {
                 bless: matches.opt_present("bless"),
                 compare_mode: matches.opt_str("compare-mode"),
                 pass: matches.opt_str("pass"),
+                run: matches.opt_str("run"),
                 test_args: matches.opt_strs("test-args"),
                 rustc_args: matches.opt_strs("rustc-args"),
                 fail_fast: !matches.opt_present("no-fail-fast"),
@@ -578,7 +582,7 @@ pub fn parse(args: &[String]) -> Flags {
 
                 Subcommand::Clean { all: matches.opt_present("all") }
             }
-            "fmt" => Subcommand::Format { check: matches.opt_present("check") },
+            "fmt" => Subcommand::Format { check: matches.opt_present("check"), paths },
             "dist" => Subcommand::Dist { paths },
             "install" => Subcommand::Install { paths },
             "run" | "r" => {
@@ -742,6 +746,13 @@ pub fn pass(&self) -> Option<&str> {
         }
     }
 
+    pub fn run(&self) -> Option<&str> {
+        match *self {
+            Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
+            _ => None,
+        }
+    }
+
     pub fn open(&self) -> bool {
         match *self {
             Subcommand::Doc { open, .. } => open,
index d21e3408144fea607c598ea6695d479c58e28539..2408344487bb1f8044b9d601bd8047dcf0e7f661 100644 (file)
@@ -42,7 +42,7 @@ struct RustfmtConfig {
     ignore: Vec<String>,
 }
 
-pub fn format(build: &Build, check: bool) {
+pub fn format(build: &Build, check: bool, paths: &[PathBuf]) {
     if build.config.dry_run {
         return;
     }
@@ -118,8 +118,19 @@ pub fn format(build: &Build, check: bool) {
         .to_path_buf();
     let src = build.src.clone();
     let (tx, rx): (SyncSender<PathBuf>, _) = std::sync::mpsc::sync_channel(128);
-    let walker =
-        WalkBuilder::new(src.clone()).types(matcher).overrides(ignore_fmt).build_parallel();
+    let walker = match paths.get(0) {
+        Some(first) => {
+            let mut walker = WalkBuilder::new(first);
+            for path in &paths[1..] {
+                walker.add(path);
+            }
+            walker
+        }
+        None => WalkBuilder::new(src.clone()),
+    }
+    .types(matcher)
+    .overrides(ignore_fmt)
+    .build_parallel();
 
     // there is a lot of blocking involved in spawning a child process and reading files to format.
     // spawn more processes than available concurrency to keep the CPU busy
index 24da44b933abc9c8a6b92185a9011b797e42d1c3..2960dd3df6bf48b81a98cbd0ee8c80e42a6176f2 100644 (file)
@@ -478,8 +478,8 @@ pub fn build(&mut self) {
             job::setup(self);
         }
 
-        if let Subcommand::Format { check } = self.config.cmd {
-            return format::format(self, check);
+        if let Subcommand::Format { check, paths } = &self.config.cmd {
+            return format::format(self, *check, &paths);
         }
 
         if let Subcommand::Clean { all } = self.config.cmd {
index db443756de3d007a8fd3b86e0f0c09ceeb611d78..78163651158ed3452bb5d42640722a55787e5a06 100644 (file)
@@ -183,6 +183,7 @@ fn run(self, builder: &Builder<'_>) {
             builder,
             cmd.arg(&cargo)
                 .arg(&out_dir)
+                .args(builder.config.cmd.test_args())
                 .env("RUSTC", builder.rustc(compiler))
                 .env("RUSTDOC", builder.rustdoc(compiler)),
         );
@@ -830,23 +831,14 @@ fn run(self, builder: &Builder<'_>) {
             command.arg("src/test/rustdoc-gui/lib.rs").arg("-o").arg(&out_dir);
             builder.run(&mut command);
 
-            for file in fs::read_dir("src/test/rustdoc-gui").unwrap() {
-                let file = file.unwrap();
-                let file_path = file.path();
-                let file_name = file.file_name();
-
-                if !file_name.to_str().unwrap().ends_with(".goml") {
-                    continue;
-                }
-                let mut command = Command::new(&nodejs);
-                command
-                    .arg("src/tools/rustdoc-gui/tester.js")
-                    .arg("--doc-folder")
-                    .arg(out_dir.join("test_docs"))
-                    .arg("--test-file")
-                    .arg(file_path);
-                builder.run(&mut command);
-            }
+            let mut command = Command::new(&nodejs);
+            command
+                .arg("src/tools/rustdoc-gui/tester.js")
+                .arg("--doc-folder")
+                .arg(out_dir.join("test_docs"))
+                .arg("--tests-folder")
+                .arg("src/test/rustdoc-gui");
+            builder.run(&mut command);
         } else {
             builder.info("No nodejs found, skipping \"src/test/rustdoc-gui\" tests");
         }
@@ -897,7 +889,7 @@ fn run(self, builder: &Builder<'_>) {
                 );
                 std::process::exit(1);
             }
-            crate::format::format(&builder.build, !builder.config.cmd.bless());
+            crate::format::format(&builder.build, !builder.config.cmd.bless(), &[]);
         }
     }
 
@@ -1240,6 +1232,11 @@ fn run(self, builder: &Builder<'_>) {
             cmd.arg(pass);
         }
 
+        if let Some(ref run) = builder.config.cmd.run() {
+            cmd.arg("--run");
+            cmd.arg(run);
+        }
+
         if let Some(ref nodejs) = builder.config.nodejs {
             cmd.arg("--nodejs").arg(nodejs);
         }
index ad2a8c771de9caed4c7a9106d0d51420e1b2aa77..c3c717266db4564c673325d7e8ace9d9b19cd3e0 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=9442def56a39d742bf27ebcc3e0614cf117e1bc2
+    local PERF=1e19fc4c6168d2f7596e512f42f358f245d8f09d
     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 8a30acc2e460f0159e66f09c3d052284e3e8d4e1..7b540b5c6e99dd2bc0f274df220bef1fb13f983a 100755 (executable)
@@ -18,7 +18,8 @@ if isMacOS; then
         bindir="$(xcode-select --print-path)/Toolchains/XcodeDefault.xctoolchain/usr/bin"
     else
         file="${MIRRORS_BASE}/clang%2Bllvm-${LLVM_VERSION}-x86_64-apple-darwin.tar.xz"
-        curl -f "${file}" | tar xJf -
+        retry curl -f "${file}" -o "clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin.tar.xz"
+        tar xJf "clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin.tar.xz"
         bindir="$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin/bin"
     fi
 
@@ -48,7 +49,8 @@ elif isWindows && [[ ${CUSTOM_MINGW-0} -ne 1 ]]; then
 
     mkdir -p citools/clang-rust
     cd citools
-    curl -f "${MIRRORS_BASE}/LLVM-${LLVM_VERSION}-win64.exe" -o "LLVM-${LLVM_VERSION}-win64.exe"
+    retry curl -f "${MIRRORS_BASE}/LLVM-${LLVM_VERSION}-win64.exe" \
+        -o "LLVM-${LLVM_VERSION}-win64.exe"
     7z x -oclang-rust/ "LLVM-${LLVM_VERSION}-win64.exe"
     ciCommandSetEnv RUST_CONFIGURE_ARGS \
         "${RUST_CONFIGURE_ARGS} --set llvm.clang-cl=$(pwd)/clang-rust/bin/clang-cl.exe"
diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-coverage.md b/src/doc/unstable-book/src/compiler-flags/instrument-coverage.md
new file mode 100644 (file)
index 0000000..f7c2a26
--- /dev/null
@@ -0,0 +1,346 @@
+# `instrument-coverage`
+
+The tracking issue for this feature is: [#79121].
+
+[#79121]: https://github.com/rust-lang/rust/issues/79121
+
+---
+
+## Introduction
+
+The Rust compiler includes two code coverage implementations:
+
+-   A GCC-compatible, gcov-based coverage implementation, enabled with `-Z profile`, which derives coverage data based on DebugInfo.
+-   A source-based code coverage implementation, enabled with `-Z instrument-coverage`, which uses LLVM's native, efficient coverage instrumentation to generate very precise coverage data.
+
+This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-Z instrument-coverage` compiler flag.
+
+## How it works
+
+When `-Z instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by:
+
+-   Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed.
+-   Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 4_, supported _only_ in LLVM 11 and up), to define the code regions (start and end positions in the source code) being counted.
+
+When running a coverage-instrumented program, the counter values are written to a `profraw` file at program termination. LLVM bundles tools that read the counter results, combine those results with the coverage map (embedded in the program binary), and generate coverage reports in multiple formats.
+
+[`llvm.instrprof.increment`]: https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic
+[llvm code coverage mapping format]: https://llvm.org/docs/CoverageMappingFormat.html
+
+> **Note**: `-Z instrument-coverage` also automatically enables `-Z symbol-mangling-version=v0` (tracking issue [#60705]). The `v0` symbol mangler is strongly recommended, but be aware that this demangler is also experimental. The `v0` demangler can be overridden by explicitly adding `-Z symbol-mangling-version=legacy`.
+
+[#60705]: https://github.com/rust-lang/rust/issues/60705
+
+## Enable coverage profiling in the Rust compiler
+
+Rust's source-based code coverage requires the Rust "profiler runtime". Without it, compiling with `-Z instrument-coverage` generates an error that the profiler runtime is missing.
+
+The Rust `nightly` distribution channel includes the profiler runtime, by default.
+
+> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.<triple>]`):
+>
+> ```toml
+> # Build the profiler runtime (required when compiling with options that depend
+> # on this runtime, such as `-C profile-generate` or `-Z  instrument-coverage`).
+> profiler = true
+> ```
+
+### Building the demangler
+
+LLVM coverage reporting tools generate results that can include function names and other symbol references, and the raw coverage results report symbols using the compiler's "mangled" version of the symbol names, which can be difficult to interpret. To work around this issue, LLVM coverage tools also support a user-specified symbol name demangler.
+
+One option for a Rust demangler is [`rustfilt`], which can be installed with:
+
+```shell
+cargo install rustfilt
+```
+
+Another option, if you are building from the Rust compiler source distribution, is to use the `rust-demangler` tool included in the Rust source distribution, which can be built with:
+
+```shell
+$ ./x.py build rust-demangler
+```
+
+[`rustfilt`]: https://crates.io/crates/rustfilt
+
+## Compiling with coverage enabled
+
+Set the `-Z instrument-coverage` compiler flag in order to enable LLVM source-based code coverage profiling.
+
+The default option generates coverage for all functions, including unused (never called) functions and generics. The compiler flag supports an optional value to tailor this behavior. (See [`-Z instrument-coverage=<options>`](#-z-instrument-coverageoptions), below.)
+
+With `cargo`, you can instrument your program binary _and_ dependencies at the same time.
+
+For example (if your project's Cargo.toml builds a binary by default):
+
+```shell
+$ cd your-project
+$ cargo clean
+$ RUSTFLAGS="-Z instrument-coverage" cargo build
+```
+
+If `cargo` is not configured to use your `profiler`-enabled version of `rustc`, set the path explicitly via the `RUSTC` environment variable. Here is another example, using a `stage1` build of `rustc` to compile an `example` binary (from the [`json5format`] crate):
+
+```shell
+$ RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc \
+    RUSTFLAGS="-Z instrument-coverage" \
+    cargo build --example formatjson5
+```
+
+> **Note**: that some compiler options, combined with `-Z instrument-coverage`, can produce LLVM IR and/or linked binaries that are incompatible with LLVM coverage maps. For example, coverage requires references to actual functions in LLVM IR. If any covered function is optimized out, the coverage tools may not be able to process the coverage results. If you need to pass additional options, with coverage enabled, test them early, to confirm you will get the coverage results you expect.
+
+## Running the instrumented binary to generate raw coverage profiling data
+
+In the previous example, `cargo` generated the coverage-instrumented binary `formatjson5`:
+
+```shell
+$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 -
+```
+
+```json5
+{
+    some: "thing",
+}
+```
+
+After running this program, a new file, `default.profraw`, should be in the current working directory. It's often preferable to set a specific file name or path. You can change the output file using the environment variable `LLVM_PROFILE_FILE`:
+
+```shell
+$ echo "{some: 'thing'}" \
+    | LLVM_PROFILE_FILE="formatjson5.profraw" target/debug/examples/formatjson5 -
+...
+$ ls formatjson5.profraw
+formatjson5.profraw
+```
+
+If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing directory structure will be created. Additionally, the following special pattern strings are rewritten:
+
+-   `%p` - The process ID.
+-   `%h` - The hostname of the machine running the program.
+-   `%t` - The value of the TMPDIR environment variable.
+-   `%Nm` - the instrumented binary’s signature: The runtime creates a pool of N raw profiles, used for on-line profile merging. The runtime takes care of selecting a raw profile from the pool, locking it, and updating it before the program exits. `N` must be between `1` and `9`, and defaults to `1` if omitted (with simply `%m`).
+-   `%c` - Does not add anything to the filename, but enables a mode (on some platforms, including Darwin) in which profile counter updates are continuously synced to a file. This means that if the instrumented program crashes, or is killed by a signal, perfect coverage information can still be recovered.
+
+## Installing LLVM coverage tools
+
+LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 11 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.):
+
+-   The LLVM tools may be installed (or installable) directly to your OS (such as via `apt-get`, for Linux).
+-   If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`.
+-   You can install compatible versions of these tools via `rustup`.
+
+The `rustup` option is guaranteed to install a compatible version of the LLVM tools, but they can be hard to find. We recommend [`cargo-binutils`], which installs Rust-specific wrappers around these and other LLVM tools, so you can invoke them via `cargo` commands!
+
+```shell
+$ rustup component add llvm-tools-preview
+$ cargo install cargo-binutils
+$ cargo profdata -- --help  # note the additional "--" preceding the tool-specific arguments
+```
+
+[`cargo-binutils`]: https://crates.io/crates/cargo-binutils
+
+## Creating coverage reports
+
+Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`] (or `cargo profdata -- merge`), which can combine multiple raw profiles and index them at the same time:
+
+```shell
+$ llvm-profdata merge -sparse formatjson5.profraw -o formatjson5.profdata
+```
+
+Finally, the `.profdata` file is used, in combination with the coverage map (from the program binary) to generate coverage reports using [`llvm-cov report`] (or `cargo cov -- report`), for a coverage summaries; and [`llvm-cov show`] (or `cargo cov -- show`), to see detailed coverage of lines and regions (character ranges) overlaid on the original source code.
+
+These commands have several display and filtering options. For example:
+
+```shell
+$ llvm-cov show -Xdemangler=rustfilt target/debug/examples/formatjson5 \
+    -instr-profile=formatjson5.profdata \
+    -show-line-counts-or-regions \
+    -show-instantiations \
+    -name=add_quoted_string
+```
+
+<img alt="Screenshot of sample `llvm-cov show` result, for function add_quoted_string" src="img/llvm-cov-show-01.png" class="center"/>
+<br/>
+<br/>
+
+Some of the more notable options in this example include:
+
+-   `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example, but this could also be a path to the `rust-demangler` tool)
+-   `target/debug/examples/formatjson5` - the instrumented binary (from which to extract the coverage map)
+-   `--instr-profile=<path-to-file>.profdata` - the location of the `.profdata` file created by `llvm-profdata merge` (from the `.profraw` file generated by the instrumented binary)
+-   `--name=<exact-function-name>` - to show coverage for a specific function (or, consider using another filter option, such as `--name-regex=<pattern>`)
+
+[`llvm-profdata merge`]: https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge
+[`llvm-cov report`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report
+[`llvm-cov show`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show
+
+> **Note**: Coverage can also be disabled on an individual function by annotating the function with the [`no_coverage` attribute] (which requires the feature flag `#![feature(no_coverage)]`).
+
+[`no_coverage` attribute]: ../language-features/no-coverage.md
+
+## Interpreting reports
+
+There are four statistics tracked in a coverage summary:
+
+-   Function coverage is the percentage of functions that have been executed at least once. A function is considered to be executed if any of its instantiations are executed.
+-   Instantiation coverage is the percentage of function instantiations that have been executed at least once. Generic functions and functions generated from macros are two kinds of functions that may have multiple instantiations.
+-   Line coverage is the percentage of code lines that have been executed at least once. Only executable lines within function bodies are considered to be code lines.
+-   Region coverage is the percentage of code regions that have been executed at least once. A code region may span multiple lines: for example, in a large function body with no control flow. In other cases, a single line can contain multiple code regions: `return x || (y && z)` has countable code regions for `x` (which may resolve the expression, if `x` is `true`), `|| (y && z)` (executed only if `x` was `false`), and `return` (executed in either situation).
+
+Of these four statistics, function coverage is usually the least granular while region coverage is the most granular. The project-wide totals for each statistic are listed in the summary.
+
+## Test coverage
+
+A typical use case for coverage analysis is test coverage. Rust's source-based coverage tools can both measure your tests' code coverage as percentage, and pinpoint functions and branches not tested.
+
+The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate.
+
+Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-Z instrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.)
+
+```shell
+$ RUSTFLAGS="-Z instrument-coverage" \
+    LLVM_PROFILE_FILE="json5format-%m.profraw" \
+    cargo test --tests
+```
+
+Make note of the test binary file paths, displayed after the word "`Running`" in the test output:
+
+```text
+   ...
+   Compiling json5format v0.1.3 ($HOME/json5format)
+    Finished test [unoptimized + debuginfo] target(s) in 14.60s
+
+     Running target/debug/deps/json5format-fececd4653271682
+running 25 tests
+...
+test result: ok. 25 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+
+     Running target/debug/deps/lib-30768f9c53506dc5
+running 31 tests
+...
+test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+```
+
+You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them:
+
+```shell
+$ cargo profdata -- merge \
+    -sparse json5format-*.profraw -o json5format.profdata
+```
+
+Then run the `cov` tool, with the `profdata` file and all test binaries:
+
+```shell
+$ cargo cov -- report \
+    --use-color --ignore-filename-regex='/.cargo/registry' \
+    --instr-profile=json5format.profdata \
+    --object target/debug/deps/lib-30768f9c53506dc5 \
+    --object target/debug/deps/json5format-fececd4653271682
+$ cargo cov -- show \
+    --use-color --ignore-filename-regex='/.cargo/registry' \
+    --instr-profile=json5format.profdata \
+    --object target/debug/deps/lib-30768f9c53506dc5 \
+    --object target/debug/deps/json5format-fececd4653271682 \
+    --show-instantiations --show-line-counts-or-regions \
+    --Xdemangler=rustfilt | less -R
+```
+
+> **Note**: The command line option `--ignore-filename-regex=/.cargo/registry`, which excludes the sources for dependencies from the coverage results.\_
+
+### Tips for listing the binaries automatically
+
+For `bash` users, one suggested way to automatically complete the `cov` command with the list of binaries is with a command like:
+
+```bash
+$ cargo cov -- report \
+    $( \
+      for file in \
+        $( \
+          RUSTFLAGS="-Z instrument-coverage" \
+            cargo test --tests --no-run --message-format=json \
+              | jq -r "select(.profile.test == true) | .filenames[]" \
+              | grep -v dSYM - \
+        ); \
+      do \
+        printf "%s %s " -object $file; \
+      done \
+    ) \
+  --instr-profile=json5format.profdata --summary-only # and/or other options
+```
+
+Adding `--no-run --message-format=json` to the _same_ `cargo test` command used to run
+the tests (including the same environment variables and flags) generates output in a JSON
+format that `jq` can easily query.
+
+The `printf` command takes this list and generates the `--object <binary>` arguments
+for each listed test binary.
+
+### Including doc tests
+
+The previous examples run `cargo test` with `--tests`, which excludes doc tests.[^79417]
+
+To include doc tests in the coverage results, drop the `--tests` flag, and apply the
+`-Z instrument-coverage` flag, and some doc-test-specific options in the
+`RUSTDOCFLAGS` environment variable. (The `cargo profdata` command does not change.)
+
+```bash
+$ RUSTFLAGS="-Z instrument-coverage" \
+  RUSTDOCFLAGS="-Z instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \
+  LLVM_PROFILE_FILE="json5format-%m.profraw" \
+    cargo test
+$ cargo profdata -- merge \
+    -sparse json5format-*.profraw -o json5format.profdata
+```
+
+The `-Z unstable-options --persist-doctests` flag is required, to save the test binaries
+(with their coverage maps) for `llvm-cov`.
+
+```bash
+$ cargo cov -- report \
+    $( \
+      for file in \
+        $( \
+          RUSTFLAGS="-Z instrument-coverage" \
+          RUSTDOCFLAGS="-Z instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \
+            cargo test --no-run --message-format=json \
+              | jq -r "select(.profile.test == true) | .filenames[]" \
+              | grep -v dSYM - \
+        ) \
+        target/debug/doctestbins/*/rust_out; \
+      do \
+        [[ -x $file ]] && printf "%s %s " -object $file; \
+      done \
+    ) \
+  --instr-profile=json5format.profdata --summary-only # and/or other options
+```
+
+> **Note**: The differences in this `cargo cov` command, compared with the version without
+> doc tests, include:
+
+-   The `cargo test ... --no-run` command is updated with the same environment variables
+    and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE`
+    is only used when _running_ the tests.)
+-   The file glob pattern `target/debug/doctestbins/*/rust_out` adds the `rust_out`
+    binaries generated for doc tests (note, however, that some `rust_out` files may not
+    be executable binaries).
+-   `[[ -x $file ]] &&` filters the files passed on to the `printf`, to include only
+    executable binaries.
+
+[^79417]:
+    There is ongoing work to resolve a known issue
+    [(#79417)](https://github.com/rust-lang/rust/issues/79417) that doc test coverage
+    generates incorrect source line numbers in `llvm-cov show` results.
+
+## `-Z instrument-coverage=<options>`
+
+-   `-Z instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-Z instrument-coverage`, with no value.)
+-   `-Z instrument-coverage=except-unused-generics`: Instrument all functions except unused generics.
+-   `-Z instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions.
+-   `-Z instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-Z instrument-coverage` option.)
+
+## Other references
+
+Rust's implementation and workflow for source-based code coverage is based on the same library and tools used to implement [source-based code coverage in Clang]. (This document is partially based on the Clang guide.)
+
+[source-based code coverage in clang]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
+[`json5format`]: https://crates.io/crates/json5format
index f0ecb6871b1b5696308920637b0ab674590f5a4f..cb65978e0a07e6dd6a27a2b54d6cf985be4911fd 100644 (file)
@@ -1,327 +1,5 @@
 # `source-based-code-coverage`
 
-The tracking issue for this feature is: [#79121].
+See compiler flag [`-Z instrument-coverage`].
 
-------------------------
-
-## Introduction
-
-The Rust compiler includes two code coverage implementations:
-
-* A GCC-compatible, gcov-based coverage implementation, enabled with [`-Zprofile`], which operates on DebugInfo.
-* A source-based code coverage implementation, enabled with `-Zinstrument-coverage`, which uses LLVM's native coverage instrumentation to generate very precise coverage data.
-
-This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-Zinstrument-coverage` compiler flag.
-
-## How it works
-
-When `-Zinstrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by:
-
-* Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed.
-* Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 4_, supported _only_ in LLVM 11 and up), to define the code regions (start and end positions in the source code) being counted.
-
-When running a coverage-instrumented program, the counter values are written to a `profraw` file at program termination. LLVM bundles tools that read the counter results, combine those results with the coverage map (embedded in the program binary), and generate coverage reports in multiple formats.
-
-## Enable coverage profiling in the Rust compiler
-
-Rust's source-based code coverage requires the Rust "profiler runtime". Without it, compiling with `-Zinstrument-coverage` generates an error that the profiler runtime is missing.
-
-The Rust `nightly` distribution channel should include the profiler runtime, by default.
-
-*IMPORTANT:* If you are building the Rust compiler from the source distribution, the profiler runtime is *not* enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true`:
-
-```toml
-# Build the profiler runtime (required when compiling with options that depend
-# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`).
-profiler = true
-```
-
-If changed, rebuild the Rust compiler (see [rustc-dev-guide-how-to-build-and-run]).
-
-### Building the demangler
-
-LLVM coverage reporting tools generate results that can include function names and other symbol references, and the raw coverage results report symbols using the compiler's "mangled" version of the symbol names, which can be difficult to interpret. To work around this issue, LLVM coverage tools also support a user-specified symbol name demangler.
-
-One option for a Rust demangler is [`rustfilt`], which can be installed with:
-
-```shell
-cargo install rustfilt
-```
-
-Another option, if you are building from the Rust compiler source distribution, is to use the `rust-demangler` tool included in the Rust source distribution, which can be built with:
-
-```shell
-$ ./x.py build rust-demangler
-```
-
-## Compiling with coverage enabled
-
-Set the `-Zinstrument-coverage` compiler flag in order to enable LLVM source-based code coverage profiling.
-
-With `cargo`, you can instrument your program binary *and* dependencies at the same time.
-
-For example (if your project's Cargo.toml builds a binary by default):
-
-```shell
-$ cd your-project
-$ cargo clean
-$ RUSTFLAGS="-Zinstrument-coverage" cargo build
-```
-
-If `cargo` is not configured to use your `profiler`-enabled version of `rustc`, set the path explicitly via the `RUSTC` environment variable. Here is another example, using a `stage1` build of `rustc` to compile an `example` binary (from the [`json5format`] crate):
-
-```shell
-$ RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc \
-    RUSTFLAGS="-Zinstrument-coverage" \
-    cargo build --example formatjson5
-```
-
-Note that some compiler options, combined with `-Zinstrument-coverage`, can produce LLVM IR and/or linked binaries that are incompatible with LLVM coverage maps. For example, coverage requires references to actual functions in LLVM IR. If any covered function is optimized out, the coverage tools may not be able to process the coverage results. If you need to pass additional options, with coverage enabled, test them early, to confirm you will get the coverage results you expect.
-
-## Running the instrumented binary to generate raw coverage profiling data
-
-In the previous example, `cargo` generated the coverage-instrumented binary `formatjson5`:
-
-```shell
-$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 -
-```
-```json5
-{
-    some: 'thing',
-}
-```
-
-After running this program, a new file, `default.profraw`, should be in the current working directory. It's often preferable to set a specific file name or path. You can change the output file using the environment variable `LLVM_PROFILE_FILE`:
-
-
-```shell
-$ echo "{some: 'thing'}" \
-    | LLVM_PROFILE_FILE="formatjson5.profraw" target/debug/examples/formatjson5 -
-...
-$ ls formatjson5.profraw
-formatjson5.profraw
-```
-
-If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing directory structure will be created. Additionally, the following special pattern strings are rewritten:
-
-* `%p` - The process ID.
-* `%h` - The hostname of the machine running the program.
-* `%t` - The value of the TMPDIR environment variable.
-* `%Nm` - the instrumented binary’s signature: The runtime creates a pool of N raw profiles, used for on-line profile merging. The runtime takes care of selecting a raw profile from the pool, locking it, and updating it before the program exits. `N` must be between `1` and `9`, and defaults to `1` if omitted (with simply `%m`).
-* `%c` - Does not add anything to the filename, but enables a mode (on some platforms, including Darwin) in which profile counter updates are continuously synced to a file. This means that if the instrumented program crashes, or is killed by a signal, perfect coverage information can still be recovered.
-
-## Installing LLVM coverage tools
-
-LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 11 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.):
-
-* The LLVM tools may be installed (or installable) directly to your OS (such as via `apt-get`, for Linux).
-* If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`.
-* You can install compatible versions of these tools via `rustup`.
-
-The `rustup` option is guaranteed to install a compatible version of the LLVM tools, but they can be hard to find. We recommend [`cargo-binutils`], which installs Rust-specific wrappers around these and other LLVM tools, so you can invoke them via `cargo` commands!
-
-```shell
-$ rustup component add llvm-tools-preview
-$ cargo install cargo-binutils
-$ cargo profdata -- --help  # note the additional "--" preceding the tool-specific arguments
-```
-
-## Creating coverage reports
-
-Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`] (or `cargo profdata -- merge`), which can combine multiple raw profiles and index them at the same time:
-
-```shell
-$ llvm-profdata merge -sparse formatjson5.profraw -o formatjson5.profdata
-```
-
-Finally, the `.profdata` file is used, in combination with the coverage map (from the program binary) to generate coverage reports using [`llvm-cov report`] (or `cargo cov -- report`), for a coverage summaries; and [`llvm-cov show`] (or `cargo cov -- show`), to see detailed coverage of lines and regions (character ranges) overlaid on the original source code.
-
-These commands have several display and filtering options. For example:
-
-```shell
-$ llvm-cov show -Xdemangler=rustfilt target/debug/examples/formatjson5 \
-    -instr-profile=formatjson5.profdata \
-    -show-line-counts-or-regions \
-    -show-instantiations \
-    -name=add_quoted_string
-```
-
-<img alt="Screenshot of sample `llvm-cov show` result, for function add_quoted_string" src="img/llvm-cov-show-01.png" class="center"/>
-<br/>
-<br/>
-
-Some of the more notable options in this example include:
-
-* `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example, but this could also be a path to the `rust-demangler` tool)
-* `target/debug/examples/formatjson5` - the instrumented binary (from which to extract the coverage map)
-* `--instr-profile=<path-to-file>.profdata` - the location of the `.profdata` file created by `llvm-profdata merge` (from the `.profraw` file generated by the instrumented binary)
-* `--name=<exact-function-name>` - to show coverage for a specific function (or, consider using another filter option, such as `--name-regex=<pattern>`)
-
-## Interpreting reports
-
-There are four statistics tracked in a coverage summary:
-
-* Function coverage is the percentage of functions that have been executed at least once. A function is considered to be executed if any of its instantiations are executed.
-* Instantiation coverage is the percentage of function instantiations that have been executed at least once. Generic functions and functions generated from macros are two kinds of functions that may have multiple instantiations.
-* Line coverage is the percentage of code lines that have been executed at least once. Only executable lines within function bodies are considered to be code lines.
-* Region coverage is the percentage of code regions that have been executed at least once. A code region may span multiple lines: for example, in a large function body with no control flow. In other cases, a single line can contain multiple code regions: `return x || (y && z)` has countable code regions for `x` (which may resolve the expression, if `x` is `true`), `|| (y && z)` (executed only if `x` was `false`), and `return` (executed in either situation).
-
-Of these four statistics, function coverage is usually the least granular while region coverage is the most granular. The project-wide totals for each statistic are listed in the summary.
-
-## Test coverage
-
-A typical use case for coverage analysis is test coverage. Rust's source-based coverage tools can both measure your tests' code coverage as percentage, and pinpoint functions and branches not tested.
-
-The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate.
-
-Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-Zinstrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.)
-
-```shell
-$ RUSTFLAGS="-Zinstrument-coverage" \
-    LLVM_PROFILE_FILE="json5format-%m.profraw" \
-    cargo test --tests
-```
-
-Make note of the test binary file paths, displayed after the word "`Running`" in the test output:
-
-```text
-   ...
-   Compiling json5format v0.1.3 ($HOME/json5format)
-    Finished test [unoptimized + debuginfo] target(s) in 14.60s
-
-     Running target/debug/deps/json5format-fececd4653271682
-running 25 tests
-...
-test result: ok. 25 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
-
-     Running target/debug/deps/lib-30768f9c53506dc5
-running 31 tests
-...
-test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
-```
-
-You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them:
-
-```shell
-$ cargo profdata -- merge \
-    -sparse json5format-*.profraw -o json5format.profdata
-```
-
-Then run the `cov` tool, with the `profdata` file and all test binaries:
-
-```shell
-$ cargo cov -- report \
-    --use-color --ignore-filename-regex='/.cargo/registry' \
-    --instr-profile=json5format.profdata \
-    --object target/debug/deps/lib-30768f9c53506dc5 \
-    --object target/debug/deps/json5format-fececd4653271682
-$ cargo cov -- show \
-    --use-color --ignore-filename-regex='/.cargo/registry' \
-    --instr-profile=json5format.profdata \
-    --object target/debug/deps/lib-30768f9c53506dc5 \
-    --object target/debug/deps/json5format-fececd4653271682 \
-    --show-instantiations --show-line-counts-or-regions \
-    --Xdemangler=rustfilt | less -R
-```
-
-_Note the command line option `--ignore-filename-regex=/.cargo/registry`, which excludes the sources for dependencies from the coverage results._
-
-### Tips for listing the binaries automatically
-
-For `bash` users, one suggested way to automatically complete the `cov` command with the list of binaries is with a command like:
-
-```bash
-$ cargo cov -- report \
-    $( \
-      for file in \
-        $( \
-          RUSTFLAGS="-Zinstrument-coverage" \
-            cargo test --tests --no-run --message-format=json \
-              | jq -r "select(.profile.test == true) | .filenames[]" \
-              | grep -v dSYM - \
-        ); \
-      do \
-        printf "%s %s " -object $file; \
-      done \
-    ) \
-  --instr-profile=json5format.profdata --summary-only # and/or other options
-```
-
-Adding `--no-run --message-format=json` to the _same_ `cargo test` command used to run
-the tests (including the same environment variables and flags) generates output in a JSON
-format that `jq` can easily query.
-
-The `printf` command takes this list and generates the `--object <binary>` arguments
-for each listed test binary.
-
-### Including doc tests
-
-The previous examples run `cargo test` with `--tests`, which excludes doc tests.[^79417]
-
-To include doc tests in the coverage results, drop the `--tests` flag, and apply the
-`-Zinstrument-coverage` flag, and some doc-test-specific options in the
-`RUSTDOCFLAGS` environment variable. (The `cargo profdata` command does not change.)
-
-```bash
-$ RUSTFLAGS="-Zinstrument-coverage" \
-  RUSTDOCFLAGS="-Zinstrument-coverage -Zunstable-options --persist-doctests target/debug/doctestbins" \
-  LLVM_PROFILE_FILE="json5format-%m.profraw" \
-    cargo test
-$ cargo profdata -- merge \
-    -sparse json5format-*.profraw -o json5format.profdata
-```
-
-The `-Zunstable-options --persist-doctests` flag is required, to save the test binaries
-(with their coverage maps) for `llvm-cov`.
-
-```bash
-$ cargo cov -- report \
-    $( \
-      for file in \
-        $( \
-          RUSTFLAGS="-Zinstrument-coverage" \
-          RUSTDOCFLAGS="-Zinstrument-coverage -Zunstable-options --persist-doctests target/debug/doctestbins" \
-            cargo test --no-run --message-format=json \
-              | jq -r "select(.profile.test == true) | .filenames[]" \
-              | grep -v dSYM - \
-        ) \
-        target/debug/doctestbins/*/rust_out; \
-      do \
-        [[ -x $file ]] && printf "%s %s " -object $file; \
-      done \
-    ) \
-  --instr-profile=json5format.profdata --summary-only # and/or other options
-```
-
-Note, the differences in this `cargo cov` command, compared with the version without
-doc tests, include:
-
-* The `cargo test ... --no-run` command is updated with the same environment variables
-  and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE`
-  is only used when _running_ the tests.)
-* The file glob pattern `target/debug/doctestbins/*/rust_out` adds the `rust_out`
-  binaries generated for doc tests (note, however, that some `rust_out` files may not
-  be executable binaries).
-* `[[ -x $file ]] &&` filters the files passed on to the `printf`, to include only
-  executable binaries.
-
-[^79417]: There is ongoing work to resolve a known issue
-[(#79417)](https://github.com/rust-lang/rust/issues/79417) that doc test coverage
-generates incorrect source line numbers in `llvm-cov show` results.
-
-## Other references
-
-Rust's implementation and workflow for source-based code coverage is based on the same library and tools used to implement [source-based code coverage in Clang]. (This document is partially based on the Clang guide.)
-
-[#79121]: https://github.com/rust-lang/rust/issues/79121
-[`-Zprofile`]: profile.md
-[`llvm.instrprof.increment`]: https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic
-[LLVM Code Coverage Mapping Format]: https://llvm.org/docs/CoverageMappingFormat.html
-[rustc-dev-guide-how-to-build-and-run]: https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html
-[`rustfilt`]: https://crates.io/crates/rustfilt
-[`json5format`]: https://crates.io/crates/json5format
-[`cargo-binutils`]: https://crates.io/crates/cargo-binutils
-[`llvm-profdata merge`]: https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge
-[`llvm-cov report`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report
-[`llvm-cov show`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show
-[source-based code coverage in Clang]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
+[`-z instrument-coverage`]: ./instrument-coverage.html
diff --git a/src/doc/unstable-book/src/language-features/const-fn.md b/src/doc/unstable-book/src/language-features/const-fn.md
deleted file mode 100644 (file)
index bcf7f78..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# `const_fn`
-
-The tracking issue for this feature is: [#57563]
-
-[#57563]: https://github.com/rust-lang/rust/issues/57563
-
-------------------------
-
-The `const_fn` feature enables additional functionality not stabilized in the
-[minimal subset of `const_fn`](https://github.com/rust-lang/rust/issues/53555)
diff --git a/src/doc/unstable-book/src/language-features/native-link-modifiers-as-needed.md b/src/doc/unstable-book/src/language-features/native-link-modifiers-as-needed.md
new file mode 100644 (file)
index 0000000..1757673
--- /dev/null
@@ -0,0 +1,18 @@
+# `native_link_modifiers_as_needed`
+
+The tracking issue for this feature is: [#81490]
+
+[#81490]: https://github.com/rust-lang/rust/issues/81490
+
+------------------------
+
+The `native_link_modifiers_as_needed` feature allows you to use the `as-needed` modifier.
+
+`as-needed` is only compatible with the `dynamic` and `framework` linking kinds. Using any other kind will result in a compiler error.
+
+`+as-needed` means that the library will be actually linked only if it satisfies some undefined symbols at the point at which it is specified on the command line, making it similar to static libraries in this regard.
+
+This modifier translates to `--as-needed` for ld-like linkers, and to `-dead_strip_dylibs` / `-needed_library` / `-needed_framework` for ld64.
+The modifier does nothing for linkers that don't support it (e.g. `link.exe`).
+
+The default for this modifier is unclear, some targets currently specify it as `+as-needed`, some do not. We may want to try making `+as-needed` a default for all targets.
diff --git a/src/doc/unstable-book/src/language-features/native-link-modifiers-bundle.md b/src/doc/unstable-book/src/language-features/native-link-modifiers-bundle.md
new file mode 100644 (file)
index 0000000..ac192cf
--- /dev/null
@@ -0,0 +1,19 @@
+# `native_link_modifiers_bundle`
+
+The tracking issue for this feature is: [#81490]
+
+[#81490]: https://github.com/rust-lang/rust/issues/81490
+
+------------------------
+
+The `native_link_modifiers_bundle` feature allows you to use the `bundle` modifier.
+
+Only compatible with the `static` linking kind. Using any other kind will result in a compiler error.
+
+`+bundle` means objects from the static library are bundled into the produced crate (a rlib, for example) and are used from this crate later during linking of the final binary.
+
+`-bundle` means the static library is included into the produced rlib "by name" and object files from it are included only during linking of the final binary, the file search by that name is also performed during final linking.
+
+This modifier is supposed to supersede the `static-nobundle` linking kind defined by [RFC 1717](https://github.com/rust-lang/rfcs/pull/1717).
+
+The default for this modifier is currently `+bundle`, but it could be changed later on some future edition boundary.
diff --git a/src/doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md b/src/doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md
new file mode 100644 (file)
index 0000000..02bd87e
--- /dev/null
@@ -0,0 +1,20 @@
+# `native_link_modifiers_verbatim`
+
+The tracking issue for this feature is: [#81490]
+
+[#81490]: https://github.com/rust-lang/rust/issues/81490
+
+------------------------
+
+The `native_link_modifiers_verbatim` feature allows you to use the `verbatim` modifier.
+
+`+verbatim` means that rustc itself won't add any target-specified library prefixes or suffixes (like `lib` or `.a`) to the library name, and will try its best to ask for the same thing from the linker.
+
+For `ld`-like linkers rustc will use the `-l:filename` syntax (note the colon) when passing the library, so the linker won't add any prefixes or suffixes as well.
+See [`-l namespec`](https://sourceware.org/binutils/docs/ld/Options.html) in ld documentation for more details.
+For linkers not supporting any verbatim modifiers (e.g. `link.exe` or `ld64`) the library name will be passed as is.
+
+The default for this modifier is `-verbatim`.
+
+This RFC changes the behavior of `raw-dylib` linking kind specified by [RFC 2627](https://github.com/rust-lang/rfcs/pull/2627). The `.dll` suffix (or other target-specified suffixes for other targets) is now added automatically.
+If your DLL doesn't have the `.dll` suffix, it can be specified with `+verbatim`.
diff --git a/src/doc/unstable-book/src/language-features/native-link-modifiers-whole-archive.md b/src/doc/unstable-book/src/language-features/native-link-modifiers-whole-archive.md
new file mode 100644 (file)
index 0000000..4961e88
--- /dev/null
@@ -0,0 +1,18 @@
+# `native_link_modifiers_whole_archive`
+
+The tracking issue for this feature is: [#81490]
+
+[#81490]: https://github.com/rust-lang/rust/issues/81490
+
+------------------------
+
+The `native_link_modifiers_whole_archive` feature allows you to use the `whole-archive` modifier.
+
+Only compatible with the `static` linking kind. Using any other kind will result in a compiler error.
+
+`+whole-archive` means that the static library is linked as a whole archive without throwing any object files away.
+
+This modifier translates to `--whole-archive` for `ld`-like linkers, to `/WHOLEARCHIVE` for `link.exe`, and to `-force_load` for `ld64`.
+The modifier does nothing for linkers that don't support it.
+
+The default for this modifier is `-whole-archive`.
diff --git a/src/doc/unstable-book/src/language-features/native-link-modifiers.md b/src/doc/unstable-book/src/language-features/native-link-modifiers.md
new file mode 100644 (file)
index 0000000..fc8b575
--- /dev/null
@@ -0,0 +1,11 @@
+# `native_link_modifiers`
+
+The tracking issue for this feature is: [#81490]
+
+[#81490]: https://github.com/rust-lang/rust/issues/81490
+
+------------------------
+
+The `native_link_modifiers` feature allows you to use the `modifiers` syntax with the `#[link(..)]` attribute.
+
+Modifiers are specified as a comma-delimited string with each modifier prefixed with either a `+` or `-` to indicate that the modifier is enabled or disabled, respectively. The last boolean value specified for a given modifier wins.
diff --git a/src/doc/unstable-book/src/language-features/no-coverage.md b/src/doc/unstable-book/src/language-features/no-coverage.md
new file mode 100644 (file)
index 0000000..327cdb3
--- /dev/null
@@ -0,0 +1,30 @@
+# `no_coverage`
+
+The tracking issue for this feature is: [#84605]
+
+[#84605]: https://github.com/rust-lang/rust/issues/84605
+
+---
+
+The `no_coverage` attribute can be used to selectively disable coverage
+instrumentation in an annotated function. This might be useful to:
+
+-   Avoid instrumentation overhead in a performance critical function
+-   Avoid generating coverage for a function that is not meant to be executed,
+    but still target 100% coverage for the rest of the program.
+
+## Example
+
+```rust
+#![feature(no_coverage)]
+
+// `foo()` will get coverage instrumentation (by default)
+fn foo() {
+  // ...
+}
+
+#[no_coverage]
+fn bar() {
+  // ...
+}
+```
index 5974cd878dd0c853bd2865c8d9771a7338e270a6..6d05ac073cca3e869828f6ae875186cd937fbdbf 100644 (file)
@@ -317,10 +317,10 @@ fn merge_attrs(
             } else {
                 Attributes::from_ast(&both, None)
             },
-            both.cfg(cx.sess().diagnostic()),
+            both.cfg(cx.sess()),
         )
     } else {
-        (old_attrs.clean(cx), old_attrs.cfg(cx.sess().diagnostic()))
+        (old_attrs.clean(cx), old_attrs.cfg(cx.sess()))
     }
 }
 
index 411cfab9f06a001f72313a5dd9def58382d82cff..e1dde8eeaf84ae838513f84283e1aa532c3204e1 100644 (file)
@@ -2018,7 +2018,7 @@ fn clean_extern_crate(
         def_id: crate_def_id.into(),
         visibility: krate.vis.clean(cx),
         kind: box ExternCrateItem { src: orig_name },
-        cfg: attrs.cfg(cx.sess().diagnostic()),
+        cfg: attrs.cfg(cx.sess()),
     }]
 }
 
index 350742e8f2898e56d32f91c3a27f543ba0cc4619..bca7a8cfcee0970a62b55b05ecff01c93dd5c501 100644 (file)
@@ -18,7 +18,7 @@
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{BodyId, Mutability};
 use rustc_index::vec::IndexVec;
@@ -86,22 +86,7 @@ impl FakeDefId {
     }
 
     #[inline]
-    crate fn as_local(self) -> Option<LocalDefId> {
-        match self {
-            FakeDefId::Real(id) => id.as_local(),
-            FakeDefId::Fake(idx, krate) => {
-                (krate == LOCAL_CRATE).then(|| LocalDefId { local_def_index: idx })
-            }
-        }
-    }
-
-    #[inline]
-    crate fn expect_local(self) -> LocalDefId {
-        self.as_local()
-            .unwrap_or_else(|| panic!("FakeDefId::expect_local: `{:?}` isn't local", self))
-    }
-
-    #[inline]
+    #[track_caller]
     crate fn expect_real(self) -> rustc_hir::def_id::DefId {
         self.as_real().unwrap_or_else(|| panic!("FakeDefId::expect_real: `{:?}` isn't real", self))
     }
@@ -444,7 +429,7 @@ pub fn from_def_id_and_parts(
             kind,
             box ast_attrs.clean(cx),
             cx,
-            ast_attrs.cfg(cx.sess().diagnostic()),
+            ast_attrs.cfg(cx.sess()),
         )
     }
 
@@ -456,7 +441,7 @@ pub fn from_def_id_and_attrs_and_parts(
         cx: &mut DocContext<'_>,
         cfg: Option<Arc<Cfg>>,
     ) -> Item {
-        debug!("name={:?}, def_id={:?}", name, def_id);
+        trace!("name={:?}, def_id={:?}", name, def_id);
 
         Item {
             def_id: def_id.into(),
@@ -517,10 +502,9 @@ pub fn from_def_id_and_attrs_and_parts(
                                 Some(ExternalLocation::Remote(ref s)) => {
                                     format!("{}/std/", s.trim_end_matches('/'))
                                 }
-                                Some(ExternalLocation::Unknown) | None => format!(
-                                    "https://doc.rust-lang.org/{}/std/",
-                                    crate::doc_rust_lang_org_channel(),
-                                ),
+                                Some(ExternalLocation::Unknown) | None => {
+                                    "https://doc.rust-lang.org/nightly/std/".to_string()
+                                }
                             };
                             // This is a primitive so the url is done "by hand".
                             let tail = fragment.find('#').unwrap_or_else(|| fragment.len());
@@ -796,7 +780,7 @@ fn size_hint(&self) -> (usize, Option<usize>) {
 
     fn other_attrs(&self) -> Vec<ast::Attribute>;
 
-    fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option<Arc<Cfg>>;
+    fn cfg(&self, sess: &Session) -> Option<Arc<Cfg>>;
 }
 
 impl AttributesExt for [ast::Attribute] {
@@ -821,17 +805,28 @@ 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>> {
+    fn cfg(&self, sess: &Session) -> Option<Arc<Cfg>> {
         let mut cfg = Cfg::True;
 
         for attr in self.iter() {
+            // #[doc]
             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),
+                // #[doc(...)]
+                if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) {
+                    for item in list {
+                        // #[doc(include)]
+                        if !item.has_name(sym::cfg) {
+                            continue;
+                        }
+                        // #[doc(cfg(...))]
+                        if let Some(cfg_mi) = item
+                            .meta_item()
+                            .and_then(|item| rustc_expand::config::parse_cfg(&item, sess))
+                        {
+                            match Cfg::parse(&cfg_mi) {
+                                Ok(new_cfg) => cfg &= new_cfg,
+                                Err(e) => sess.span_err(e.span, e.msg),
+                            }
                         }
                     }
                 }
@@ -998,29 +993,6 @@ impl Attributes {
         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;
-
-        if let ast::MetaItemKind::List(ref nmis) = mi.kind {
-            if nmis.len() == 1 {
-                if let MetaItem(ref cfg_mi) = nmis[0] {
-                    if cfg_mi.has_name(sym::cfg) {
-                        if let ast::MetaItemKind::List(ref cfg_nmis) = cfg_mi.kind {
-                            if cfg_nmis.len() == 1 {
-                                if let MetaItem(ref content_mi) = cfg_nmis[0] {
-                                    return Some(content_mi);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        None
-    }
-
     /// Reads a `MetaItem` from within an attribute, looks for whether it is a
     /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from
     /// its expansion.
@@ -1634,7 +1606,6 @@ impl Type {
                 }
             }
             RawPointer(..) => Some(PrimitiveType::RawPointer),
-            BorrowedRef { type_: box Generic(..), .. } => Some(PrimitiveType::Reference),
             BareFunction(..) => Some(PrimitiveType::Fn),
             Never => Some(PrimitiveType::Never),
             _ => None,
@@ -1693,13 +1664,7 @@ impl Type {
     }
 
     crate fn is_primitive(&self) -> bool {
-        match self {
-            Self::Primitive(_) => true,
-            Self::BorrowedRef { ref type_, .. } | Self::RawPointer(_, ref type_) => {
-                type_.is_primitive()
-            }
-            _ => false,
-        }
+        self.primitive_type().is_some()
     }
 
     crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> {
index 7235217e290407f646e1feeb1f3b72944af48782..51a011cf1977364ec7081bac0e954156f8835b31 100644 (file)
@@ -542,14 +542,3 @@ fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: &'tcx ty::Const<'tc
             && attr.meta_item_list().map_or(false, |l| rustc_attr::list_contains_name(&l, flag))
     })
 }
-
-/// Return a channel suitable for using in a `doc.rust-lang.org/{channel}` format string.
-crate fn doc_rust_lang_org_channel() -> &'static str {
-    match env!("CFG_RELEASE_CHANNEL") {
-        "stable" => env!("CFG_RELEASE_NUM"),
-        "beta" => "beta",
-        "nightly" | "dev" => "nightly",
-        // custom build of rustdoc maybe? link to the stable docs just in case
-        _ => "",
-    }
-}
index 48eb14ed291470184ab90a83521860f15bd5568e..045b42d0dcaf75b8efbe3fe2827a211e803c5472 100644 (file)
@@ -7,10 +7,7 @@
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_session::config::{self, parse_crate_types_from_list, parse_externs, CrateType};
-use rustc_session::config::{
-    build_codegen_options, build_debugging_options, get_cmd_lint_options, host_triple,
-    nightly_options,
-};
+use rustc_session::config::{get_cmd_lint_options, host_triple, nightly_options};
 use rustc_session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs};
 use rustc_session::getopts;
 use rustc_session::lint::Level;
@@ -360,8 +357,8 @@ fn println_condition(condition: Condition) {
             config::parse_json(&matches);
         let error_format = config::parse_error_format(&matches, color, json_rendered);
 
-        let codegen_options = build_codegen_options(matches, error_format);
-        let debugging_opts = build_debugging_options(matches, error_format);
+        let codegen_options = CodegenOptions::build(matches, error_format);
+        let debugging_opts = DebuggingOptions::build(matches, error_format);
 
         let diag = new_handler(error_format, None, &debugging_opts);
 
index b0d163763b4bd249178bcb85ccbfa5786ec029d3..b43916f9f332cc76f47ae964b84f32587aebf086 100644 (file)
@@ -226,14 +226,7 @@ impl<'tcx> DocContext<'tcx> {
     lints_to_show.extend(crate::lint::RUSTDOC_LINTS.iter().map(|lint| lint.name.to_string()));
 
     let (lint_opts, lint_caps) = crate::lint::init_lints(lints_to_show, lint_opts, |lint| {
-        // FIXME: why is this necessary?
-        if lint.name == crate::lint::BROKEN_INTRA_DOC_LINKS.name
-            || lint.name == crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name
-        {
-            None
-        } else {
-            Some((lint.name_lower(), lint::Allow))
-        }
+        Some((lint.name_lower(), lint::Allow))
     });
 
     let crate_types =
@@ -406,18 +399,15 @@ impl<'tcx> DocContext<'tcx> {
     let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt));
 
     if krate.module.doc_value().map(|d| d.is_empty()).unwrap_or(true) {
-        let help = format!(
-            "The following guide may be of use:\n\
-            https://doc.rust-lang.org/{}/rustdoc/how-to-write-documentation.html",
-            crate::doc_rust_lang_org_channel(),
-        );
+        let help = "The following guide may be of use:\n\
+                https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html";
         tcx.struct_lint_node(
             crate::lint::MISSING_CRATE_LEVEL_DOCS,
             DocContext::as_local_hir_id(tcx, krate.module.def_id).unwrap(),
             |lint| {
                 let mut diag =
                     lint.build("no documentation found for this crate's top-level module");
-                diag.help(&help);
+                diag.help(help);
                 diag.emit();
             },
         );
index c0157121e1923f8943d000cac3f8a78d5820f99b..03e3fe52f71ee3535b3d733e2429a3713031f501 100644 (file)
@@ -76,7 +76,6 @@
         externs: options.externs.clone(),
         unstable_features: options.render_options.unstable_features,
         actually_rustdoc: true,
-        debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() },
         edition: options.edition,
         target_triple: options.target.clone(),
         crate_name: options.crate_name.clone(),
@@ -1096,7 +1095,7 @@ fn visit_testable<F: FnOnce(&mut Self)>(
         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 let Some(ref cfg) = ast_attrs.cfg(self.sess) {
             if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
                 return;
             }
index 7130a6bc1e8883f57a5e76a59081fa4573ad4410..51392ca1191891a7dc3932f909bb4e5e9efa62eb 100644 (file)
@@ -13,7 +13,6 @@
 use rustc_lexer::{LiteralKind, TokenKind};
 use rustc_span::edition::Edition;
 use rustc_span::symbol::Symbol;
-use rustc_span::with_default_session_globals;
 
 use super::format::Buffer;
 
@@ -25,6 +24,7 @@
     playground_button: Option<&str>,
     tooltip: Option<(Option<Edition>, &str)>,
     edition: Edition,
+    extra_content: Option<Buffer>,
 ) {
     debug!("highlighting: ================\n{}\n==============", src);
     if let Some((edition_info, class)) = tooltip {
         );
     }
 
-    write_header(out, class);
+    write_header(out, class, extra_content);
     write_code(out, &src, edition);
     write_footer(out, playground_button);
 }
 
-fn write_header(out: &mut Buffer, class: Option<&str>) {
-    writeln!(out, "<div class=\"example-wrap\"><pre class=\"rust {}\">", class.unwrap_or_default());
+fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buffer>) {
+    write!(out, "<div class=\"example-wrap\">");
+    if let Some(extra) = extra_content {
+        out.push_buffer(extra);
+    }
+    if let Some(class) = class {
+        writeln!(out, "<pre class=\"rust {}\">", class);
+    } else {
+        writeln!(out, "<pre class=\"rust\">");
+    }
 }
 
 fn write_code(out: &mut Buffer, src: &str, edition: Edition) {
@@ -238,28 +246,26 @@ fn next(&mut self) -> Option<(TokenKind, &'a str)> {
     /// possibly giving it an HTML span with a class specifying what flavor of
     /// token is used.
     fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) {
-        with_default_session_globals(|| {
-            loop {
-                if self
-                    .tokens
-                    .peek()
-                    .map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
-                    .unwrap_or(false)
-                {
-                    let tokens = self.get_full_ident_path();
-                    for (token, start, end) in tokens {
-                        let text = &self.src[start..end];
-                        self.advance(token, text, sink);
-                        self.byte_pos += text.len() as u32;
-                    }
-                }
-                if let Some((token, text)) = self.next() {
+        loop {
+            if self
+                .tokens
+                .peek()
+                .map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
+                .unwrap_or(false)
+            {
+                let tokens = self.get_full_ident_path();
+                for (token, start, end) in tokens {
+                    let text = &self.src[start..end];
                     self.advance(token, text, sink);
-                } else {
-                    break;
+                    self.byte_pos += text.len() as u32;
                 }
             }
-        })
+            if let Some((token, text)) = self.next() {
+                self.advance(token, text, sink);
+            } else {
+                break;
+            }
+        }
     }
 
     /// Single step of highlighting. This will classify `token`, but maybe also
index 305cf61091dc6f7607c4a4bf3a43fe5c681ba511..a0da2c963d167312314911e4aed238f1fff62a42 100644 (file)
@@ -2,6 +2,7 @@
 use crate::html::format::Buffer;
 use expect_test::expect_file;
 use rustc_span::edition::Edition;
+use rustc_span::with_default_session_globals;
 
 const STYLE: &str = r#"
 <style>
 
 #[test]
 fn test_html_highlighting() {
-    let src = include_str!("fixtures/sample.rs");
-    let html = {
-        let mut out = Buffer::new();
-        write_code(&mut out, src, Edition::Edition2018);
-        format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
-    };
-    expect_file!["fixtures/sample.html"].assert_eq(&html);
+    with_default_session_globals(|| {
+        let src = include_str!("fixtures/sample.rs");
+        let html = {
+            let mut out = Buffer::new();
+            write_code(&mut out, src, Edition::Edition2018);
+            format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
+        };
+        expect_file!["fixtures/sample.html"].assert_eq(&html);
+    });
 }
 
 #[test]
 fn test_dos_backline() {
-    let src = "pub fn foo() {\r\n\
+    with_default_session_globals(|| {
+        let src = "pub fn foo() {\r\n\
     println!(\"foo\");\r\n\
 }\r\n";
-    let mut html = Buffer::new();
-    write_code(&mut html, src, Edition::Edition2018);
-    expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
+        let mut html = Buffer::new();
+        write_code(&mut html, src, Edition::Edition2018);
+        expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
+    });
 }
index dc29add9333141ac7b1b7bac92b20a0f4f05dcfd..99e96fdcf1eb4e91404e7d65b6ba30f91c59f515 100644 (file)
     crate static_extra_scripts: &'a [&'a str],
 }
 
+impl<'a> Page<'a> {
+    crate fn get_static_root_path(&self) -> &str {
+        self.static_root_path.unwrap_or(self.root_path)
+    }
+}
+
 crate fn render<T: Print, S: Print>(
     layout: &Layout,
     page: &Page<'_>,
@@ -41,7 +47,7 @@
     t: T,
     style_files: &[StylePath],
 ) -> String {
-    let static_root_path = page.static_root_path.unwrap_or(page.root_path);
+    let static_root_path = page.get_static_root_path();
     format!(
         "<!DOCTYPE html>\
 <html lang=\"en\">\
index 509f1730557756ee7b1cc92d569eb45a4c1b1639..c2b40ab34e2551c1062525c3ce6d7f33c45705c4 100644 (file)
@@ -315,6 +315,7 @@ fn dont_escape(c: u8) -> bool {
             playground_button.as_deref(),
             tooltip,
             edition,
+            None,
         );
         Some(Event::Html(s.into_inner().into()))
     }
index 2f3f87215c3a1343588b7340d4274c5a513371af..4e17dc8d3a7d0121f2b9d8f3be38699c0912807d 100644 (file)
@@ -155,7 +155,7 @@ pub(crate) fn cache(&self) -> &Cache {
         &self.cache
     }
 
-    fn sess(&self) -> &'tcx Session {
+    pub(super) fn sess(&self) -> &'tcx Session {
         &self.shared.tcx.sess
     }
 
@@ -215,7 +215,7 @@ fn render_item(&self, it: &clean::Item, is_module: bool) -> String {
                 &self.shared.layout,
                 &page,
                 |buf: &mut _| print_sidebar(self, it, buf),
-                |buf: &mut _| print_item(self, it, buf),
+                |buf: &mut _| print_item(self, it, buf, &page),
                 &self.shared.style_files,
             )
         } else {
index ea57831c0e5de07248c58858bdadb6f60f0422ce..5b54b32e4ddeaef06f1d49e032dc56b250cbc713 100644 (file)
@@ -509,7 +509,7 @@ fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option
         info!("Documenting {}", name);
     }
     document_item_info(w, cx, item, parent);
-    document_full(w, item, cx);
+    document_full_collapsible(w, item, cx);
 }
 
 /// Render md_text as markdown.
@@ -561,10 +561,29 @@ fn document_short(
     }
 }
 
+fn document_full_collapsible(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>) {
+    document_full_inner(w, item, cx, true);
+}
+
 fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>) {
+    document_full_inner(w, item, cx, false);
+}
+
+fn document_full_inner(w: &mut Buffer, item: &clean::Item, cx: &Context<'_>, is_collapsible: 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));
+        if is_collapsible {
+            w.write_str(
+                "<details class=\"rustdoc-toggle top-doc\" open>\
+                <summary class=\"hideme\">\
+                     <span>Expand description</span>\
+                </summary>",
+            );
+            render_markdown(w, cx, &s, item.links(cx));
+            w.write_str("</details>");
+        } else {
+            render_markdown(w, cx, &s, item.links(cx));
+        }
     }
 }
 
index 70b5458ece89467e07c16ead66ccc2abd5c68797..7ccc313cc5905ef6c6486f33af86d43f80dd38bb 100644 (file)
 use crate::html::escape::Escape;
 use crate::html::format::{print_abi_with_space, print_where_clause, Buffer, PrintWithSpace};
 use crate::html::highlight;
+use crate::html::layout::Page;
 use crate::html::markdown::MarkdownSummaryLine;
 
-pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
+pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, page: &Page<'_>) {
     debug_assert!(!item.is_stripped());
     // Write the breadcrumb trail header for the top
     buf.write_str("<h1 class=\"fqn\"><span class=\"in-band\">");
@@ -74,7 +75,16 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer)
         }
     }
     write!(buf, "<a class=\"{}\" href=\"\">{}</a>", item.type_(), item.name.as_ref().unwrap());
-    write!(buf, "<button id=\"copy-path\" onclick=\"copy_path(this)\">⎘</button>");
+    write!(
+        buf,
+        "<button id=\"copy-path\" onclick=\"copy_path(this)\">\
+            <img src=\"{static_root_path}clipboard{suffix}.svg\" \
+                width=\"19\" height=\"18\" \
+                alt=\"Copy item import\">\
+         </button>",
+        static_root_path = page.get_static_root_path(),
+        suffix = page.resource_suffix,
+    );
 
     buf.write_str("</span>"); // in-band
     buf.write_str("<span class=\"out-of-band\">");
@@ -296,7 +306,7 @@ fn cmp(
                     let import_item = clean::Item {
                         def_id: import_def_id.into(),
                         attrs: import_attrs,
-                        cfg: ast_attrs.cfg(cx.tcx().sess.diagnostic()),
+                        cfg: ast_attrs.cfg(cx.sess()),
                         ..myitem.clone()
                     };
 
@@ -1016,6 +1026,7 @@ fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Mac
             None,
             None,
             it.span(cx.tcx()).inner().edition(),
+            None,
         );
     });
     document(w, cx, it, None)
@@ -1464,17 +1475,23 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str {
 
 fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
     if item.is_non_exhaustive() {
-        write!(w, "<div class=\"docblock non-exhaustive non-exhaustive-{}\">", {
-            if item.is_struct() {
-                "struct"
-            } else if item.is_enum() {
-                "enum"
-            } else if item.is_variant() {
-                "variant"
-            } else {
-                "type"
+        write!(
+            w,
+            "<details class=\"rustdoc-toggle non-exhaustive\">\
+                 <summary class=\"hideme\"><span>{}</span></summary>\
+                 <div class=\"docblock\">",
+            {
+                if item.is_struct() {
+                    "This struct is marked as non-exhaustive"
+                } else if item.is_enum() {
+                    "This enum is marked as non-exhaustive"
+                } else if item.is_variant() {
+                    "This variant is marked as non-exhaustive"
+                } else {
+                    "This type is marked as non-exhaustive"
+                }
             }
-        });
+        );
 
         if item.is_struct() {
             w.write_str(
@@ -1502,6 +1519,6 @@ struct update syntax will not work.",
             );
         }
 
-        w.write_str("</div>");
+        w.write_str("</div></details>");
     }
 }
index c493801d9907fa79666cce98493511a92623b8fd..d0518cb6862fe3987e4015712bf510e1a6c8a397 100644 (file)
@@ -207,6 +207,7 @@ pub(super) fn write_shared(
     }
     write_toolchain("brush.svg", static_files::BRUSH_SVG)?;
     write_toolchain("wheel.svg", static_files::WHEEL_SVG)?;
+    write_toolchain("clipboard.svg", static_files::CLIPBOARD_SVG)?;
     write_toolchain("down-arrow.svg", static_files::DOWN_ARROW_SVG)?;
 
     let mut themes: Vec<&String> = themes.iter().collect();
index 14e2d65d94ef8e84df2462801d57eb655488875a..57c33f94918d71fd097ddf80609814ca0e406504 100644 (file)
@@ -169,16 +169,17 @@ fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
 /// adding line numbers to the left-hand side.
 fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
     let lines = s.lines().count();
+    let mut line_numbers = Buffer::empty_from(buf);
     let mut cols = 0;
     let mut tmp = lines;
     while tmp > 0 {
         cols += 1;
         tmp /= 10;
     }
-    buf.write_str("<pre class=\"line-numbers\">");
+    line_numbers.write_str("<pre class=\"line-numbers\">");
     for i in 1..=lines {
-        writeln!(buf, "<span id=\"{0}\">{0:1$}</span>", i, cols);
+        writeln!(line_numbers, "<span id=\"{0}\">{0:1$}</span>", i, cols);
     }
-    buf.write_str("</pre>");
-    highlight::render_with_highlighting(s, buf, None, None, None, edition);
+    line_numbers.write_str("</pre>");
+    highlight::render_with_highlighting(s, buf, None, None, None, edition, Some(line_numbers));
 }
diff --git a/src/librustdoc/html/static/clipboard.svg b/src/librustdoc/html/static/clipboard.svg
new file mode 100644 (file)
index 0000000..8adbd99
--- /dev/null
@@ -0,0 +1 @@
+<svg width="24" height="25" viewBox="0 0 24 25" xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard"><path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/><path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/></svg>
index 1bc5362592494d7b70048af27823ff5232535e4f..dc65e14ab37b83cee273fb8b41710b66e1da45f6 100644 (file)
@@ -377,28 +377,7 @@ function hideThemeButtonState() {
             if (savedHash.length === 0) {
                 return;
             }
-            elem = document.getElementById(savedHash.slice(1)); // we remove the '#'
-            if (!elem || !isHidden(elem)) {
-                return;
-            }
-            var parent = elem.parentNode;
-            if (parent && hasClass(parent, "impl-items")) {
-                // In case this is a trait implementation item, we first need to toggle
-                // the "Show hidden undocumented items".
-                onEachLazy(parent.getElementsByClassName("collapsed"), function(e) {
-                    if (e.parentNode === parent) {
-                        // Only click on the toggle we're looking for.
-                        e.click();
-                        return true;
-                    }
-                });
-                if (isHidden(elem)) {
-                    // The whole parent is collapsed. We need to click on its toggle as well!
-                    if (hasClass(parent.lastElementChild, "collapse-toggle")) {
-                        parent.lastElementChild.click();
-                    }
-                }
-            }
+            expandSection(savedHash.slice(1)); // we remove the '#'
         }
     }
 
@@ -465,25 +444,7 @@ function hideThemeButtonState() {
     }
 
     function expandSection(id) {
-        var elem = document.getElementById(id);
-        if (elem && isHidden(elem)) {
-            var h3 = elem.parentNode.previousElementSibling;
-            if (h3 && h3.tagName !== "H3") {
-                h3 = h3.previousElementSibling; // skip div.docblock
-            }
-
-            if (h3) {
-                var collapses = h3.getElementsByClassName("collapse-toggle");
-                if (collapses.length > 0) {
-                    // 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);
-            }
-        }
+        openParentDetails(document.getElementById(id));
     }
 
     function getHelpElement(build) {
@@ -678,10 +639,6 @@ function hideThemeButtonState() {
         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) {
@@ -898,72 +855,34 @@ function hideThemeButtonState() {
         return "\u2212"; // "\u2212" is "−" minus sign
     }
 
-    function onEveryMatchingChild(elem, className, func) {
-        if (elem && className && func) {
-            var length = elem.childNodes.length;
-            var nodes = elem.childNodes;
-            for (var i = 0; i < length; ++i) {
-                if (hasClass(nodes[i], className)) {
-                    func(nodes[i]);
-                } else {
-                    onEveryMatchingChild(nodes[i], className, func);
-                }
-            }
-        }
-    }
-
-    function toggleAllDocs(fromAutoCollapse) {
+    function toggleAllDocs() {
         var innerToggle = document.getElementById(toggleAllDocsId);
         if (!innerToggle) {
             return;
         }
+        var sectionIsCollapsed = false;
         if (hasClass(innerToggle, "will-expand")) {
             removeClass(innerToggle, "will-expand");
-            onEachLazy(document.getElementsByTagName("details"), function(e) {
-                e.open = true;
-            });
-            onEveryMatchingChild(innerToggle, "inner", function(e) {
-                e.innerHTML = labelForToggleButton(false);
+            onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
+                if (!hasClass(e, "type-contents-toggle")) {
+                    e.open = true;
+                }
             });
             innerToggle.title = "collapse all docs";
-            if (fromAutoCollapse !== true) {
-                onEachLazy(document.getElementsByClassName("collapse-toggle"), function(e) {
-                    collapseDocs(e, "show");
-                });
-            }
         } else {
             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;
-
-                if (parent) {
-                    superParent = parent.parentNode;
-                }
-                if (!parent || !superParent || superParent.id !== "main" ||
-                    hasClass(parent, "impl") === false) {
-                    e.innerHTML = labelForToggleButton(true);
+            onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
+                if (e.parentNode.id !== "main" ||
+                    (!hasClass(e, "implementors-toggle") &&
+                     !hasClass(e, "type-contents-toggle")))
+                {
+                    e.open = false;
                 }
             });
+            sectionIsCollapsed = true;
             innerToggle.title = "expand all docs";
-            if (fromAutoCollapse !== true) {
-                onEachLazy(document.getElementsByClassName("collapse-toggle"), function(e) {
-                    var parent = e.parentNode;
-                    var superParent = null;
-
-                    if (parent) {
-                        superParent = parent.parentNode;
-                    }
-                    if (!parent || !superParent || superParent.id !== "main" ||
-                        hasClass(parent, "impl") === false) {
-                        collapseDocs(e, "hide");
-                    }
-                });
-            }
         }
+        innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed);
     }
 
     function collapseDocs(toggle, mode) {
@@ -1102,71 +1021,26 @@ function hideThemeButtonState() {
         referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
     }
 
-    function createSimpleToggle(sectionIsCollapsed) {
-        var toggle = document.createElement("a");
-        toggle.href = "javascript:void(0)";
-        toggle.className = "collapse-toggle";
-        toggle.innerHTML = "[<span class=\"inner\">" + labelForToggleButton(sectionIsCollapsed) +
-                           "</span>]";
-        return toggle;
-    }
-
-    function createToggle(toggle, otherMessage, fontSize, extraClass, show) {
-        var span = document.createElement("span");
-        span.className = "toggle-label";
-        if (show) {
-            span.style.display = "none";
-        }
-        if (!otherMessage) {
-            span.innerHTML = "&nbsp;Expand&nbsp;description";
-        } else {
-            span.innerHTML = otherMessage;
-        }
-
-        if (fontSize) {
-            span.style.fontSize = fontSize;
-        }
-
-        var mainToggle = toggle.cloneNode(true);
-        mainToggle.appendChild(span);
-
-        var wrapper = document.createElement("div");
-        wrapper.className = "toggle-wrapper";
-        if (!show) {
-            addClass(wrapper, "collapsed");
-            var inner = mainToggle.getElementsByClassName("inner");
-            if (inner && inner.length > 0) {
-                inner[0].innerHTML = "+";
-            }
-        }
-        if (extraClass) {
-            addClass(wrapper, extraClass);
-        }
-        wrapper.appendChild(mainToggle);
-        return wrapper;
-    }
-
     (function() {
         var toggles = document.getElementById(toggleAllDocsId);
         if (toggles) {
             toggles.onclick = toggleAllDocs;
         }
 
-        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 impl_list = document.getElementById("trait-implementations-list");
         if (impl_list !== null) {
-            onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {
+            onEachLazy(impl_list.getElementsByClassName("rustdoc-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) {
+            onEachLazy(blanket_list.getElementsByClassName("rustdoc-toggle"), function(e) {
                 collapseNonInherent(e);
             });
         }
@@ -1205,66 +1079,6 @@ function hideThemeButtonState() {
             }
         }
 
-        function buildToggleWrapper(e) {
-            if (hasClass(e, "autohide")) {
-                var wrap = e.previousElementSibling;
-                if (wrap && hasClass(wrap, "toggle-wrapper")) {
-                    var inner_toggle = wrap.childNodes[0];
-                    var extra = e.childNodes[0].tagName === "H3";
-
-                    e.style.display = "none";
-                    addClass(wrap, "collapsed");
-                    onEachLazy(inner_toggle.getElementsByClassName("inner"), function(e) {
-                        e.innerHTML = labelForToggleButton(true);
-                    });
-                    onEachLazy(inner_toggle.getElementsByClassName("toggle-label"), function(e) {
-                        e.style.display = "inline-block";
-                        if (extra === true) {
-                            e.innerHTML = " Show " + e.childNodes[0].innerHTML;
-                        }
-                    });
-                }
-            }
-            if (e.parentNode.id === "main") {
-                var otherMessage = "";
-                var fontSize;
-                var extraClass;
-
-                if (hasClass(e, "type-decl")) {
-                    // We do something special for these
-                    return;
-                } else if (hasClass(e, "non-exhaustive")) {
-                    otherMessage = "&nbsp;This&nbsp;";
-                    if (hasClass(e, "non-exhaustive-struct")) {
-                        otherMessage += "struct";
-                    } else if (hasClass(e, "non-exhaustive-enum")) {
-                        otherMessage += "enum";
-                    } else if (hasClass(e, "non-exhaustive-variant")) {
-                        otherMessage += "enum variant";
-                    } else if (hasClass(e, "non-exhaustive-type")) {
-                        otherMessage += "type";
-                    }
-                    otherMessage += "&nbsp;is&nbsp;marked&nbsp;as&nbsp;non-exhaustive";
-                } else if (hasClass(e.childNodes[0], "impl-items")) {
-                    extraClass = "marg-left";
-                }
-
-                e.parentNode.insertBefore(
-                    createToggle(
-                        toggle,
-                        otherMessage,
-                        fontSize,
-                        extraClass,
-                        true),
-                    e);
-                if (hasClass(e, "non-exhaustive") === true) {
-                    collapseDocs(e.previousSibling.childNodes[0], "toggle");
-                }
-            }
-        }
-
-        onEachLazy(document.getElementsByClassName("docblock"), buildToggleWrapper);
-
         var pageId = getPageId();
         if (pageId !== null) {
             expandSection(pageId);
@@ -1438,15 +1252,31 @@ function hideThemeButtonState() {
         document.execCommand('copy');
         document.body.removeChild(el);
 
-        but.textContent = '✓';
+        // There is always one children, but multiple childNodes.
+        but.children[0].style.display = 'none';
+
+        var tmp;
+        if (but.childNodes.length < 2) {
+            tmp = document.createTextNode('✓');
+            but.appendChild(tmp);
+        } else {
+            onEachLazy(but.childNodes, function(e) {
+                if (e.nodeType === Node.TEXT_NODE) {
+                    tmp = e;
+                    return true;
+                }
+            });
+            tmp.textContent = '✓';
+        }
 
         if (reset_button_timeout !== null) {
             window.clearTimeout(reset_button_timeout);
         }
 
         function reset_button() {
-            but.textContent = '⎘';
+            tmp.textContent = '';
             reset_button_timeout = null;
+            but.children[0].style.display = "";
         }
 
         reset_button_timeout = window.setTimeout(reset_button, 1000);
index d3fe59e8d0b01d7b97d3073aacec4a9f3196da5d..aaa2525644f117fa2318e26f5fb4d1c09c39c36c 100644 (file)
@@ -206,7 +206,6 @@ li {
        max-width: none;
        overflow: visible;
        margin-left: 0px;
-       min-width: 70em;
 }
 
 nav.sub {
@@ -357,7 +356,7 @@ nav.sub {
        padding-left: 0;
 }
 
-.rustdoc:not(.source) .example-wrap {
+.rustdoc .example-wrap {
        display: inline-flex;
        margin-bottom: 10px;
 }
@@ -370,8 +369,6 @@ nav.sub {
 .example-wrap > pre.line-number {
        overflow: initial;
        border: 1px solid;
-       border-top-left-radius: 5px;
-       border-bottom-left-radius: 5px;
        padding: 13px 8px;
        text-align: right;
 }
@@ -381,7 +378,7 @@ nav.sub {
        overflow-x: auto;
 }
 
-.rustdoc:not(.source) .example-wrap > pre {
+.rustdoc .example-wrap > pre {
        margin: 0;
 }
 
@@ -395,15 +392,14 @@ nav.sub {
        table-layout: fixed;
 }
 
-.content pre.line-numbers {
-       float: left;
-       border: none;
+.content > .example-wrap pre.line-numbers {
        position: relative;
-
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
+       border-top-left-radius: 5px;
+       border-bottom-left-radius: 5px;
 }
 .line-numbers span {
        cursor: pointer;
@@ -1321,11 +1317,12 @@ h4 > .notable-traits {
 }
 
 #copy-path {
-       height: 30px;
-       font-size: 18px;
        margin-left: 10px;
-       padding: 0 6px;
-       width: 28px;
+       padding: 0;
+       padding-left: 2px;
+}
+#copy-path> img {
+       margin-bottom: 2px;
 }
 
 #theme-choices {
@@ -1790,6 +1787,18 @@ details.rustdoc-toggle > summary::before {
        cursor: pointer;
 }
 
+details.rustdoc-toggle.top-doc > summary,
+details.rustdoc-toggle.top-doc > summary::before,
+details.rustdoc-toggle.non-exhaustive > summary,
+details.rustdoc-toggle.non-exhaustive > summary::before {
+       font-family: 'Fira Sans';
+       font-size: 16px;
+}
+
+details.non-exhaustive {
+       margin-bottom: 8px;
+}
+
 details.rustdoc-toggle > summary.hideme::before {
        position: relative;
 }
index aace0b3c037caf6be61d86ed5f3b9485ef0d0dec..aafb7f6300ea476aee1b7bc098401e68cd75eb18 100644 (file)
@@ -53,7 +53,7 @@ span code {
 .docblock code, .docblock-short code {
        background-color: #191f26;
 }
-pre {
+pre, .rustdoc.source .example-wrap {
        color: #e6e1cf;
        background-color: #191f26;
 }
@@ -509,7 +509,7 @@ kbd {
        color: #fff;
 }
 
-#theme-picker > img, #settings-menu > img {
+#theme-picker > img, #settings-menu > img, #copy-path > img {
        filter: invert(100);
 }
 
index c23e95ce107a9431410efedb309d947a72225d65..715605d7b3785cfa0ea4dd962f40168915afb086 100644 (file)
@@ -26,7 +26,7 @@ h4:not(.method):not(.type):not(.tymethod) {
 .docblock code, .docblock-short code {
        background-color: #2A2A2A;
 }
-pre {
+pre, .rustdoc.source .example-wrap {
        background-color: #2A2A2A;
 }
 
index 9330972121073b31b29705c793f806748b94af38..60ed8898793875504b60ac54d027699f55c7473d 100644 (file)
@@ -28,7 +28,7 @@ h4:not(.method):not(.type):not(.tymethod) {
 .docblock code, .docblock-short code {
        background-color: #F5F5F5;
 }
-pre {
+pre, .rustdoc.source .example-wrap {
        background-color: #F5F5F5;
 }
 
index 2b73bd5d52ee614988b79b14eea08600ed5f2f5d..1abb1f7294a18f9eb04898cee963e22f6074003d 100644 (file)
@@ -41,6 +41,9 @@
 /// The file contents of `wheel.svg`, the icon used for the settings button.
 crate static WHEEL_SVG: &[u8] = include_bytes!("static/wheel.svg");
 
+/// The file contents of `clipboard.svg`, the icon used for the "copy path" button.
+crate static CLIPBOARD_SVG: &[u8] = include_bytes!("static/clipboard.svg");
+
 /// The file contents of `down-arrow.svg`, the icon used for the crate choice combobox.
 crate static DOWN_ARROW_SVG: &[u8] = include_bytes!("static/down-arrow.svg");
 
index 2a25b595625c9fbcc211c35c482120222f06a422..169ef015fa88c71bff5c03da04b59b27fb83a1b5 100644 (file)
@@ -82,8 +82,6 @@
 use rustc_session::getopts;
 use rustc_session::{early_error, early_warn};
 
-use crate::clean::utils::doc_rust_lang_org_channel;
-
 /// A macro to create a FxHashMap.
 ///
 /// Example:
@@ -606,10 +604,7 @@ fn usage(argv0: &str) {
     }
     println!("{}", options.usage(&format!("{} [options] <input>", argv0)));
     println!("    @path               Read newline separated options from `path`\n");
-    println!(
-        "More information available at https://doc.rust-lang.org/{}/rustdoc/what-is-rustdoc.html",
-        doc_rust_lang_org_channel()
-    );
+    println!("More information available at https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html")
 }
 
 /// A result type used by several functions under `main()`.
index c8b82fb1563dfa63d801ba92320c873bc36f028c..e65fcf057f15c1605bd09865c214a90f604e1b8a 100644 (file)
@@ -213,7 +213,13 @@ fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
 
                 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());
+                // The `expect_real()` should be okay because `local_def_id_to_hir_id`
+                // would presumably panic if a fake `DefIndex` were passed.
+                let hir_id = self
+                    .ctx
+                    .tcx
+                    .hir()
+                    .local_def_id_to_hir_id(i.def_id.expect_real().expect_local());
                 let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
                 // `missing_docs` is allow-by-default, so don't treat this as ignoring the item
                 // unless the user had an explicit `allow`
index c5fb54e52c7c471f4ed032008f35e3e4097ad948..8838dc57d5a059f96bcdf1a0af5e4b5b8aefcb21 100644 (file)
@@ -1207,7 +1207,11 @@ fn resolve_link(
             // item can be non-local e.g. when using #[doc(primitive = "pointer")]
             if let Some((src_id, dst_id)) = id
                 .as_local()
-                .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id)))
+                // The `expect_real()` should be okay because `local_def_id_to_hir_id`
+                // would presumably panic if a fake `DefIndex` were passed.
+                .and_then(|dst_id| {
+                    item.def_id.expect_real().as_local().map(|src_id| (src_id, dst_id))
+                })
             {
                 use rustc_hir::def_id::LOCAL_CRATE;
 
@@ -2018,12 +2022,8 @@ fn disambiguator_error(
 ) {
     diag_info.link_range = disambiguator_range;
     report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |diag, _sp| {
-        let msg = format!(
-            "see https://doc.rust-lang.org/{}/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators \
-             for more info about disambiguators",
-            crate::doc_rust_lang_org_channel(),
-        );
-        diag.note(&msg);
+        let msg = "see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators";
+        diag.note(msg);
     });
 }
 
index 7362cfcb71b7eec1e948efb5217f3ef50f468ee9..ba698474f16298386a2596b1890ffc90d65e3e44 100644 (file)
@@ -71,7 +71,9 @@ fn add_test(&mut self, _: String, config: LangString, _: usize) {
     {
         return false;
     }
-    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local());
+    // The `expect_real()` should be okay because `local_def_id_to_hir_id`
+    // would presumably panic if a fake `DefIndex` were passed.
+    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_real().expect_local());
     if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden)
         || inherits_doc_hidden(cx.tcx, hir_id)
     {
index b61c24f3521303d442fa86fe691bc8e6acc15103..c78cf18a07f19faa3e51f15220bca39f47d437e0 160000 (submodule)
@@ -1 +1 @@
-Subproject commit b61c24f3521303d442fa86fe691bc8e6acc15103
+Subproject commit c78cf18a07f19faa3e51f15220bca39f47d437e0
index 1e0d22cbce40453443f465fd7e82ca482514b4fa..eef6d99d2a91c044d6a1706e6cec80a43b709a78 100644 (file)
@@ -2,6 +2,7 @@
 
 // == Test [gdb|lldb]-[command|check] are parsed correctly ===
 // should-fail
+// needs-run-enabled
 // compile-flags:-g
 
 // === GDB TESTS ===================================================================================
index 8a445433ab65fd61af91d318f8751b4581cc107b..dc06a485a8fc124bd26a7779b81c9bd0ebd1dbef 100644 (file)
    12|      1|    if b {
    13|      1|        println!("non_async_func println in block");
    14|      1|    }
+                   ^0
    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.
+   17|       |
+   18|       |
+   19|       |
    20|      1|async fn async_func() {
    21|      1|    println!("async_func was covered");
    22|      1|    let b = true;
@@ -26,9 +27,9 @@
                    ^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.
+   28|       |
+   29|       |
+   30|       |
    31|      1|async fn async_func_just_println() {
    32|      1|    println!("async_func_just_println was covered");
    33|      1|}
index a39e3a16fc64ba088e3c9795d834a18df7552530..5715e0cc269f4f80eb0d06be405fbff921075527 100644 (file)
@@ -37,7 +37,7 @@
    37|      0|            countdown = 10;
    38|      0|        }
    39|      0|        "alt string 2".to_owned()
-   40|      1|    };
+   40|      0|    };
    41|      1|    println!(
    42|      1|        "The string or alt: {}"
    43|      1|        ,
   125|      0|            countdown = 10;
   126|      0|        }
   127|      0|        "closure should be unused".to_owned()
-  128|      1|    };
-  129|      1|
+  128|      0|    };
+  129|       |
   130|      1|    let mut countdown = 10;
   131|      1|    let _short_unused_closure = | _unused_arg: u8 | countdown += 1;
                                                                   ^0
-  132|      1|
-  133|      1|    // Macros can sometimes confuse the coverage results. Compare this next assignment, with an
-  134|      1|    // unused closure that invokes the `println!()` macro, with the closure assignment above, that
-  135|      1|    // does not use a macro. The closure above correctly shows `0` executions.
-  136|      1|    let _short_unused_closure = | _unused_arg: u8 | println!("not called");
-  137|      1|    // The closure assignment above is executed, with a line count of `1`, but the `println!()`
-  138|      1|    // could not have been called, and yet, there is no indication that it wasn't...
-  139|      1|
-  140|      1|    // ...but adding block braces gives the expected result, showing the block was not executed.
+  132|       |
+  133|       |
+  134|      1|    let short_used_covered_closure_macro = | used_arg: u8 | println!("called");
+  135|      1|    let short_used_not_covered_closure_macro = | used_arg: u8 | println!("not called");
+                                                                              ^0
+  136|      1|    let _short_unused_closure_macro = | _unused_arg: u8 | println!("not called");
+                                                                        ^0
+  137|       |
+  138|       |
+  139|       |
+  140|       |
   141|      1|    let _short_unused_closure_block = | _unused_arg: u8 | { println!("not called") };
                                                                         ^0
-  142|      1|
+  142|       |
   143|      1|    let _shortish_unused_closure = | _unused_arg: u8 | {
   144|      0|        println!("not called")
-  145|      1|    };
-  146|      1|
+  145|      0|    };
+  146|       |
   147|      1|    let _as_short_unused_closure = |
   148|       |        _unused_arg: u8
-  149|      1|    | { println!("not called") };
-                    ^0
-  150|      1|
+  149|      0|    | { println!("not called") };
+  150|       |
   151|      1|    let _almost_as_short_unused_closure = |
   152|       |        _unused_arg: u8
-  153|      1|    | { println!("not called") }
-                    ^0
-  154|      1|    ;
-  155|      1|}
+  153|      0|    | { println!("not called") }
+  154|       |    ;
+  155|       |
+  156|       |
+  157|       |
+  158|       |
+  159|       |
+  160|      1|    let _short_unused_closure_line_break_no_block = | _unused_arg: u8 |
+  161|      0|println!("not called")
+  162|       |    ;
+  163|       |
+  164|      1|    let _short_unused_closure_line_break_no_block2 =
+  165|       |        | _unused_arg: u8 |
+  166|      0|            println!(
+  167|      0|                "not called"
+  168|      0|            )
+  169|       |    ;
+  170|       |
+  171|      1|    let short_used_not_covered_closure_line_break_no_block_embedded_branch =
+  172|      1|        | _unused_arg: u8 |
+  173|      0|            println!(
+  174|      0|                "not called: {}",
+  175|      0|                if is_true { "check" } else { "me" }
+  176|      0|            )
+  177|       |    ;
+  178|       |
+  179|      1|    let short_used_not_covered_closure_line_break_block_embedded_branch =
+  180|      1|        | _unused_arg: u8 |
+  181|      0|        {
+  182|      0|            println!(
+  183|      0|                "not called: {}",
+  184|      0|                if is_true { "check" } else { "me" }
+  185|       |            )
+  186|      0|        }
+  187|       |    ;
+  188|       |
+  189|      1|    let short_used_covered_closure_line_break_no_block_embedded_branch =
+  190|      1|        | _unused_arg: u8 |
+  191|      1|            println!(
+  192|      1|                "not called: {}",
+  193|      1|                if is_true { "check" } else { "me" }
+                                                            ^0
+  194|      1|            )
+  195|       |    ;
+  196|       |
+  197|      1|    let short_used_covered_closure_line_break_block_embedded_branch =
+  198|      1|        | _unused_arg: u8 |
+  199|      1|        {
+  200|      1|            println!(
+  201|      1|                "not called: {}",
+  202|      1|                if is_true { "check" } else { "me" }
+                                                            ^0
+  203|       |            )
+  204|      1|        }
+  205|       |    ;
+  206|       |
+  207|      1|    if is_false {
+  208|      0|        short_used_not_covered_closure_macro(0);
+  209|      0|        short_used_not_covered_closure_line_break_no_block_embedded_branch(0);
+  210|      0|        short_used_not_covered_closure_line_break_block_embedded_branch(0);
+  211|      1|    }
+  212|      1|    short_used_covered_closure_macro(0);
+  213|      1|    short_used_covered_closure_line_break_no_block_embedded_branch(0);
+  214|      1|    short_used_covered_closure_line_break_block_embedded_branch(0);
+  215|      1|}
 
index a030035f13bae284c41182e4eb305b9e84e8bba3..87f7014760edf345a5e262dae753eaf7b8bf3ba9 100644 (file)
    14|       |
    15|       |macro_rules! on_error {
    16|       |    ($value:expr, $error_message:expr) => {
-   17|      0|        $value.or_else(|e| {
-   18|      0|            let message = format!($error_message, e);
-   19|      0|            if message.len() > 0 {
-   20|      0|                println!("{}", message);
-   21|      0|                Ok(String::from("ok"))
+   17|       |        $value.or_else(|e| { // FIXME(85000): no coverage in closure macros
+   18|       |            let message = format!($error_message, e);
+   19|       |            if message.len() > 0 {
+   20|       |                println!("{}", message);
+   21|       |                Ok(String::from("ok"))
    22|       |            } else {
-   23|      0|                bail!("error");
+   23|       |                bail!("error");
    24|       |            }
-   25|      0|        })
+   25|       |        })
    26|       |    };
    27|       |}
    28|       |
index a954eb3037832ef35b66dd59be8e59561daecf4c..2b5418132c307227f6cd2686b886a5b8c0082032 100644 (file)
    14|       |
    15|       |macro_rules! on_error {
    16|       |    ($value:expr, $error_message:expr) => {
-   17|      0|        $value.or_else(|e| {
-   18|      0|            let message = format!($error_message, e);
-   19|      0|            if message.len() > 0 {
-   20|      0|                println!("{}", message);
-   21|      0|                Ok(String::from("ok"))
+   17|       |        $value.or_else(|e| { // FIXME(85000): no coverage in closure macros
+   18|       |            let message = format!($error_message, e);
+   19|       |            if message.len() > 0 {
+   20|       |                println!("{}", message);
+   21|       |                Ok(String::from("ok"))
    22|       |            } else {
-   23|      0|                bail!("error");
+   23|       |                bail!("error");
    24|       |            }
-   25|      0|        })
+   25|       |        })
    26|       |    };
    27|       |}
    28|       |
index 656a26597759d337bac841ad5a4bfa956cac3a9b..2d8a98a5d0c92f6a19ebc1c7b3fa63a63515f57c 100644 (file)
@@ -5,6 +5,7 @@
     5|      1|    if true {
     6|      1|        countdown = 10;
     7|      1|    }
+                   ^0
     8|       |
     9|       |    const B: u32 = 100;
    10|      1|    let x = if countdown > 7 {
@@ -24,6 +25,7 @@
    24|      1|    if true {
    25|      1|        countdown = 10;
    26|      1|    }
+                   ^0
    27|       |
    28|      1|    if countdown > 7 {
    29|      1|        countdown -= 4;
@@ -42,6 +44,7 @@
    41|      1|        if true {
    42|      1|            countdown = 10;
    43|      1|        }
+                       ^0
    44|       |
    45|      1|        if countdown > 7 {
    46|      1|            countdown -= 4;
    53|       |        } else {
    54|      0|            return;
    55|       |        }
-   56|       |    } // Note: closing brace shows uncovered (vs. `0` for implicit else) because condition literal
-   57|       |      // `true` was const-evaluated. The compiler knows the `if` block will be executed.
+   56|      0|    }
+   57|       |
    58|       |
    59|      1|    let mut countdown = 0;
    60|      1|    if true {
    61|      1|        countdown = 1;
    62|      1|    }
+                   ^0
    63|       |
    64|      1|    let z = if countdown > 7 {
                       ^0
index 1b6bb9ff8891de6c5ad657868580e8d31afe28ff..7ae0e978808e7e7c717422a44ed141ae1e43b37d 100644 (file)
@@ -9,7 +9,7 @@
     8|      1|//!     assert_eq!(1, 1);
     9|       |//! } else {
    10|       |//!     // this is not!
-   11|       |//!     assert_eq!(1, 2);
+   11|      0|//!     assert_eq!(1, 2);
    12|       |//! }
    13|      1|//! ```
    14|       |//!
@@ -84,7 +84,7 @@
    74|      1|    if true {
    75|      1|        assert_eq!(1, 1);
    76|       |    } else {
-   77|       |        assert_eq!(1, 2);
+   77|      0|        assert_eq!(1, 2);
    78|       |    }
    79|      1|}
    80|       |
index fab5be41901c9b544ba12e3379b30cb96d331177..fe6a9e93cbf710ab516bdc1e2b283ed7ffae99eb 100644 (file)
    19|      1|    if true {
    20|      1|        println!("Exiting with error...");
    21|      1|        return Err(1);
-   22|       |    }
-   23|       |
-   24|       |    let _ = Firework { strength: 1000 };
-   25|       |
-   26|       |    Ok(())
+   22|      0|    }
+   23|      0|
+   24|      0|    let _ = Firework { strength: 1000 };
+   25|      0|
+   26|      0|    Ok(())
    27|      1|}
    28|       |
    29|       |// Expected program output:
index 7b38ffb87cba89b257125b6585d09100014763df..8e8bc0fd1894338f3f7b0579ee35ca6c02baf744 100644 (file)
    30|      1|    if true {
    31|      1|        println!("Exiting with error...");
    32|      1|        return Err(1);
-   33|       |    } // The remaining lines below have no coverage because `if true` (with the constant literal
-   34|       |      // `true`) is guaranteed to execute the `then` block, which is also guaranteed to `return`.
-   35|       |      // Thankfully, in the normal case, conditions are not guaranteed ahead of time, and as shown
-   36|       |      // in other tests, the lines below would have coverage (which would show they had `0`
-   37|       |      // executions, assuming the condition still evaluated to `true`).
-   38|       |
-   39|       |    let _ = Firework { strength: 1000 };
-   40|       |
-   41|       |    Ok(())
+   33|      0|    }
+   34|      0|
+   35|      0|
+   36|      0|
+   37|      0|
+   38|      0|
+   39|      0|    let _ = Firework { strength: 1000 };
+   40|      0|
+   41|      0|    Ok(())
    42|      1|}
    43|       |
    44|       |// Expected program output:
index 81d5c7d90346d08a397e577d120352dbe465f124..5d572db7cc60da3b6b90c2a9dd799c7eda9143ed 100644 (file)
@@ -9,23 +9,23 @@
     9|      1|    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    10|      1|        if true {
    11|      1|            if false {
-   12|       |                while true {
-   13|       |                }
+   12|      0|                while true {
+   13|      0|                }
    14|      1|            }
-   15|      1|            write!(f, "error")?;
-                                            ^0
-   16|       |        } else {
-   17|       |        }
+   15|      1|            write!(f, "cool")?;
+                                           ^0
+   16|      0|        } else {
+   17|      0|        }
    18|       |
    19|     10|        for i in 0..10 {
    20|     10|            if true {
    21|     10|                if false {
-   22|       |                    while true {}
+   22|      0|                    while true {}
    23|     10|                }
-   24|     10|                write!(f, "error")?;
-                                                ^0
-   25|       |            } else {
-   26|       |            }
+   24|     10|                write!(f, "cool")?;
+                                               ^0
+   25|      0|            } else {
+   26|      0|            }
    27|       |        }
    28|      1|        Ok(())
    29|      1|    }
    34|       |impl std::fmt::Display for DisplayTest {
    35|      1|    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    36|      1|        if false {
-   37|       |        } else {
+   37|      0|        } else {
    38|      1|            if false {
-   39|       |                while true {}
+   39|      0|                while true {}
    40|      1|            }
-   41|      1|            write!(f, "error")?;
-                                            ^0
+   41|      1|            write!(f, "cool")?;
+                                           ^0
    42|       |        }
    43|     10|        for i in 0..10 {
    44|     10|            if false {
-   45|       |            } else {
+   45|      0|            } else {
    46|     10|                if false {
-   47|       |                    while true {}
+   47|      0|                    while true {}
    48|     10|                }
-   49|     10|                write!(f, "error")?;
-                                                ^0
+   49|     10|                write!(f, "cool")?;
+                                               ^0
    50|       |            }
    51|       |        }
    52|      1|        Ok(())
index c4a7b0cc7e9f3798b9a5fa468eb18e6ce7b8b60f..324b9138c4d9c10271c007767fbc080d9f061fb1 100644 (file)
    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|}
+   14|       |#[no_coverage]
+   15|       |fn do_not_add_coverage_not_called() {
+   16|       |    println!("not called and not covered");
+   17|       |}
+   18|       |
+   19|      1|fn add_coverage_1() {
+   20|      1|    println!("called and covered");
+   21|      1|}
+   22|       |
+   23|      1|fn add_coverage_2() {
+   24|      1|    println!("called and covered");
+   25|      1|}
+   26|       |
+   27|      0|fn add_coverage_not_called() {
+   28|      0|    println!("not called but covered");
+   29|      0|}
+   30|       |
+   31|      1|fn main() {
+   32|      1|    do_not_add_coverage_1();
+   33|      1|    do_not_add_coverage_2();
+   34|      1|    add_coverage_1();
+   35|      1|    add_coverage_2();
+   36|      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
deleted file mode 100644 (file)
index 16eaf7c..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-    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 c77ee5ddc207b2715e9585d9ee70aa60a93b95b8..114507dc9fd2add172507e4fbfeaa01083c792b1 100644 (file)
    29|       |//   2. Since the `panic_unwind.rs` test is allowed to unwind, it is also allowed to execute the
    30|       |//      normal program exit cleanup, including writing out the current values of the coverage
    31|       |//      counters.
-   32|       |//   3. The coverage results show (interestingly) that the `panic!()` call did execute, but it does
-   33|       |//      not show coverage of the `if countdown == 1` branch in `main()` that calls
-   34|       |//      `might_panic(true)` (causing the call to `panic!()`).
-   35|       |//   4. The reason `main()`s `if countdown == 1` branch, calling `might_panic(true)`, appears
-   36|       |//      "uncovered" is, InstrumentCoverage (intentionally) treats `TerminatorKind::Call` terminators
-   37|       |//      as non-branching, because when a program executes normally, they always are. Errors handled
-   38|       |//      via the try `?` operator produce error handling branches that *are* treated as branches in
-   39|       |//      coverage results. By treating calls without try `?` operators as non-branching (assumed to
-   40|       |//      return normally and continue) the coverage graph can be simplified, producing smaller,
-   41|       |//      faster binaries, and cleaner coverage results.
-   42|       |//   5. The reason the coverage results actually show `panic!()` was called is most likely because
-   43|       |//      `panic!()` is a macro, not a simple function call, and there are other `Statement`s and/or
-   44|       |//      `Terminator`s that execute with a coverage counter before the panic and unwind occur.
-   45|       |//   6. Since the common practice is not to use `panic!()` for error handling, the coverage
-   46|       |//      implementation avoids incurring an additional cost (in program size and execution time) to
-   47|       |//      improve coverage results for an event that is generally not "supposed" to happen.
-   48|       |//   7. FIXME(#78544): This issue describes a feature request for a proposed option to enable
-   49|       |//      more accurate coverage results for tests that intentionally panic.
 
index 5adeef7d0850b462b369da348651e695db38307b..2d4c57f451a2de3871bb0223d6cfbd72b71d2dac 100644 (file)
@@ -1,6 +1,6 @@
     1|      1|fn main() {
     2|      1|    if false {
-    3|       |        loop {}
+    3|      0|        loop {}
     4|      1|    }
     5|      1|}
 
index 6171d95ff5543d458b6f0f657b2c771a7cbae541..959d48ce9db160540aa5fcfe86f55e9619d111f1 100644 (file)
@@ -14,9 +14,9 @@ fn non_async_func() {
     }
 }
 
-// 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;
@@ -25,9 +25,9 @@ async fn async_func() {
     }
 }
 
-// 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");
 }
index 796512f0c71ed2d1f5667a8ac5b2fb386165a2ef..32ec0bcdf8c99d4467a713b5a619884f978ca648 100644 (file)
@@ -130,14 +130,14 @@ fn main() {
     let mut countdown = 10;
     let _short_unused_closure = | _unused_arg: u8 | countdown += 1;
 
-    // Macros can sometimes confuse the coverage results. Compare this next assignment, with an
-    // unused closure that invokes the `println!()` macro, with the closure assignment above, that
-    // does not use a macro. The closure above correctly shows `0` executions.
-    let _short_unused_closure = | _unused_arg: u8 | println!("not called");
-    // The closure assignment above is executed, with a line count of `1`, but the `println!()`
-    // could not have been called, and yet, there is no indication that it wasn't...
-
-    // ...but adding block braces gives the expected result, showing the block was not executed.
+
+    let short_used_covered_closure_macro = | used_arg: u8 | println!("called");
+    let short_used_not_covered_closure_macro = | used_arg: u8 | println!("not called");
+    let _short_unused_closure_macro = | _unused_arg: u8 | println!("not called");
+
+
+
+
     let _short_unused_closure_block = | _unused_arg: u8 | { println!("not called") };
 
     let _shortish_unused_closure = | _unused_arg: u8 | {
@@ -152,4 +152,64 @@ fn main() {
         _unused_arg: u8
     | { println!("not called") }
     ;
+
+
+
+
+
+    let _short_unused_closure_line_break_no_block = | _unused_arg: u8 |
+println!("not called")
+    ;
+
+    let _short_unused_closure_line_break_no_block2 =
+        | _unused_arg: u8 |
+            println!(
+                "not called"
+            )
+    ;
+
+    let short_used_not_covered_closure_line_break_no_block_embedded_branch =
+        | _unused_arg: u8 |
+            println!(
+                "not called: {}",
+                if is_true { "check" } else { "me" }
+            )
+    ;
+
+    let short_used_not_covered_closure_line_break_block_embedded_branch =
+        | _unused_arg: u8 |
+        {
+            println!(
+                "not called: {}",
+                if is_true { "check" } else { "me" }
+            )
+        }
+    ;
+
+    let short_used_covered_closure_line_break_no_block_embedded_branch =
+        | _unused_arg: u8 |
+            println!(
+                "not called: {}",
+                if is_true { "check" } else { "me" }
+            )
+    ;
+
+    let short_used_covered_closure_line_break_block_embedded_branch =
+        | _unused_arg: u8 |
+        {
+            println!(
+                "not called: {}",
+                if is_true { "check" } else { "me" }
+            )
+        }
+    ;
+
+    if is_false {
+        short_used_not_covered_closure_macro(0);
+        short_used_not_covered_closure_line_break_no_block_embedded_branch(0);
+        short_used_not_covered_closure_line_break_block_embedded_branch(0);
+    }
+    short_used_covered_closure_macro(0);
+    short_used_covered_closure_line_break_no_block_embedded_branch(0);
+    short_used_covered_closure_line_break_block_embedded_branch(0);
 }
index 10e434007b88487219b3659dff873de59e0c07bc..5e3b00d1ef54b4c34a1121f6929a8cf8ef9fe7b1 100644 (file)
@@ -14,7 +14,7 @@ macro_rules! bail {
 
 macro_rules! on_error {
     ($value:expr, $error_message:expr) => {
-        $value.or_else(|e| {
+        $value.or_else(|e| { // FIXME(85000): no coverage in closure macros
             let message = format!($error_message, e);
             if message.len() > 0 {
                 println!("{}", message);
index bcdfd11f8990da2eaab02e0a592f637640981407..e3e89e9c8b3c987e675d463c4b4ad9c5de4b209b 100644 (file)
@@ -14,7 +14,7 @@ macro_rules! bail {
 
 macro_rules! on_error {
     ($value:expr, $error_message:expr) => {
-        $value.or_else(|e| {
+        $value.or_else(|e| { // FIXME(85000): no coverage in closure macros
             let message = format!($error_message, e);
             if message.len() > 0 {
                 println!("{}", message);
index 8a2a0b53e5862f3031364e1e340834aedfbefda8..057599d1b471ab2e12a63f6a4a0b5b2fc9a3f654 100644 (file)
@@ -53,8 +53,8 @@ fn main() {
         } else {
             return;
         }
-    } // Note: closing brace shows uncovered (vs. `0` for implicit else) because condition literal
-      // `true` was const-evaluated. The compiler knows the `if` block will be executed.
+    }
+
 
     let mut countdown = 0;
     if true {
index cbeda35d3b8cfe8a5b226f769282d568f9689012..18b38868496d4bc10a699e6fe68035adaf2690e2 100644 (file)
@@ -30,11 +30,11 @@ fn main() -> Result<(),u8> {
     if true {
         println!("Exiting with error...");
         return Err(1);
-    } // The remaining lines below have no coverage because `if true` (with the constant literal
-      // `true`) is guaranteed to execute the `then` block, which is also guaranteed to `return`.
-      // Thankfully, in the normal case, conditions are not guaranteed ahead of time, and as shown
-      // in other tests, the lines below would have coverage (which would show they had `0`
-      // executions, assuming the condition still evaluated to `true`).
+    }
+
+
+
+
 
     let _ = Firework { strength: 1000 };
 
index 4d9bbad3367f6ef50c9d072327ab996ec5d62cb9..7116ce47f4b9dcdf28bbd8fd161509f0e74faa6a 100644 (file)
@@ -12,7 +12,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                 while true {
                 }
             }
-            write!(f, "error")?;
+            write!(f, "cool")?;
         } else {
         }
 
@@ -21,7 +21,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                 if false {
                     while true {}
                 }
-                write!(f, "error")?;
+                write!(f, "cool")?;
             } else {
             }
         }
@@ -38,7 +38,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
             if false {
                 while true {}
             }
-            write!(f, "error")?;
+            write!(f, "cool")?;
         }
         for i in 0..10 {
             if false {
@@ -46,7 +46,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                 if false {
                     while true {}
                 }
-                write!(f, "error")?;
+                write!(f, "cool")?;
             }
         }
         Ok(())
index 300570db7e8f7a205e9d626ab556b5a431a1960a..6f8586d9f5ca642ec63c25d8c2f99ad739e1c3c1 100644 (file)
@@ -11,7 +11,26 @@ fn do_not_add_coverage_2() {
     println!("called but not covered");
 }
 
+#[no_coverage]
+fn do_not_add_coverage_not_called() {
+    println!("not called and not covered");
+}
+
+fn add_coverage_1() {
+    println!("called and covered");
+}
+
+fn add_coverage_2() {
+    println!("called and covered");
+}
+
+fn add_coverage_not_called() {
+    println!("not called but covered");
+}
+
 fn main() {
     do_not_add_coverage_1();
     do_not_add_coverage_2();
+    add_coverage_1();
+    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
deleted file mode 100644 (file)
index e19a2c4..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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 b6c0c080762b2a351f49de02d84ed037604ef533..03128c2cce6281160e6e67be0022c448825f7dc1 100644 (file)
@@ -29,21 +29,3 @@ fn main() -> Result<(), u8> {
 //   2. Since the `panic_unwind.rs` test is allowed to unwind, it is also allowed to execute the
 //      normal program exit cleanup, including writing out the current values of the coverage
 //      counters.
-//   3. The coverage results show (interestingly) that the `panic!()` call did execute, but it does
-//      not show coverage of the `if countdown == 1` branch in `main()` that calls
-//      `might_panic(true)` (causing the call to `panic!()`).
-//   4. The reason `main()`s `if countdown == 1` branch, calling `might_panic(true)`, appears
-//      "uncovered" is, InstrumentCoverage (intentionally) treats `TerminatorKind::Call` terminators
-//      as non-branching, because when a program executes normally, they always are. Errors handled
-//      via the try `?` operator produce error handling branches that *are* treated as branches in
-//      coverage results. By treating calls without try `?` operators as non-branching (assumed to
-//      return normally and continue) the coverage graph can be simplified, producing smaller,
-//      faster binaries, and cleaner coverage results.
-//   5. The reason the coverage results actually show `panic!()` was called is most likely because
-//      `panic!()` is a macro, not a simple function call, and there are other `Statement`s and/or
-//      `Terminator`s that execute with a coverage counter before the panic and unwind occur.
-//   6. Since the common practice is not to use `panic!()` for error handling, the coverage
-//      implementation avoids incurring an additional cost (in program size and execution time) to
-//      improve coverage results for an event that is generally not "supposed" to happen.
-//   7. FIXME(#78544): This issue describes a feature request for a proposed option to enable
-//      more accurate coverage results for tests that intentionally panic.
diff --git a/src/test/rustdoc-gui/source-code-page.goml b/src/test/rustdoc-gui/source-code-page.goml
new file mode 100644 (file)
index 0000000..f11c41e
--- /dev/null
@@ -0,0 +1,13 @@
+goto: file://|DOC_PATH|/../src/test_docs/lib.rs.html
+// Check that we can click on the line number.
+click: (40, 224) // This is the position of the span for line 4.
+// Unfortunately, "#4" isn't a valid query selector, so we have to go around that limitation
+// by instead getting the nth span.
+assert: (".line-numbers > span:nth-child(4)", "class", "line-highlighted")
+// We now check that the good spans are highlighted
+goto: file://|DOC_PATH|/../src/test_docs/lib.rs.html#4-6
+assert-false: (".line-numbers > span:nth-child(3)", "class", "line-highlighted")
+assert: (".line-numbers > span:nth-child(4)", "class", "line-highlighted")
+assert: (".line-numbers > span:nth-child(5)", "class", "line-highlighted")
+assert: (".line-numbers > span:nth-child(6)", "class", "line-highlighted")
+assert-false: (".line-numbers > span:nth-child(7)", "class", "line-highlighted")
diff --git a/src/test/rustdoc-ui/doc-attr2.rs b/src/test/rustdoc-ui/doc-attr2.rs
deleted file mode 100644 (file)
index 3fb4846..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#![crate_type = "lib"]
-#![deny(warnings)]
-
-#[doc(test(no_crate_inject))] //~ ERROR
-//~^ WARN
-pub fn foo() {}
-
-pub mod bar {
-    #![doc(test(no_crate_inject))] //~ ERROR
-    //~^ WARN
-}
diff --git a/src/test/rustdoc-ui/doc-attr2.stderr b/src/test/rustdoc-ui/doc-attr2.stderr
deleted file mode 100644 (file)
index 6431073..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-error: `#![doc(test(...)]` is only allowed as a crate-level attribute
-  --> $DIR/doc-attr2.rs:4:7
-   |
-LL | #[doc(test(no_crate_inject))]
-   |       ^^^^^^^^^^^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/doc-attr2.rs:2:9
-   |
-LL | #![deny(warnings)]
-   |         ^^^^^^^^
-   = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]`
-   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
-
-error: `#![doc(test(...)]` is only allowed as a crate-level attribute
-  --> $DIR/doc-attr2.rs:9:12
-   |
-LL |     #![doc(test(no_crate_inject))]
-   |            ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
-
-error: aborting due to 2 previous errors
-
diff --git a/src/test/rustdoc-ui/invalid-cfg.rs b/src/test/rustdoc-ui/invalid-cfg.rs
new file mode 100644 (file)
index 0000000..d237b86
--- /dev/null
@@ -0,0 +1,4 @@
+#![feature(doc_cfg)]
+#[doc(cfg = "x")] //~ ERROR not followed by parentheses
+#[doc(cfg(x, y))] //~ ERROR multiple `cfg` predicates
+struct S {}
diff --git a/src/test/rustdoc-ui/invalid-cfg.stderr b/src/test/rustdoc-ui/invalid-cfg.stderr
new file mode 100644 (file)
index 0000000..dae238b
--- /dev/null
@@ -0,0 +1,14 @@
+error: `cfg` is not followed by parentheses
+  --> $DIR/invalid-cfg.rs:2:7
+   |
+LL | #[doc(cfg = "x")]
+   |       ^^^^^^^^^ help: expected syntax is: `cfg(/* predicate */)`
+
+error: multiple `cfg` predicates are specified
+  --> $DIR/invalid-cfg.rs:3:14
+   |
+LL | #[doc(cfg(x, y))]
+   |              ^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/rustdoc-ui/invalid-doc-attr.rs b/src/test/rustdoc-ui/invalid-doc-attr.rs
new file mode 100644 (file)
index 0000000..de004b4
--- /dev/null
@@ -0,0 +1,32 @@
+#![crate_type = "lib"]
+#![deny(warnings)]
+
+#[doc(test(no_crate_inject))]
+//~^ ERROR can only be applied at the crate level
+//~| WARN is being phased out
+//~| HELP to apply to the crate, use an inner attribute
+//~| SUGGESTION #![doc(test(no_crate_inject))]
+#[doc(inline)]
+//~^ ERROR can only be applied to a `use` item
+//~| WARN is being phased out
+pub fn foo() {}
+
+pub mod bar {
+    #![doc(test(no_crate_inject))]
+    //~^ ERROR can only be applied at the crate level
+    //~| WARN is being phased out
+
+    #[doc(test(no_crate_inject))]
+    //~^ ERROR can only be applied at the crate level
+    //~| WARN is being phased out
+    #[doc(inline)]
+    //~^ ERROR can only be applied to a `use` item
+    //~| WARN is being phased out
+    pub fn baz() {}
+}
+
+#[doc(inline)]
+#[doc(no_inline)]
+//~^^ ERROR conflicting doc inlining attributes
+//~|  HELP remove one of the conflicting attributes
+pub use bar::baz;
diff --git a/src/test/rustdoc-ui/invalid-doc-attr.stderr b/src/test/rustdoc-ui/invalid-doc-attr.stderr
new file mode 100644 (file)
index 0000000..595ece2
--- /dev/null
@@ -0,0 +1,78 @@
+error: this attribute can only be applied at the crate level
+  --> $DIR/invalid-doc-attr.rs:4:7
+   |
+LL | #[doc(test(no_crate_inject))]
+   |       ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/invalid-doc-attr.rs:2:9
+   |
+LL | #![deny(warnings)]
+   |         ^^^^^^^^
+   = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]`
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information
+help: to apply to the crate, use an inner attribute
+   |
+LL | #![doc(test(no_crate_inject))]
+   |
+
+error: this attribute can only be applied to a `use` item
+  --> $DIR/invalid-doc-attr.rs:9:7
+   |
+LL | #[doc(inline)]
+   |       ^^^^^^ only applicable on `use` items
+...
+LL | pub fn foo() {}
+   | ------------ not a `use` item
+   |
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#docno_inlinedocinline for more information
+
+error: this attribute can only be applied at the crate level
+  --> $DIR/invalid-doc-attr.rs:15:12
+   |
+LL |     #![doc(test(no_crate_inject))]
+   |            ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information
+
+error: conflicting doc inlining attributes
+  --> $DIR/invalid-doc-attr.rs:28:7
+   |
+LL | #[doc(inline)]
+   |       ^^^^^^ this attribute...
+LL | #[doc(no_inline)]
+   |       ^^^^^^^^^ ...conflicts with this attribute
+   |
+   = help: remove one of the conflicting attributes
+
+error: this attribute can only be applied at the crate level
+  --> $DIR/invalid-doc-attr.rs:19:11
+   |
+LL |     #[doc(test(no_crate_inject))]
+   |           ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information
+
+error: this attribute can only be applied to a `use` item
+  --> $DIR/invalid-doc-attr.rs:22:11
+   |
+LL |     #[doc(inline)]
+   |           ^^^^^^ only applicable on `use` items
+...
+LL |     pub fn baz() {}
+   |     ------------ not a `use` item
+   |
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#docno_inlinedocinline for more information
+
+error: aborting due to 6 previous errors
+
index 89a61a289fdebb5f00a0c0453202523a83828d3f..1fc80b3e76c53a7d775bf2f06192a9bef0028185 100644 (file)
@@ -91,3 +91,11 @@ pub unsafe fn uses_target_feature() {
 pub fn uses_cfg_target_feature() {
     uses_target_feature();
 }
+
+// multiple attributes should be allowed
+// @has doc_cfg/fn.multiple_attrs.html \
+//  '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//  'This is supported on x and y and z only.'
+#[doc(inline, cfg(x))]
+#[doc(cfg(y), cfg(z))]
+pub fn multiple_attrs() {}
index f156d225bd79b5ee9c3846522f6d9781fdd2c9bf..bc0ad14be03e07e48465bd6e6f479adc305691b3 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="fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/details[@open=""]/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="fn.bar.html"]' 'bar'
+    // @has - '//section[@id="main"]/details/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="fn.foo.html"]' 'foo'
+    // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
     /// See [foo]
     pub fn bar() {}
 }
@@ -26,8 +26,8 @@ pub fn 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="../fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/details/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="fn.bar.html"]' 'bar'
+    // @has - '//section[@id="main"]/details/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="fn.foo.html"]' 'foo'
+    // @has - '//section[@id="main"]/details/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="../fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
+// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
 pub mod subthree {
     //! See either [foo][super::foo] or [bar][super::bar]
 }
diff --git a/src/test/ui/attributes/doc-attr2.rs b/src/test/ui/attributes/doc-attr2.rs
deleted file mode 100644 (file)
index 3fb4846..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#![crate_type = "lib"]
-#![deny(warnings)]
-
-#[doc(test(no_crate_inject))] //~ ERROR
-//~^ WARN
-pub fn foo() {}
-
-pub mod bar {
-    #![doc(test(no_crate_inject))] //~ ERROR
-    //~^ WARN
-}
diff --git a/src/test/ui/attributes/doc-attr2.stderr b/src/test/ui/attributes/doc-attr2.stderr
deleted file mode 100644 (file)
index 6431073..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-error: `#![doc(test(...)]` is only allowed as a crate-level attribute
-  --> $DIR/doc-attr2.rs:4:7
-   |
-LL | #[doc(test(no_crate_inject))]
-   |       ^^^^^^^^^^^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/doc-attr2.rs:2:9
-   |
-LL | #![deny(warnings)]
-   |         ^^^^^^^^
-   = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]`
-   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
-
-error: `#![doc(test(...)]` is only allowed as a crate-level attribute
-  --> $DIR/doc-attr2.rs:9:12
-   |
-LL |     #![doc(test(no_crate_inject))]
-   |            ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
-
-error: aborting due to 2 previous errors
-
diff --git a/src/test/ui/attributes/invalid-doc-attr.rs b/src/test/ui/attributes/invalid-doc-attr.rs
new file mode 100644 (file)
index 0000000..de004b4
--- /dev/null
@@ -0,0 +1,32 @@
+#![crate_type = "lib"]
+#![deny(warnings)]
+
+#[doc(test(no_crate_inject))]
+//~^ ERROR can only be applied at the crate level
+//~| WARN is being phased out
+//~| HELP to apply to the crate, use an inner attribute
+//~| SUGGESTION #![doc(test(no_crate_inject))]
+#[doc(inline)]
+//~^ ERROR can only be applied to a `use` item
+//~| WARN is being phased out
+pub fn foo() {}
+
+pub mod bar {
+    #![doc(test(no_crate_inject))]
+    //~^ ERROR can only be applied at the crate level
+    //~| WARN is being phased out
+
+    #[doc(test(no_crate_inject))]
+    //~^ ERROR can only be applied at the crate level
+    //~| WARN is being phased out
+    #[doc(inline)]
+    //~^ ERROR can only be applied to a `use` item
+    //~| WARN is being phased out
+    pub fn baz() {}
+}
+
+#[doc(inline)]
+#[doc(no_inline)]
+//~^^ ERROR conflicting doc inlining attributes
+//~|  HELP remove one of the conflicting attributes
+pub use bar::baz;
diff --git a/src/test/ui/attributes/invalid-doc-attr.stderr b/src/test/ui/attributes/invalid-doc-attr.stderr
new file mode 100644 (file)
index 0000000..595ece2
--- /dev/null
@@ -0,0 +1,78 @@
+error: this attribute can only be applied at the crate level
+  --> $DIR/invalid-doc-attr.rs:4:7
+   |
+LL | #[doc(test(no_crate_inject))]
+   |       ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/invalid-doc-attr.rs:2:9
+   |
+LL | #![deny(warnings)]
+   |         ^^^^^^^^
+   = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]`
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information
+help: to apply to the crate, use an inner attribute
+   |
+LL | #![doc(test(no_crate_inject))]
+   |
+
+error: this attribute can only be applied to a `use` item
+  --> $DIR/invalid-doc-attr.rs:9:7
+   |
+LL | #[doc(inline)]
+   |       ^^^^^^ only applicable on `use` items
+...
+LL | pub fn foo() {}
+   | ------------ not a `use` item
+   |
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#docno_inlinedocinline for more information
+
+error: this attribute can only be applied at the crate level
+  --> $DIR/invalid-doc-attr.rs:15:12
+   |
+LL |     #![doc(test(no_crate_inject))]
+   |            ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information
+
+error: conflicting doc inlining attributes
+  --> $DIR/invalid-doc-attr.rs:28:7
+   |
+LL | #[doc(inline)]
+   |       ^^^^^^ this attribute...
+LL | #[doc(no_inline)]
+   |       ^^^^^^^^^ ...conflicts with this attribute
+   |
+   = help: remove one of the conflicting attributes
+
+error: this attribute can only be applied at the crate level
+  --> $DIR/invalid-doc-attr.rs:19:11
+   |
+LL |     #[doc(test(no_crate_inject))]
+   |           ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information
+
+error: this attribute can only be applied to a `use` item
+  --> $DIR/invalid-doc-attr.rs:22:11
+   |
+LL |     #[doc(inline)]
+   |           ^^^^^^ only applicable on `use` items
+...
+LL |     pub fn baz() {}
+   |     ------------ not a `use` item
+   |
+   = 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 #82730 <https://github.com/rust-lang/rust/issues/82730>
+   = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#docno_inlinedocinline for more information
+
+error: aborting due to 6 previous errors
+
index 4862883fd96bdc2c7decd8dcdaa26d8de0066bd2..b976c3a13bd5927f37ab09a2d72c5dd5a5176b7c 100644 (file)
@@ -6,16 +6,11 @@
 // Unfortunately, LLVM has no "disable" option for this, so we have to set
 // "enable" to 0 instead.
 
-// compile-flags:-g -Cllvm-args=-enable-tail-merge=0 -Cllvm-args=-opt-bisect-limit=0
+// compile-flags:-g -Copt-level=0 -Cllvm-args=-enable-tail-merge=0
 // compile-flags:-Cforce-frame-pointers=yes
 // ignore-pretty issue #37195
 // ignore-emscripten spawning processes is not supported
 // ignore-sgx no processes
-// normalize-stderr-test ".*\n" -> ""
-
-// Note that above `-opt-bisect-limit=0` is used to basically disable
-// optimizations. It creates tons of output on stderr, hence we normalize
-// that away entirely.
 
 use std::env;
 
index dbb5932a9bb85a6a64cb47f1b07ecaf83e4b2ebe..d2113e4f5ecc60515960ecb0f812c67f5ce8e843 100644 (file)
@@ -1,5 +1,6 @@
 // build-pass
 // compile-flags: -C panic=unwind
+// needs-unwind
 // ignore-emscripten no panic_unwind implementation
 // ignore-wasm32     no panic_unwind implementation
 // ignore-wasm64     no panic_unwind implementation
index b79ce4a523f9671ffe2b73fef8c6c4963bbde300..bee5a7cb3ba7255fce8b1c48f497be30b7f0a277 100644 (file)
@@ -10,6 +10,8 @@ impl<T> PrintName<T> {
 
 const fn no_codegen<T>() {
     if false {
+        // This bad constant is only used in dead code in a no-codegen function... and yet we still
+        // must make sure that the build fails.
         let _ = PrintName::<T>::VOID; //~ERROR could not evaluate static initializer
     }
 }
index 16ed596628bf569b9c6198f967179e8124f08b19..7e2a60929c73d36a278389859fe79ade6c460f3e 100644 (file)
@@ -27,16 +27,16 @@ LL | #![warn(const_err, unconditional_panic)]
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
 error[E0080]: could not evaluate static initializer
-  --> $DIR/erroneous-const.rs:13:17
+  --> $DIR/erroneous-const.rs:15:17
    |
 LL |         let _ = PrintName::<T>::VOID;
    |                 ^^^^^^^^^^^^^^^^^^^^
    |                 |
    |                 referenced constant has errors
-   |                 inside `no_codegen::<i32>` at $DIR/erroneous-const.rs:13:17
+   |                 inside `no_codegen::<i32>` at $DIR/erroneous-const.rs:15:17
 ...
 LL | pub static FOO: () = no_codegen::<i32>();
-   |                      ------------------- inside `FOO` at $DIR/erroneous-const.rs:17:22
+   |                      ------------------- inside `FOO` at $DIR/erroneous-const.rs:19:22
 
 error: aborting due to previous error; 2 warnings emitted
 
diff --git a/src/test/ui/consts/const-eval/erroneous-const2.rs b/src/test/ui/consts/const-eval/erroneous-const2.rs
new file mode 100644 (file)
index 0000000..aa0f093
--- /dev/null
@@ -0,0 +1,21 @@
+//! Make sure we error on erroneous consts even if they are unused.
+#![warn(const_err, unconditional_panic)]
+
+struct PrintName<T>(T);
+impl<T> PrintName<T> {
+    const VOID: () = [()][2]; //~WARN any use of this value will cause an error
+    //~^ WARN this operation will panic at runtime
+    //~| WARN this was previously accepted by the compiler but is being phased out
+}
+
+pub static FOO: () = {
+    if false {
+        // This bad constant is only used in dead code in a static initializer... and yet we still
+        // must make sure that the build fails.
+        let _ = PrintName::<i32>::VOID; //~ERROR could not evaluate static initializer
+    }
+};
+
+fn main() {
+    FOO
+}
diff --git a/src/test/ui/consts/const-eval/erroneous-const2.stderr b/src/test/ui/consts/const-eval/erroneous-const2.stderr
new file mode 100644 (file)
index 0000000..813d3ee
--- /dev/null
@@ -0,0 +1,37 @@
+warning: this operation will panic at runtime
+  --> $DIR/erroneous-const2.rs:6:22
+   |
+LL |     const VOID: () = [()][2];
+   |                      ^^^^^^^ index out of bounds: the length is 1 but the index is 2
+   |
+note: the lint level is defined here
+  --> $DIR/erroneous-const2.rs:2:20
+   |
+LL | #![warn(const_err, unconditional_panic)]
+   |                    ^^^^^^^^^^^^^^^^^^^
+
+warning: any use of this value will cause an error
+  --> $DIR/erroneous-const2.rs:6:22
+   |
+LL |     const VOID: () = [()][2];
+   |     -----------------^^^^^^^-
+   |                      |
+   |                      index out of bounds: the length is 1 but the index is 2
+   |
+note: the lint level is defined here
+  --> $DIR/erroneous-const2.rs:2:9
+   |
+LL | #![warn(const_err, unconditional_panic)]
+   |         ^^^^^^^^^
+   = 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 #71800 <https://github.com/rust-lang/rust/issues/71800>
+
+error[E0080]: could not evaluate static initializer
+  --> $DIR/erroneous-const2.rs:15:17
+   |
+LL |         let _ = PrintName::<i32>::VOID;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+
+error: aborting due to previous error; 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0080`.
index 6f266801bdb4a65f5b6659f2f76d74b2cae35005..77e7d484071860fb5f979a1ed501a37040982d4d 100644 (file)
@@ -1,12 +1,12 @@
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:13:5
+  --> $DIR/promoted_errors.rs:15:5
    |
 LL |       0 - 1
    |       ^^^^^
    |       |
    |       attempt to compute `0_u32 - 1_u32`, which would overflow
-   |       inside `overflow` at $DIR/promoted_errors.rs:13:5
-   |       inside `X` at $DIR/promoted_errors.rs:33:29
+   |       inside `overflow` at $DIR/promoted_errors.rs:15:5
+   |       inside `X` at $DIR/promoted_errors.rs:38:29
 ...
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -18,7 +18,7 @@ LL | | };
    | |__-
    |
 note: the lint level is defined here
-  --> $DIR/promoted_errors.rs:9:9
+  --> $DIR/promoted_errors.rs:11:9
    |
 LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    |         ^^^^^^^^^
@@ -26,7 +26,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:33:28
+  --> $DIR/promoted_errors.rs:38:28
    |
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
index 892f57bfdfc1bc72c937c68c4e6bf78669b6b44b..6b17346e6ecd1456f65c34ece14b1d3e522b834a 100644 (file)
@@ -1,12 +1,12 @@
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:18:5
+  --> $DIR/promoted_errors.rs:20:5
    |
 LL |       1 / 0
    |       ^^^^^
    |       |
    |       attempt to divide `1_i32` by zero
-   |       inside `div_by_zero1` at $DIR/promoted_errors.rs:18:5
-   |       inside `X` at $DIR/promoted_errors.rs:36:29
+   |       inside `div_by_zero1` at $DIR/promoted_errors.rs:20:5
+   |       inside `X` at $DIR/promoted_errors.rs:41:29
 ...
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -18,7 +18,7 @@ LL | | };
    | |__-
    |
 note: the lint level is defined here
-  --> $DIR/promoted_errors.rs:9:9
+  --> $DIR/promoted_errors.rs:11:9
    |
 LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    |         ^^^^^^^^^
@@ -26,7 +26,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:36:28
+  --> $DIR/promoted_errors.rs:41:28
    |
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
index 6f266801bdb4a65f5b6659f2f76d74b2cae35005..77e7d484071860fb5f979a1ed501a37040982d4d 100644 (file)
@@ -1,12 +1,12 @@
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:13:5
+  --> $DIR/promoted_errors.rs:15:5
    |
 LL |       0 - 1
    |       ^^^^^
    |       |
    |       attempt to compute `0_u32 - 1_u32`, which would overflow
-   |       inside `overflow` at $DIR/promoted_errors.rs:13:5
-   |       inside `X` at $DIR/promoted_errors.rs:33:29
+   |       inside `overflow` at $DIR/promoted_errors.rs:15:5
+   |       inside `X` at $DIR/promoted_errors.rs:38:29
 ...
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
@@ -18,7 +18,7 @@ LL | | };
    | |__-
    |
 note: the lint level is defined here
-  --> $DIR/promoted_errors.rs:9:9
+  --> $DIR/promoted_errors.rs:11:9
    |
 LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    |         ^^^^^^^^^
@@ -26,7 +26,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
    = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
 
 warning: any use of this value will cause an error
-  --> $DIR/promoted_errors.rs:33:28
+  --> $DIR/promoted_errors.rs:38:28
    |
 LL | / const X: () = {
 LL | |     let _x: &'static u32 = &overflow();
index 7840f67c216c075a2597da756eadb013bb04c908..5bafea1ed46bdf1c4faa8cd048462869ab8dbec6 100644 (file)
@@ -6,6 +6,8 @@
 // build-pass
 // ignore-pass (test emits codegen-time warnings and verifies that they are not errors)
 
+//! This test ensures that when we promote code that fails to evaluate, the build still succeeds.
+
 #![warn(const_err, arithmetic_overflow, unconditional_panic)]
 
 // The only way to have promoteds that fail is in `const fn` called from `const`/`static`.
@@ -29,6 +31,9 @@ const fn oob() -> i32 {
     [1, 2, 3][4]
 }
 
+// An unused constant containing failing promoteds.
+// This should work as long as `const_err` can be turned into just a warning;
+// once it turns into a hard error, just remove `X`.
 const X: () = {
     let _x: &'static u32 = &overflow();
     //[opt_with_overflow_checks,noopt]~^ WARN any use of this value will cause an error
@@ -41,4 +46,21 @@ const fn oob() -> i32 {
     let _x: &'static i32 = &oob();
 };
 
-fn main() {}
+const fn mk_false() -> bool { false }
+
+// An actually used constant referencing failing promoteds in dead code.
+// This needs to always work.
+const Y: () = {
+    if mk_false() {
+        let _x: &'static u32 = &overflow();
+        let _x: &'static i32 = &div_by_zero1();
+        let _x: &'static i32 = &div_by_zero2();
+        let _x: &'static i32 = &div_by_zero3();
+        let _x: &'static i32 = &oob();
+    }
+    ()
+};
+
+fn main() {
+    let _y = Y;
+}
index 7ca5c647d88683ddc4d5db5afd24a6b2626da74f..c69674a6721bc0fd61f04913849a30e1e70fc4ff 100644 (file)
@@ -296,7 +296,7 @@ error[E0080]: could not evaluate static initializer
   --> $DIR/ub-wide-ptr.rs:135:5
    |
 LL |     mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ null pointer is not a valid pointer for this operation
 
 error[E0080]: could not evaluate static initializer
   --> $DIR/ub-wide-ptr.rs:139:5
index e42c65a1517d28b78d7778ea7bab45d9c5e9e301..bb95343a786a40056bcf471a8358432bd19cb2b3 100644 (file)
@@ -296,7 +296,7 @@ error[E0080]: could not evaluate static initializer
   --> $DIR/ub-wide-ptr.rs:135:5
    |
 LL |     mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ null pointer is not a valid pointer for this operation
 
 error[E0080]: could not evaluate static initializer
   --> $DIR/ub-wide-ptr.rs:139:5
index 0da4b44a96839daa9d6fb3e1941dbeb441c92704..9add1f8d5a3592615956cefb6ccb69ab5fe1a4e5 100644 (file)
@@ -1,13 +1,11 @@
-// only-x86_64
+#![feature(rustc_attrs)]
 
-#[cfg(target_arch = "x86")]
-use std::arch::x86::*;
-#[cfg(target_arch = "x86_64")]
-use std::arch::x86_64::*;
+#[rustc_args_required_const(0)]
+fn foo(_imm8: i32) {}
 
-unsafe fn pclmul(a: __m128i, b: __m128i) -> __m128i {
+fn bar() {
     let imm8 = 3;
-    _mm_clmulepi64_si128(a, b, imm8) //~ ERROR argument 3 is required to be a constant
+    foo(imm8) //~ ERROR argument 1 is required to be a constant
 }
 
 fn main() {}
index 197b2f082e5a00d13471d07642cc506c58bc9596..bad85471a6b376df719c9574b98488ba457176b7 100644 (file)
@@ -1,8 +1,8 @@
-error: argument 3 is required to be a constant
-  --> $DIR/const_arg_local.rs:10:5
+error: argument 1 is required to be a constant
+  --> $DIR/const_arg_local.rs:8:5
    |
-LL |     _mm_clmulepi64_si128(a, b, imm8)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     foo(imm8)
+   |     ^^^^^^^^^
 
 error: aborting due to previous error
 
index 25f45104d6aa1d3c75b1e64912472c32020eccfc..cea3817550eb2b101215ac0f16289dd03d53e38d 100644 (file)
@@ -1,12 +1,10 @@
-// only-x86_64
+#![feature(rustc_attrs)]
 
-#[cfg(target_arch = "x86")]
-use std::arch::x86::*;
-#[cfg(target_arch = "x86_64")]
-use std::arch::x86_64::*;
+#[rustc_args_required_const(0)]
+fn foo(_imm8: i32) {}
 
-unsafe fn pclmul(a: __m128i, b: __m128i) -> __m128i {
-    _mm_clmulepi64_si128(a, b, *&mut 42) //~ ERROR argument 3 is required to be a constant
+fn bar() {
+    foo(*&mut 42) //~ ERROR argument 1 is required to be a constant
 }
 
 fn main() {}
index 5de3ee6654ad9c47d400f9f0a905a52ae47b35fc..b24b245b3ce0c867f5539e035711208e6b5400cc 100644 (file)
@@ -1,8 +1,8 @@
-error: argument 3 is required to be a constant
-  --> $DIR/const_arg_promotable.rs:9:5
+error: argument 1 is required to be a constant
+  --> $DIR/const_arg_promotable.rs:7:5
    |
-LL |     _mm_clmulepi64_si128(a, b, *&mut 42)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     foo(*&mut 42)
+   |     ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
index 92ff264cd2b8ef959aee3a3425dc2a811fd47e5b..3dd3a2ffaf3cba83f332c2fca31e2b85592a8a5f 100644 (file)
@@ -1,12 +1,10 @@
-// only-x86_64
+#![feature(rustc_attrs)]
 
-#[cfg(target_arch = "x86")]
-use std::arch::x86::*;
-#[cfg(target_arch = "x86_64")]
-use std::arch::x86_64::*;
+#[rustc_args_required_const(0)]
+fn foo(_imm8: i32) {}
 
-unsafe fn pclmul(a: __m128i, b: __m128i, imm8: i32) -> __m128i {
-    _mm_clmulepi64_si128(a, b, imm8) //~ ERROR argument 3 is required to be a constant
+fn bar(imm8: i32) {
+    foo(imm8) //~ ERROR argument 1 is required to be a constant
 }
 
 fn main() {}
index 4acf2f0f4d1e5dfa87835df27864cfabb90ca163..9cd95a2020fcd1c2b2e0f2ddb0039568e7946e06 100644 (file)
@@ -1,8 +1,8 @@
-error: argument 3 is required to be a constant
-  --> $DIR/const_arg_wrapper.rs:9:5
+error: argument 1 is required to be a constant
+  --> $DIR/const_arg_wrapper.rs:7:5
    |
-LL |     _mm_clmulepi64_si128(a, b, imm8)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     foo(imm8)
+   |     ^^^^^^^^^
 
 error: aborting due to previous error
 
index eb726f9cb113faf53af9c91514d38a18e6a64db1..4254cda2a0084c4f81f8f53b943e2584e9483cfe 100644 (file)
@@ -74,7 +74,7 @@ error: any use of this value will cause an error
 LL |           unsafe { intrinsics::ptr_offset_from(self, origin) }
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                    |
-   |                    inbounds test failed: 0x0 is not a valid pointer
+   |                    null pointer is not a valid pointer for this operation
    |                    inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |                    inside `OFFSET_FROM_NULL` at $DIR/offset_from_ub.rs:36:14
    | 
index 082142fbbb77c4f9cf348289fec15fa8b65cd437..45203d3e271199b82e42e735f8d6685b51aa901e 100644 (file)
@@ -23,7 +23,7 @@ error: any use of this value will cause an error
 LL |         unsafe { intrinsics::offset(self, count) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                  |
-   |                  inbounds test failed: pointer must be in-bounds at offset 2, but is outside bounds of allocN which has size 1
+   |                  pointer arithmetic failed: pointer must be in-bounds at offset 2, but is outside bounds of allocN which has size 1
    |                  inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |                  inside `AFTER_END` at $DIR/offset_ub.rs:7:43
    | 
@@ -41,7 +41,7 @@ error: any use of this value will cause an error
 LL |         unsafe { intrinsics::offset(self, count) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                  |
-   |                  inbounds test failed: pointer must be in-bounds at offset 101, but is outside bounds of allocN which has size 100
+   |                  pointer arithmetic failed: pointer must be in-bounds at offset 101, but is outside bounds of allocN which has size 100
    |                  inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |                  inside `AFTER_ARRAY` at $DIR/offset_ub.rs:8:45
    | 
@@ -131,7 +131,7 @@ error: any use of this value will cause an error
 LL |         unsafe { intrinsics::offset(self, count) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                  |
-   |                  inbounds test failed: pointer must be in-bounds at offset 1, but is outside bounds of allocN which has size 0
+   |                  pointer arithmetic failed: pointer must be in-bounds at offset 1, but is outside bounds of allocN which has size 0
    |                  inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |                  inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:15:50
    | 
@@ -167,7 +167,7 @@ error: any use of this value will cause an error
 LL |         unsafe { intrinsics::offset(self, count) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                  |
-   |                  inbounds test failed: 0x0 is not a valid pointer
+   |                  pointer arithmetic failed: 0x0 is not a valid pointer
    |                  inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |                  inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:19:50
    | 
index 9ec009c55c443e4d1984e280342652c435254a59..62b33000e60b8e214fb41a2c29c2e406024f3db6 100644 (file)
@@ -4,7 +4,7 @@ error: any use of this value will cause an error
 LL |         unsafe { intrinsics::offset(self, count) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                  |
-   |                  inbounds test failed: pointer must be in-bounds at offset $TWO_WORDS, but is outside bounds of alloc2 which has size $WORD
+   |                  pointer arithmetic failed: pointer must be in-bounds at offset $TWO_WORDS, but is outside bounds of alloc2 which has size $WORD
    |                  inside `ptr::const_ptr::<impl *const usize>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |                  inside `_` at $DIR/ptr_comparisons.rs:61:34
    | 
index 6bbf67fa5408dcdd42f7df108770c68368a0e129..4fddfc44ac60a2afb753d09317d4888013303a2f 100644 (file)
@@ -1,9 +1,7 @@
-// build-fail
+// run-pass
 // aux-build:main_functions.rs
 
 #![feature(imported_main)]
 
 extern crate main_functions;
-pub use main_functions::boilerplate as main; //~ ERROR entry symbol `main` from foreign crate
-
-// FIXME: Should be run-pass
+pub use main_functions::boilerplate as main;
diff --git a/src/test/ui/entry-point/imported_main_from_extern_crate.stderr b/src/test/ui/entry-point/imported_main_from_extern_crate.stderr
deleted file mode 100644 (file)
index 8792e1e..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-error: entry symbol `main` from foreign crate is not yet supported.
-  --> $DIR/imported_main_from_extern_crate.rs:7:9
-   |
-LL | pub use main_functions::boilerplate as main;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #28937 <https://github.com/rust-lang/rust/issues/28937> for more information
-
-error: aborting due to previous error
-
index dbe700355957b1ebe76edff13adb9d7108211fca..c7bbbf114997d36b612fa344abc1b1a8f7c87ded 100644 (file)
@@ -4,7 +4,7 @@ error[E0583]: file not found for module `module_that_doesnt_exist`
 LL | mod module_that_doesnt_exist;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: to create the module `module_that_doesnt_exist`, create file "$DIR/module_that_doesnt_exist.rs"
+   = help: to create the module `module_that_doesnt_exist`, create file "$DIR/module_that_doesnt_exist.rs" or "$DIR/module_that_doesnt_exist/mod.rs"
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/feature-gates/feature-gate-const_fn.rs b/src/test/ui/feature-gates/feature-gate-const_fn.rs
deleted file mode 100644 (file)
index b97aa21..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-// Test use of advanced const fn without the `const_fn` feature gate.
-
-const fn foo() -> usize { 0 } // ok
-
-trait Foo {
-    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 {
-    const fn foo() -> u32 { 0 } //~ ERROR functions in traits cannot be declared const
-}
-
-trait Bar {}
-
-impl dyn Bar {
-    const fn baz() -> u32 { 0 } // ok
-}
-
-static FOO: usize = foo();
-const BAR: usize = foo();
-
-macro_rules! constant {
-    ($n:ident: $t:ty = $v:expr) => {
-        const $n: $t = $v;
-    }
-}
-
-constant! {
-    BAZ: usize = foo()
-}
-
-fn main() {
-    let x: [usize; foo()] = [];
-}
diff --git a/src/test/ui/feature-gates/feature-gate-const_fn.stderr b/src/test/ui/feature-gates/feature-gate-const_fn.stderr
deleted file mode 100644 (file)
index 1e7fd66..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-error[E0379]: functions in traits cannot be declared const
-  --> $DIR/feature-gate-const_fn.rs:6:5
-   |
-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: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:11:5
-   |
-LL |     const fn foo() -> u32 { 0 }
-   |     ^^^^^ functions in traits cannot be const
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0379`.
diff --git a/src/test/ui/feature-gates/feature-gate-native_link_modifiers.rs b/src/test/ui/feature-gates/feature-gate-native_link_modifiers.rs
new file mode 100644 (file)
index 0000000..2d00aa2
--- /dev/null
@@ -0,0 +1,5 @@
+#[link(name = "foo", modifiers = "")]
+//~^ ERROR: native link modifiers are experimental
+extern "C" {}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-native_link_modifiers.stderr b/src/test/ui/feature-gates/feature-gate-native_link_modifiers.stderr
new file mode 100644 (file)
index 0000000..20a2d6a
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: native link modifiers are experimental
+  --> $DIR/feature-gate-native_link_modifiers.rs:1:22
+   |
+LL | #[link(name = "foo", modifiers = "")]
+   |                      ^^^^^^^^^^^^^^
+   |
+   = note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information
+   = help: add `#![feature(native_link_modifiers)]` 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-native_link_modifiers_as_needed.rs b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.rs
new file mode 100644 (file)
index 0000000..4cf8067
--- /dev/null
@@ -0,0 +1,8 @@
+#![allow(incomplete_features)]
+#![feature(native_link_modifiers)]
+
+#[link(name = "foo", modifiers = "+as-needed")]
+//~^ ERROR: `#[link(modifiers="as-needed")]` is unstable
+extern "C" {}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.stderr b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.stderr
new file mode 100644 (file)
index 0000000..08ce807
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: `#[link(modifiers="as-needed")]` is unstable
+  --> $DIR/feature-gate-native_link_modifiers_as_needed.rs:4:34
+   |
+LL | #[link(name = "foo", modifiers = "+as-needed")]
+   |                                  ^^^^^^^^^^^^
+   |
+   = note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information
+   = help: add `#![feature(native_link_modifiers_as_needed)]` 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-native_link_modifiers_bundle.rs b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_bundle.rs
new file mode 100644 (file)
index 0000000..b2b1dc2
--- /dev/null
@@ -0,0 +1,8 @@
+#![allow(incomplete_features)]
+#![feature(native_link_modifiers)]
+
+#[link(name = "foo", modifiers = "+bundle")]
+//~^ ERROR: `#[link(modifiers="bundle")]` is unstable
+extern "C" {}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-native_link_modifiers_bundle.stderr b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_bundle.stderr
new file mode 100644 (file)
index 0000000..b3e22b0
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: `#[link(modifiers="bundle")]` is unstable
+  --> $DIR/feature-gate-native_link_modifiers_bundle.rs:4:34
+   |
+LL | #[link(name = "foo", modifiers = "+bundle")]
+   |                                  ^^^^^^^^^
+   |
+   = note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information
+   = help: add `#![feature(native_link_modifiers_bundle)]` 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-native_link_modifiers_verbatim.rs b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.rs
new file mode 100644 (file)
index 0000000..042ce0b
--- /dev/null
@@ -0,0 +1,8 @@
+#![allow(incomplete_features)]
+#![feature(native_link_modifiers)]
+
+#[link(name = "foo", modifiers = "+verbatim")]
+//~^ ERROR: `#[link(modifiers="verbatim")]` is unstable
+extern "C" {}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.stderr b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_verbatim.stderr
new file mode 100644 (file)
index 0000000..8159416
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: `#[link(modifiers="verbatim")]` is unstable
+  --> $DIR/feature-gate-native_link_modifiers_verbatim.rs:4:34
+   |
+LL | #[link(name = "foo", modifiers = "+verbatim")]
+   |                                  ^^^^^^^^^^^
+   |
+   = note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information
+   = help: add `#![feature(native_link_modifiers_verbatim)]` 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-native_link_modifiers_whole_archive.rs b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_whole_archive.rs
new file mode 100644 (file)
index 0000000..ca801e5
--- /dev/null
@@ -0,0 +1,8 @@
+#![allow(incomplete_features)]
+#![feature(native_link_modifiers)]
+
+#[link(name = "foo", modifiers = "+whole-archive")]
+//~^ ERROR: `#[link(modifiers="whole-archive")]` is unstable
+extern "C" {}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-native_link_modifiers_whole_archive.stderr b/src/test/ui/feature-gates/feature-gate-native_link_modifiers_whole_archive.stderr
new file mode 100644 (file)
index 0000000..cacaa78
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: `#[link(modifiers="whole-archive")]` is unstable
+  --> $DIR/feature-gate-native_link_modifiers_whole_archive.rs:4:34
+   |
+LL | #[link(name = "foo", modifiers = "+whole-archive")]
+   |                                  ^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #81490 <https://github.com/rust-lang/rust/issues/81490> for more information
+   = help: add `#![feature(native_link_modifiers_whole_archive)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
index c6b79f9a431710b7983b2b12967562b601d77080..fd4c6f76059aa6af2db3bcf2337a457e8c253c5a 100644 (file)
@@ -1,8 +1,13 @@
 #![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() {}
+#[derive(PartialEq, Eq)] // ensure deriving `Eq` does not enable `feature(no_coverage)`
+struct Foo {
+    a: u8,
+    b: u32,
+}
 
 #[no_coverage] //~ ERROR the `#[no_coverage]` attribute is an experimental feature
-fn requires_feature_no_coverage() {}
+fn requires_feature_no_coverage() -> bool {
+    let bar = Foo { a: 0, b: 0 };
+    bar == Foo { a: 0, b: 0 }
+}
index 04627be4aaf65f4a1b9325c1652814ecad5984fa..f7167e0b771c0689caae0ff6b566c9f1356dee4c 100644 (file)
@@ -1,12 +1,11 @@
 error[E0658]: the `#[no_coverage]` attribute is an experimental feature
-  --> $DIR/feature-gate-no_coverage.rs:7:1
+  --> $DIR/feature-gate-no_coverage.rs:9: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
 
index 3296a17a0b7ef0416d637b08717b08e3203922d9..d7daaaaa101e7d4e3db0b87a59abf2d02f2d2fda 100644 (file)
@@ -1,7 +1,5 @@
 // Test internal const fn feature gate.
 
-#![feature(const_fn)]
-
 #[rustc_const_unstable(feature="fzzzzzt")] //~ stability attributes may not be used outside
 pub const fn bazinga() {}
 
index 9df926dcf90f5582980ecedb5052bb27e9fb7a9f..48493b786d6495c376ef662b0612dd46ceeb16cb 100644 (file)
@@ -1,5 +1,5 @@
 error[E0734]: stability attributes may not be used outside of the standard library
-  --> $DIR/feature-gate-rustc_const_unstable.rs:5:1
+  --> $DIR/feature-gate-rustc_const_unstable.rs:3:1
    |
 LL | #[rustc_const_unstable(feature="fzzzzzt")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index d96a48cde9f99dd10d3ce008f9d80d4457bc1768..301a1e1341eedc692a6ffa72f328f2e8c028a347 100644 (file)
@@ -1,3 +1,5 @@
+warning: library kind `static-nobundle` has been superseded by specifying `-bundle` on library kind `static`. Try `static:-bundle`
+
 error[E0658]: kind="static-nobundle" is unstable
    |
    = note: see issue #37403 <https://github.com/rust-lang/rust/issues/37403> for more information
index 05c52f9dbead2e86370dd7457e06247be4c4dcd3..e4bfe8e8e05cae507bda21f81492bf3e91ceb0f5 100644 (file)
@@ -1,5 +1,6 @@
 #[link(name = "foo", kind = "static-nobundle")]
-//~^ ERROR: kind="static-nobundle" is unstable
+//~^ WARNING: library kind `static-nobundle` has been superseded by specifying modifier `-bundle` with library kind `static`
+//~^^ ERROR: kind="static-nobundle" is unstable
 extern "C" {}
 
 fn main() {}
index 3a3c86c34295dde938a665da62ae3bc834e0edb1..9695618207cc9289127727dee4d08b43c1e25941 100644 (file)
@@ -1,3 +1,9 @@
+warning: library kind `static-nobundle` has been superseded by specifying modifier `-bundle` with library kind `static`
+  --> $DIR/feature-gate-static-nobundle.rs:1:22
+   |
+LL | #[link(name = "foo", kind = "static-nobundle")]
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
+
 error[E0658]: kind="static-nobundle" is unstable
   --> $DIR/feature-gate-static-nobundle.rs:1:1
    |
@@ -7,6 +13,6 @@ LL | #[link(name = "foo", kind = "static-nobundle")]
    = note: see issue #37403 <https://github.com/rust-lang/rust/issues/37403> for more information
    = help: add `#![feature(static_nobundle)]` to the crate attributes to enable
 
-error: aborting due to previous error
+error: aborting due to previous error; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0658`.
index 554c67be4e0b04ef6cf4b3f61c1fcc5272d208ec..470262a15ba0dea88f7e7bf4871ae79b3def2273 100644 (file)
@@ -3,7 +3,7 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
 #![feature(staged_api)]
-#![feature(const_transmute, const_fn)]
+#![feature(const_transmute)]
 
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_const_stable(feature = "rust1", since = "1.0.0")]
index 592409ba89f9b5379571b3fae1450b26750625de..5a27ea8783a2f3b149f23057929ff84c75b8d8cc 100644 (file)
@@ -14,6 +14,7 @@ mod rusti {
           target_os = "dragonfly",
           target_os = "emscripten",
           target_os = "freebsd",
+          target_os = "fuchsia",
           target_os = "linux",
           target_os = "macos",
           target_os = "netbsd",
index 52296042eb4a73b80b129fdda944822d3ee7e1cd..7bc8efd7e4a97ee2cdde8a50e7d015d6968f50da 100644 (file)
@@ -4,7 +4,7 @@ error[E0583]: file not found for module `baz`
 LL | pub mod baz;
    | ^^^^^^^^^^^^
    |
-   = help: to create the module `baz`, create file "$DIR/auxiliary/foo/bar/baz.rs"
+   = help: to create the module `baz`, create file "$DIR/auxiliary/foo/bar/baz.rs" or "$DIR/auxiliary/foo/bar/baz/mod.rs"
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/invalid/invalid-llvm-passes.rs b/src/test/ui/invalid/invalid-llvm-passes.rs
new file mode 100644 (file)
index 0000000..ca3c623
--- /dev/null
@@ -0,0 +1,4 @@
+// build-fail
+// compile-flags: -Cpasses=unknown-pass -Z new-llvm-pass-manager=yes
+
+fn main() {}
diff --git a/src/test/ui/invalid/invalid-llvm-passes.stderr b/src/test/ui/invalid/invalid-llvm-passes.stderr
new file mode 100644 (file)
index 0000000..ae1f85e
--- /dev/null
@@ -0,0 +1,4 @@
+error: failed to run LLVM passes: unknown pass name 'unknown-pass'
+
+error: aborting due to previous error
+
index 00805eb5dc90d041c1d5aae815b9034e03ee56cf..42a4d5b674b229c239149037f9398ce90f86565c 100644 (file)
@@ -1,5 +1,3 @@
-#![feature(const_fn)]
-
 const ARR_LEN: usize = Tt::const_val::<[i8; 123]>();
 //~^ ERROR type annotations needed
 
index 29d439b457ff64ecc2d3e5891911317c8da1c581..9de58d83c8b074340f6cdb2749baf202b7b94a66 100644 (file)
@@ -1,11 +1,11 @@
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/issue-54954.rs:7:5
+  --> $DIR/issue-54954.rs:5:5
    |
 LL |     const fn const_val<T: Sized>() -> usize {
    |     ^^^^^ functions in traits cannot be const
 
 error[E0283]: type annotations needed
-  --> $DIR/issue-54954.rs:3:24
+  --> $DIR/issue-54954.rs:1:24
    |
 LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>();
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
index 95ab86ebcb1f46f9ff66b3e17b06b76e4b6ff8bf..fbe68fb9379f866da8d45bd7438c31fd9c74c378 100644 (file)
@@ -1,6 +1,7 @@
 // run-pass
 // compile-flags: -Zlink-native-libraries=no -Cdefault-linker-libraries=yes
 // ignore-windows - this will probably only work on unixish systems
+// ignore-fuchsia - missing __libc_start_main for some reason (#84733)
 
 #[link(name = "some-random-non-existent-library", kind = "static")]
 extern "C" {}
diff --git a/src/test/ui/lifetimes/issue-84398.rs b/src/test/ui/lifetimes/issue-84398.rs
new file mode 100644 (file)
index 0000000..1912fa5
--- /dev/null
@@ -0,0 +1,20 @@
+// check-pass
+
+pub trait Deserialize<'de>: Sized {}
+pub trait DeserializeOwned: for<'de> Deserialize<'de> {}
+
+pub trait Extensible {
+    type Config;
+}
+
+// The `C` here generates a `C: Sized` candidate
+pub trait Installer<C> {
+    fn init<B: Extensible<Config = C>>(&mut self) -> ()
+    where
+        // This clause generates a `for<'de> C: Sized` candidate
+        B::Config: DeserializeOwned,
+    {
+    }
+}
+
+fn main() {}
index 01f1518c1c6ed9f044e11adcb08bcb70fed5e18e..37ddbe99a9f0310d7dd0f2ca03dca5516efc8ba2 100644 (file)
@@ -4,6 +4,7 @@
 // run-fail
 // revisions: foo bar
 // should-fail
+// needs-run-enabled
 //[foo] error-pattern:bar
 //[bar] error-pattern:foo
 
index 3b1992d90b7c5966ee8932701b534d615954b1a4..e04d59c58c22bfa86c4f021c66f6ef21d2a29c77 100644 (file)
@@ -1,5 +1,3 @@
-#![feature(const_fn)]
-
 trait Foo {
     fn f() -> u32;
     const fn g(); //~ ERROR cannot be declared const
index 450981a9183a220cd45744eed319c0643b9aacc5..7d1fbe45c5302ce815920f4b06566e218f1f65de 100644 (file)
@@ -1,11 +1,11 @@
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/const-fn-in-trait.rs:5:5
+  --> $DIR/const-fn-in-trait.rs:3:5
    |
 LL |     const fn g();
    |     ^^^^^ functions in traits cannot be const
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/const-fn-in-trait.rs:9:5
+  --> $DIR/const-fn-in-trait.rs:7:5
    |
 LL |     const fn f() -> u32 { 22 }
    |     ^^^^^ functions in traits cannot be const
index 91b3fe15c4be7a87a4b8525988378e29831eb974..31e4206a5463abeafc3656b63a5dc6cddbe959b8 100644 (file)
@@ -4,7 +4,7 @@ error[E0583]: file not found for module `missing`
 LL | mod missing;
    | ^^^^^^^^^^^^
    |
-   = help: to create the module `missing`, create file "$DIR/foo/missing.rs"
+   = help: to create the module `missing`, create file "$DIR/foo/missing.rs" or "$DIR/foo/missing/mod.rs"
 
 error: aborting due to previous error
 
index f519de46c767f9627419778a00845c35a4750ab1..9d252398b7a14bf0d492678bfa82da2f75ea9442 100644 (file)
@@ -4,7 +4,7 @@ error[E0583]: file not found for module `missing`
 LL |     mod missing;
    |     ^^^^^^^^^^^^
    |
-   = help: to create the module `missing`, create file "$DIR/foo_inline/inline/missing.rs"
+   = help: to create the module `missing`, create file "$DIR/foo_inline/inline/missing.rs" or "$DIR/foo_inline/inline/missing/mod.rs"
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/native-library-link-flags/empty-kind-1.rs b/src/test/ui/native-library-link-flags/empty-kind-1.rs
new file mode 100644 (file)
index 0000000..6f93d38
--- /dev/null
@@ -0,0 +1,6 @@
+// Unspecified kind should fail with an error
+
+// compile-flags: -l =mylib
+// error-pattern: unknown library kind ``, expected one of dylib, framework, or static
+
+fn main() {}
diff --git a/src/test/ui/native-library-link-flags/empty-kind-1.stderr b/src/test/ui/native-library-link-flags/empty-kind-1.stderr
new file mode 100644 (file)
index 0000000..2a4a82d
--- /dev/null
@@ -0,0 +1,2 @@
+error: unknown library kind ``, expected one of dylib, framework, or static
+
diff --git a/src/test/ui/native-library-link-flags/empty-kind-2.rs b/src/test/ui/native-library-link-flags/empty-kind-2.rs
new file mode 100644 (file)
index 0000000..c0c3557
--- /dev/null
@@ -0,0 +1,6 @@
+// Unspecified kind should fail with an error
+
+// compile-flags: -l :+bundle=mylib
+// error-pattern: unknown library kind ``, expected one of dylib, framework, or static
+
+fn main() {}
diff --git a/src/test/ui/native-library-link-flags/empty-kind-2.stderr b/src/test/ui/native-library-link-flags/empty-kind-2.stderr
new file mode 100644 (file)
index 0000000..2a4a82d
--- /dev/null
@@ -0,0 +1,2 @@
+error: unknown library kind ``, expected one of dylib, framework, or static
+
index c80a90b3eaaac5d44cae0b509048f6bd6b56d12f..77390aae2d688a0d87630bde952525821f62ad95 100644 (file)
@@ -36,6 +36,8 @@ macro_rules! a {
     panic!(a!()); //~ WARN panic message is not a string literal
 
     panic!(format!("{}", 1)); //~ WARN panic message is not a string literal
+    assert!(false, format!("{}", 1)); //~ WARN panic message is not a string literal
+    debug_assert!(false, format!("{}", 1)); //~ WARN panic message is not a string literal
 
     panic![123]; //~ WARN panic message is not a string literal
     panic!{123}; //~ WARN panic message is not a string literal
index 7a333b3e76abe8d7d2c6ed27354117729c665a5b..3278eb5f0238e41db4b3b71a8731755c9b7dddaf 100644 (file)
@@ -213,7 +213,33 @@ LL |     panic!("{}", 1);
    |           --     --
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:40:12
+  --> $DIR/non-fmt-panic.rs:39:20
+   |
+LL |     assert!(false, format!("{}", 1));
+   |                    ^^^^^^^^^^^^^^^^
+   |
+   = note: this is no longer accepted in Rust 2021
+   = note: the assert!() macro supports formatting, so there's no need for the format!() macro here
+help: remove the `format!(..)` macro call
+   |
+LL |     assert!(false, "{}", 1);
+   |                   --     --
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:40:26
+   |
+LL |     debug_assert!(false, format!("{}", 1));
+   |                          ^^^^^^^^^^^^^^^^
+   |
+   = note: this is no longer accepted in Rust 2021
+   = note: the debug_assert!() macro supports formatting, so there's no need for the format!() macro here
+help: remove the `format!(..)` macro call
+   |
+LL |     debug_assert!(false, "{}", 1);
+   |                         --     --
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:42:12
    |
 LL |     panic![123];
    |            ^^^
@@ -229,7 +255,7 @@ LL |     std::panic::panic_any(123);
    |     ^^^^^^^^^^^^^^^^^^^^^^   ^
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:41:12
+  --> $DIR/non-fmt-panic.rs:43:12
    |
 LL |     panic!{123};
    |            ^^^
@@ -244,5 +270,5 @@ help: or use std::panic::panic_any instead
 LL |     std::panic::panic_any(123);
    |     ^^^^^^^^^^^^^^^^^^^^^^   ^
 
-warning: 18 warnings emitted
+warning: 20 warnings emitted
 
index 3fa3822831b5275b54d7e43df427a68f04a2d825..df31e614cf809be8d815908cd1393a562712479d 100644 (file)
@@ -1,6 +1,7 @@
 // aux-build:weak-lang-items.rs
 // error-pattern: `#[panic_handler]` function required, but not found
 // error-pattern: language item required, but not found: `eh_personality`
+// needs-unwind since it affects the error output
 // ignore-emscripten compiled with panic=abort, personality not required
 
 #![no_std]
index 68e3e21df3e0841079bbece611ebcef22e160e6e..1f14b20e4514dea794b1187f1a3b6243d5f50983 100644 (file)
@@ -1,5 +1,5 @@
 error[E0259]: the name `core` is defined multiple times
-  --> $DIR/weak-lang-item.rs:8:1
+  --> $DIR/weak-lang-item.rs:9:1
    |
 LL | extern crate core;
    | ^^^^^^^^^^^^^^^^^^ `core` reimported here
index f8368ff69008f02f5ea144e127d2e716db4d1843..58a90a592c4c0f0b62ab32b8883a267018f31a8d 100644 (file)
@@ -1,5 +1,6 @@
 // build-fail
 // compile-flags:-C panic=abort -C prefer-dynamic
+// needs-unwind
 // ignore-musl - no dylibs here
 // ignore-emscripten
 // ignore-sgx no dynamic lib support
index 6f39b76526b64aae9a63dbcd99e3468b2dee7394..24048ebe008fa4432528efc9739c9237b87e5b95 100644 (file)
@@ -2,6 +2,7 @@
 #![allow(unused_variables)]
 
 // compile-flags:-C lto -C panic=unwind
+// needs-unwind
 // no-prefer-dynamic
 // ignore-emscripten no processes
 // ignore-sgx no processes
index 1848c986e3615e13b391b5ad89f2e284e3d3979d..622535a75aff6e5eb25b766a64bac7b6a0dd11d3 100644 (file)
@@ -1,4 +1,5 @@
 // build-fail
+// needs-unwind
 // aux-build:panic-runtime-unwind.rs
 // aux-build:panic-runtime-abort.rs
 // aux-build:wants-panic-runtime-unwind.rs
index 894a5eb38b8e73adb20fe3a68ddda802e8b7e002..c48caaf07907742018ffb362e32f5a2093764405 100644 (file)
@@ -1,4 +1,5 @@
 // build-fail
+// needs-unwind
 // error-pattern:is incompatible with this crate's strategy of `unwind`
 // aux-build:panic-runtime-abort.rs
 // aux-build:panic-runtime-lang-items.rs
index 5955075bae581de1a2e340e2469bc53d46e3bb3b..7a2e48e2f10a960b6f0d56e6af99f19cbb7ef06c 100644 (file)
@@ -1,4 +1,5 @@
 // build-fail
+// needs-unwind
 // error-pattern:is incompatible with this crate's strategy of `unwind`
 // aux-build:panic-runtime-abort.rs
 // aux-build:wants-panic-runtime-abort.rs
index 0bbaeec0c7fec54c8f1d358f53ee7501bc846585..91a63bafd99a9688d6847e380c863a8ee8768dca 100644 (file)
@@ -3,7 +3,6 @@
 // edition:2018
 
 #![feature(const_extern_fn)]
-#![feature(const_fn)]
 
 fn main() {
     async fn ff1() {} // OK.
index 2e513415e6c64fdbcf862e3fe1a333ba2781ccf2..b3f60b13b0f74a640869a8bb5dd1a8e9c0ec07ea 100644 (file)
@@ -1,5 +1,5 @@
 error: functions cannot be both `const` and `async`
-  --> $DIR/fn-header-semantic-fail.rs:13:5
+  --> $DIR/fn-header-semantic-fail.rs:12:5
    |
 LL |     const async unsafe extern "C" fn ff5() {} // OK.
    |     ^^^^^-^^^^^------------------------------
@@ -8,7 +8,7 @@ LL |     const async unsafe extern "C" fn ff5() {} // OK.
    |     `const` because of this
 
 error[E0706]: functions in traits cannot be declared `async`
-  --> $DIR/fn-header-semantic-fail.rs:17:9
+  --> $DIR/fn-header-semantic-fail.rs:16:9
    |
 LL |         async fn ft1();
    |         -----^^^^^^^^^^
@@ -19,19 +19,19 @@ LL |         async fn ft1();
    = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/fn-header-semantic-fail.rs:19:9
+  --> $DIR/fn-header-semantic-fail.rs:18:9
    |
 LL |         const fn ft3();
    |         ^^^^^ functions in traits cannot be const
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/fn-header-semantic-fail.rs:21:9
+  --> $DIR/fn-header-semantic-fail.rs:20:9
    |
 LL |         const async unsafe extern "C" fn ft5();
    |         ^^^^^ functions in traits cannot be const
 
 error[E0706]: functions in traits cannot be declared `async`
-  --> $DIR/fn-header-semantic-fail.rs:21:9
+  --> $DIR/fn-header-semantic-fail.rs:20:9
    |
 LL |         const async unsafe extern "C" fn ft5();
    |         ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -42,7 +42,7 @@ LL |         const async unsafe extern "C" fn ft5();
    = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
 
 error: functions cannot be both `const` and `async`
-  --> $DIR/fn-header-semantic-fail.rs:21:9
+  --> $DIR/fn-header-semantic-fail.rs:20:9
    |
 LL |         const async unsafe extern "C" fn ft5();
    |         ^^^^^-^^^^^----------------------------
@@ -51,7 +51,7 @@ LL |         const async unsafe extern "C" fn ft5();
    |         `const` because of this
 
 error[E0706]: functions in traits cannot be declared `async`
-  --> $DIR/fn-header-semantic-fail.rs:29:9
+  --> $DIR/fn-header-semantic-fail.rs:28:9
    |
 LL |         async fn ft1() {}
    |         -----^^^^^^^^^^^^
@@ -62,19 +62,19 @@ LL |         async fn ft1() {}
    = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/fn-header-semantic-fail.rs:32:9
+  --> $DIR/fn-header-semantic-fail.rs:31:9
    |
 LL |         const fn ft3() {}
    |         ^^^^^ functions in traits cannot be const
 
 error[E0379]: functions in traits cannot be declared const
-  --> $DIR/fn-header-semantic-fail.rs:34:9
+  --> $DIR/fn-header-semantic-fail.rs:33:9
    |
 LL |         const async unsafe extern "C" fn ft5() {}
    |         ^^^^^ functions in traits cannot be const
 
 error[E0706]: functions in traits cannot be declared `async`
-  --> $DIR/fn-header-semantic-fail.rs:34:9
+  --> $DIR/fn-header-semantic-fail.rs:33:9
    |
 LL |         const async unsafe extern "C" fn ft5() {}
    |         ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -85,7 +85,7 @@ LL |         const async unsafe extern "C" fn ft5() {}
    = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
 
 error: functions cannot be both `const` and `async`
-  --> $DIR/fn-header-semantic-fail.rs:34:9
+  --> $DIR/fn-header-semantic-fail.rs:33:9
    |
 LL |         const async unsafe extern "C" fn ft5() {}
    |         ^^^^^-^^^^^------------------------------
@@ -94,7 +94,7 @@ LL |         const async unsafe extern "C" fn ft5() {}
    |         `const` because of this
 
 error: functions cannot be both `const` and `async`
-  --> $DIR/fn-header-semantic-fail.rs:46:9
+  --> $DIR/fn-header-semantic-fail.rs:45:9
    |
 LL |         const async unsafe extern "C" fn fi5() {}
    |         ^^^^^-^^^^^------------------------------
@@ -103,7 +103,7 @@ LL |         const async unsafe extern "C" fn fi5() {}
    |         `const` because of this
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:51:18
+  --> $DIR/fn-header-semantic-fail.rs:50:18
    |
 LL |     extern "C" {
    |     ---------- in this `extern` block
@@ -116,7 +116,7 @@ LL |         fn fe1();
    |         ^^
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:52:19
+  --> $DIR/fn-header-semantic-fail.rs:51:19
    |
 LL |     extern "C" {
    |     ---------- in this `extern` block
@@ -130,7 +130,7 @@ LL |         fn fe2();
    |         ^^
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:53:18
+  --> $DIR/fn-header-semantic-fail.rs:52:18
    |
 LL |     extern "C" {
    |     ---------- in this `extern` block
@@ -144,7 +144,7 @@ LL |         fn fe3();
    |         ^^
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:54:23
+  --> $DIR/fn-header-semantic-fail.rs:53:23
    |
 LL |     extern "C" {
    |     ---------- in this `extern` block
@@ -158,7 +158,7 @@ LL |         fn fe4();
    |         ^^
 
 error: functions in `extern` blocks cannot have qualifiers
-  --> $DIR/fn-header-semantic-fail.rs:55:42
+  --> $DIR/fn-header-semantic-fail.rs:54:42
    |
 LL |     extern "C" {
    |     ---------- in this `extern` block
@@ -172,7 +172,7 @@ LL |         fn fe5();
    |         ^^
 
 error: functions cannot be both `const` and `async`
-  --> $DIR/fn-header-semantic-fail.rs:55:9
+  --> $DIR/fn-header-semantic-fail.rs:54:9
    |
 LL |         const async unsafe extern "C" fn fe5();
    |         ^^^^^-^^^^^----------------------------
@@ -181,7 +181,7 @@ LL |         const async unsafe extern "C" fn fe5();
    |         `const` because of this
 
 error[E0053]: method `ft1` has an incompatible type for trait
-  --> $DIR/fn-header-semantic-fail.rs:29:24
+  --> $DIR/fn-header-semantic-fail.rs:28:24
    |
 LL |         async fn ft1();
    |                       - type in trait
@@ -197,7 +197,7 @@ LL |         async fn ft1() {}
               found fn pointer `fn() -> impl Future`
 
 error[E0053]: method `ft5` has an incompatible type for trait
-  --> $DIR/fn-header-semantic-fail.rs:34:48
+  --> $DIR/fn-header-semantic-fail.rs:33:48
    |
 LL |         const async unsafe extern "C" fn ft5();
    |                                               - type in trait
index 4e08125625f0a2f15ecd8e48d1e57deeec2f1925..62456d518804feb0646358d5412a88b90d79f970 100644 (file)
@@ -4,7 +4,7 @@ error[E0583]: file not found for module `not_a_real_file`
 LL | mod not_a_real_file;
    | ^^^^^^^^^^^^^^^^^^^^
    |
-   = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs"
+   = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" or "$DIR/not_a_real_file/mod.rs"
 
 error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux`
   --> $DIR/mod_file_not_exist.rs:7:16
index 73cdf098b00c96adf29a6c03321e4c3b5c2fd480..d5143dbe982ae28703b94d9f45a9e72166e7df79 100644 (file)
@@ -4,7 +4,7 @@ error[E0583]: file not found for module `not_a_real_file`
 LL | mod not_a_real_file;
    | ^^^^^^^^^^^^^^^^^^^^
    |
-   = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs"
+   = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" or "$DIR/not_a_real_file/mod.rs"
 
 error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux`
   --> $DIR/mod_file_not_exist_windows.rs:7:16
diff --git a/src/test/ui/parser/trait-object-delimiters.rs b/src/test/ui/parser/trait-object-delimiters.rs
new file mode 100644 (file)
index 0000000..650ab57
--- /dev/null
@@ -0,0 +1,17 @@
+// edition:2018
+
+fn foo1(_: &dyn Drop + AsRef<str>) {} //~ ERROR ambiguous `+` in a type
+//~^ ERROR only auto traits can be used as additional traits in a trait object
+
+fn foo2(_: &dyn (Drop + AsRef<str>)) {} //~ ERROR incorrect braces around trait bounds
+
+fn foo3(_: &dyn {Drop + AsRef<str>}) {} //~ ERROR expected parameter name, found `{`
+//~^ ERROR expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{`
+//~| ERROR at least one trait is required for an object type
+
+fn foo4(_: &dyn <Drop + AsRef<str>>) {} //~ ERROR expected identifier, found `<`
+
+fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {} //~ ERROR invalid `dyn` keyword
+//~^ ERROR only auto traits can be used as additional traits in a trait object
+
+fn main() {}
diff --git a/src/test/ui/parser/trait-object-delimiters.stderr b/src/test/ui/parser/trait-object-delimiters.stderr
new file mode 100644 (file)
index 0000000..18b1b24
--- /dev/null
@@ -0,0 +1,77 @@
+error: ambiguous `+` in a type
+  --> $DIR/trait-object-delimiters.rs:3:13
+   |
+LL | fn foo1(_: &dyn Drop + AsRef<str>) {}
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(dyn Drop + AsRef<str>)`
+
+error: incorrect braces around trait bounds
+  --> $DIR/trait-object-delimiters.rs:6:17
+   |
+LL | fn foo2(_: &dyn (Drop + AsRef<str>)) {}
+   |                 ^                 ^
+   |
+help: remove the parentheses
+   |
+LL | fn foo2(_: &dyn Drop + AsRef<str>) {}
+   |                --               --
+
+error: expected parameter name, found `{`
+  --> $DIR/trait-object-delimiters.rs:8:17
+   |
+LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
+   |                 ^ expected parameter name
+
+error: expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{`
+  --> $DIR/trait-object-delimiters.rs:8:17
+   |
+LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
+   |                -^ expected one of 8 possible tokens
+   |                |
+   |                help: missing `,`
+
+error: expected identifier, found `<`
+  --> $DIR/trait-object-delimiters.rs:12:17
+   |
+LL | fn foo4(_: &dyn <Drop + AsRef<str>>) {}
+   |                 ^ expected identifier
+
+error: invalid `dyn` keyword
+  --> $DIR/trait-object-delimiters.rs:14:25
+   |
+LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
+   |                         ^^^ help: remove this keyword
+   |
+   = help: `dyn` is only needed at the start of a trait `+`-separated list
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/trait-object-delimiters.rs:3:24
+   |
+LL | fn foo1(_: &dyn Drop + AsRef<str>) {}
+   |                 ----   ^^^^^^^^^^ 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: Drop + AsRef<str> {}`
+   = 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[E0224]: at least one trait is required for an object type
+  --> $DIR/trait-object-delimiters.rs:8:13
+   |
+LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
+   |             ^^^
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/trait-object-delimiters.rs:14:29
+   |
+LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
+   |                  ----       ^^^^^^^^^^ 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: Drop + AsRef<str> {}`
+   = 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: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0224, E0225.
+For more information about an error, try `rustc --explain E0224`.
index 259b2c1d61e08f865dc856682daf10e0d9f36330..dac6e7a3550567f89fcf5241381d2519ca84448d 100644 (file)
@@ -4,7 +4,7 @@ error[E0583]: file not found for module `n`
 LL | unsafe mod n;
    | ^^^^^^^^^^^^^
    |
-   = help: to create the module `n`, create file "$DIR/n.rs"
+   = help: to create the module `n`, create file "$DIR/n.rs" or "$DIR/n/mod.rs"
 
 error: module cannot be declared unsafe
   --> $DIR/unsafe-mod.rs:1:1
index 05240908917bc0f37e55ead53e56ab9a1dd67f15..a3bed707eccda69c6ea5c9614ba97787853a5819 100644 (file)
@@ -6,7 +6,7 @@
 // run-pass
 // compile-flags: -Z unleash-the-miri-inside-of-you
 
-#![feature(core_intrinsics, const_caller_location, const_fn)]
+#![feature(core_intrinsics, const_caller_location)]
 
 type L = &'static std::panic::Location<'static>;
 
index 89b0b69f38dc09cd5e7d0e81929908c67a9362c8..6e15cf3fe8ad375922d9468447b0164ff6de2060 100644 (file)
@@ -2,7 +2,7 @@
 // revisions: default mir-opt
 //[mir-opt] compile-flags: -Zmir-opt-level=4
 
-#![feature(const_caller_location, const_fn)]
+#![feature(const_caller_location)]
 
 use std::panic::Location;
 
index e857a1e60e5fefb4be8b315bbaf99f982521764f..dd0dac95e36463f412007a7e1a0fb170137b2882 100644 (file)
@@ -4,7 +4,7 @@ error[E0583]: file not found for module `řųśť`
 LL | mod řųśť;
    | ^^^^^^^^^
    |
-   = help: to create the module `řųśť`, create file "$DIR/řųśť.rs"
+   = help: to create the module `řųśť`, create file "$DIR/řųśť.rs" or "$DIR/řųśť/mod.rs"
 
 error[E0754]: trying to load file for module `řųśť` with non-ascii identifier name
   --> $DIR/mod_file_nonascii_forbidden.rs:1:5
index 5feeca6f530e8e5bcf9a6b512ea82213963f97d5..06457bc5ca65a1465da34ca75195ab43752445cf 100644 (file)
@@ -1,4 +1,4 @@
-#![feature(rustc_attrs, const_fn)]
+#![feature(rustc_attrs)]
 
 #[rustc_args_required_const(0)]
 fn foo(_a: i32) {
index 9f8ecc262812624d7e593f9b564db7e9e03ad1fa..d7cb66d9c84ec72657e53a69a519c5fb9f9c6f20 100644 (file)
@@ -1,6 +1,6 @@
 // Various checks that stability attributes are used correctly, per RFC 507
 
-#![feature(const_fn, staged_api)]
+#![feature(staged_api)]
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
index 0de28d5469b389b2d9830dbb4573a6a9a31e5dd0..e7ea8663d5af84cefa08ff85ab170f6cc144c464 100644 (file)
@@ -1,5 +1,3 @@
-#![feature(const_fn)]
-
 struct WithDtor;
 
 impl Drop for WithDtor {
index ed81734f6ebd7cd981f0d8d968f47044ed3ccab7..ac32f217fd76904facc0c879a6e08aae9e01127d 100644 (file)
@@ -1,5 +1,5 @@
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:9:60
+  --> $DIR/static-drop-scope.rs:7:60
    |
 LL | static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor);
    |                                                            ^^^^^^^^- value is dropped here
@@ -7,7 +7,7 @@ LL | static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor);
    |                                                            statics cannot evaluate destructors
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/static-drop-scope.rs:9:60
+  --> $DIR/static-drop-scope.rs:7:60
    |
 LL | static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor);
    |                                                      ------^^^^^^^^-
@@ -17,7 +17,7 @@ LL | static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor);
    |                                                      using this value as a static requires that borrow lasts for `'static`
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:13:59
+  --> $DIR/static-drop-scope.rs:11:59
    |
 LL | const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor);
    |                                                           ^^^^^^^^- value is dropped here
@@ -25,7 +25,7 @@ LL | const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor);
    |                                                           constants cannot evaluate destructors
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/static-drop-scope.rs:13:59
+  --> $DIR/static-drop-scope.rs:11:59
    |
 LL | const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor);
    |                                                     ------^^^^^^^^-
@@ -35,7 +35,7 @@ LL | const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor);
    |                                                     using this value as a constant requires that borrow lasts for `'static`
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:17:28
+  --> $DIR/static-drop-scope.rs:15:28
    |
 LL | static EARLY_DROP_S: i32 = (WithDtor, 0).1;
    |                            ^^^^^^^^^^^^^ - value is dropped here
@@ -43,7 +43,7 @@ LL | static EARLY_DROP_S: i32 = (WithDtor, 0).1;
    |                            statics cannot evaluate destructors
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:20:27
+  --> $DIR/static-drop-scope.rs:18:27
    |
 LL | const EARLY_DROP_C: i32 = (WithDtor, 0).1;
    |                           ^^^^^^^^^^^^^ - value is dropped here
@@ -51,7 +51,7 @@ LL | const EARLY_DROP_C: i32 = (WithDtor, 0).1;
    |                           constants cannot evaluate destructors
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:23:24
+  --> $DIR/static-drop-scope.rs:21:24
    |
 LL | const fn const_drop<T>(_: T) {}
    |                        ^      - value is dropped here
@@ -59,7 +59,7 @@ LL | const fn const_drop<T>(_: T) {}
    |                        constant functions cannot evaluate destructors
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:27:5
+  --> $DIR/static-drop-scope.rs:25:5
    |
 LL |     (x, ()).1
    |     ^^^^^^^ constant functions cannot evaluate destructors
@@ -68,7 +68,7 @@ LL | }
    | - value is dropped here
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:31:34
+  --> $DIR/static-drop-scope.rs:29:34
    |
 LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1;
    |                                  ^^^^^^^^^^^^^^^^^^^ - value is dropped here
@@ -76,7 +76,7 @@ LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1;
    |                                  constants cannot evaluate destructors
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/static-drop-scope.rs:36:43
+  --> $DIR/static-drop-scope.rs:34:43
    |
 LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1;
    |                                           ^^^^^^^^^^^ - value is dropped here
index 69544b1c0603633c3a9b96b5085a7658c4d240ec..cc6412e271a132825e6498267211a4e0235d1468 100644 (file)
@@ -35,6 +35,7 @@ struct Outer {
           target_os = "dragonfly",
           target_os = "emscripten",
           target_os = "freebsd",
+          target_os = "fuchsia",
           target_os = "linux",
           target_os = "macos",
           target_os = "netbsd",
diff --git a/src/test/ui/structs-enums/struct-rec/issue-74224.rs b/src/test/ui/structs-enums/struct-rec/issue-74224.rs
new file mode 100644 (file)
index 0000000..f3b72c5
--- /dev/null
@@ -0,0 +1,11 @@
+struct A<T> {
+//~^ ERROR recursive type `A` has infinite size
+    x: T,
+    y: A<A<T>>,
+}
+
+struct B {
+    z: A<usize>
+}
+
+fn main() {}
diff --git a/src/test/ui/structs-enums/struct-rec/issue-74224.stderr b/src/test/ui/structs-enums/struct-rec/issue-74224.stderr
new file mode 100644 (file)
index 0000000..d61ab19
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0072]: recursive type `A` has infinite size
+  --> $DIR/issue-74224.rs:1:1
+   |
+LL | struct A<T> {
+   | ^^^^^^^^^^^ recursive type has infinite size
+...
+LL |     y: A<A<T>>,
+   |        ------- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `A` representable
+   |
+LL |     y: Box<A<A<T>>>,
+   |        ^^^^       ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/structs-enums/struct-rec/issue-84611.rs b/src/test/ui/structs-enums/struct-rec/issue-84611.rs
new file mode 100644 (file)
index 0000000..4c356af
--- /dev/null
@@ -0,0 +1,11 @@
+struct Foo<T> {
+//~^ ERROR recursive type `Foo` has infinite size
+    x: Foo<[T; 1]>,
+    y: T,
+}
+
+struct Bar {
+    x: Foo<Bar>,
+}
+
+fn main() {}
diff --git a/src/test/ui/structs-enums/struct-rec/issue-84611.stderr b/src/test/ui/structs-enums/struct-rec/issue-84611.stderr
new file mode 100644 (file)
index 0000000..0a898e5
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0072]: recursive type `Foo` has infinite size
+  --> $DIR/issue-84611.rs:1:1
+   |
+LL | struct Foo<T> {
+   | ^^^^^^^^^^^^^ recursive type has infinite size
+LL |
+LL |     x: Foo<[T; 1]>,
+   |        ----------- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable
+   |
+LL |     x: Box<Foo<[T; 1]>>,
+   |        ^^^^           ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.rs b/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.rs
new file mode 100644 (file)
index 0000000..cca97f4
--- /dev/null
@@ -0,0 +1,23 @@
+struct A<T> {
+//~^ ERROR recursive type `A` has infinite size
+    x: T,
+    y: B<T>,
+}
+
+struct B<T> {
+//~^ ERROR recursive type `B` has infinite size
+    z: A<T>
+}
+
+struct C<T> {
+//~^ ERROR recursive type `C` has infinite size
+    x: T,
+    y: Option<Option<D<T>>>,
+}
+
+struct D<T> {
+//~^ ERROR recursive type `D` has infinite size
+    z: Option<Option<C<T>>>,
+}
+
+fn main() {}
diff --git a/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.stderr b/src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.stderr
new file mode 100644 (file)
index 0000000..efc4ba9
--- /dev/null
@@ -0,0 +1,59 @@
+error[E0072]: recursive type `A` has infinite size
+  --> $DIR/mutual-struct-recursion.rs:1:1
+   |
+LL | struct A<T> {
+   | ^^^^^^^^^^^ recursive type has infinite size
+...
+LL |     y: B<T>,
+   |        ---- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `A` representable
+   |
+LL |     y: Box<B<T>>,
+   |        ^^^^    ^
+
+error[E0072]: recursive type `B` has infinite size
+  --> $DIR/mutual-struct-recursion.rs:7:1
+   |
+LL | struct B<T> {
+   | ^^^^^^^^^^^ recursive type has infinite size
+LL |
+LL |     z: A<T>
+   |        ---- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `B` representable
+   |
+LL |     z: Box<A<T>>
+   |        ^^^^    ^
+
+error[E0072]: recursive type `C` has infinite size
+  --> $DIR/mutual-struct-recursion.rs:12:1
+   |
+LL | struct C<T> {
+   | ^^^^^^^^^^^ recursive type has infinite size
+...
+LL |     y: Option<Option<D<T>>>,
+   |        -------------------- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `C` representable
+   |
+LL |     y: Box<Option<Option<D<T>>>>,
+   |        ^^^^                    ^
+
+error[E0072]: recursive type `D` has infinite size
+  --> $DIR/mutual-struct-recursion.rs:18:1
+   |
+LL | struct D<T> {
+   | ^^^^^^^^^^^ recursive type has infinite size
+LL |
+LL |     z: Option<Option<C<T>>>,
+   |        -------------------- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `D` representable
+   |
+LL |     z: Box<Option<Option<C<T>>>>,
+   |        ^^^^                    ^
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0072`.
diff --git a/src/test/ui/structs/struct-fn-in-definition.rs b/src/test/ui/structs/struct-fn-in-definition.rs
new file mode 100644 (file)
index 0000000..5ae1b72
--- /dev/null
@@ -0,0 +1,33 @@
+// It might be intuitive for a user coming from languages like Java
+// to declare a method directly in a struct's definition. Make sure
+// rustc can give a helpful suggestion.
+// Suggested in issue #76421
+
+struct S {
+    field: usize,
+
+    fn foo() {}
+    //~^ ERROR functions are not allowed in struct definitions
+    //~| HELP unlike in C++, Java, and C#, functions are declared in `impl` blocks
+    //~| HELP see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+}
+
+union U {
+    variant: usize,
+
+    fn foo() {}
+    //~^ ERROR functions are not allowed in union definitions
+    //~| HELP unlike in C++, Java, and C#, functions are declared in `impl` blocks
+    //~| HELP see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+}
+
+enum E {
+    Variant,
+
+    fn foo() {}
+    //~^ ERROR functions are not allowed in enum definitions
+    //~| HELP unlike in C++, Java, and C#, functions are declared in `impl` blocks
+    //~| HELP see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+}
+
+fn main() {}
diff --git a/src/test/ui/structs/struct-fn-in-definition.stderr b/src/test/ui/structs/struct-fn-in-definition.stderr
new file mode 100644 (file)
index 0000000..1d7cd52
--- /dev/null
@@ -0,0 +1,29 @@
+error: functions are not allowed in struct definitions
+  --> $DIR/struct-fn-in-definition.rs:9:5
+   |
+LL |     fn foo() {}
+   |     ^^^^^^^^^^^
+   |
+   = help: unlike in C++, Java, and C#, functions are declared in `impl` blocks
+   = help: see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+
+error: functions are not allowed in union definitions
+  --> $DIR/struct-fn-in-definition.rs:18:5
+   |
+LL |     fn foo() {}
+   |     ^^^^^^^^^^^
+   |
+   = help: unlike in C++, Java, and C#, functions are declared in `impl` blocks
+   = help: see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+
+error: functions are not allowed in enum definitions
+  --> $DIR/struct-fn-in-definition.rs:27:5
+   |
+LL |     fn foo() {}
+   |     ^^^^^^^^^^^
+   |
+   = help: unlike in C++, Java, and C#, functions are declared in `impl` blocks
+   = help: see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/suggestions/issue-68049-1.rs b/src/test/ui/suggestions/issue-68049-1.rs
new file mode 100644 (file)
index 0000000..0acb7b1
--- /dev/null
@@ -0,0 +1,16 @@
+use std::alloc::{GlobalAlloc, Layout};
+
+struct Test(u32);
+
+unsafe impl GlobalAlloc for Test {
+    unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
+        self.0 += 1; //~ ERROR cannot assign
+        0 as *mut u8
+    }
+
+    unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
+        unimplemented!();
+    }
+}
+
+fn main() { }
diff --git a/src/test/ui/suggestions/issue-68049-1.stderr b/src/test/ui/suggestions/issue-68049-1.stderr
new file mode 100644 (file)
index 0000000..32367d2
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0594]: cannot assign to `self.0` which is behind a `&` reference
+  --> $DIR/issue-68049-1.rs:7:9
+   |
+LL |         self.0 += 1;
+   |         ^^^^^^^^^^^ `self` 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`.
diff --git a/src/test/ui/suggestions/issue-68049-2.rs b/src/test/ui/suggestions/issue-68049-2.rs
new file mode 100644 (file)
index 0000000..1c3430c
--- /dev/null
@@ -0,0 +1,21 @@
+trait Hello {
+  fn example(&self, input: &i32); // should suggest here
+}
+
+struct Test1(i32);
+
+impl Hello for Test1 {
+  fn example(&self, input: &i32) { // should not suggest here
+      *input = self.0; //~ ERROR cannot assign
+  }
+}
+
+struct Test2(i32);
+
+impl Hello for Test2 {
+  fn example(&self, input: &i32) { // should not suggest here
+    self.0 += *input; //~ ERROR cannot assign
+  }
+}
+
+fn main() { }
diff --git a/src/test/ui/suggestions/issue-68049-2.stderr b/src/test/ui/suggestions/issue-68049-2.stderr
new file mode 100644 (file)
index 0000000..f10a83c
--- /dev/null
@@ -0,0 +1,21 @@
+error[E0594]: cannot assign to `*input` which is behind a `&` reference
+  --> $DIR/issue-68049-2.rs:9:7
+   |
+LL |   fn example(&self, input: &i32); // should suggest here
+   |                            ---- help: consider changing that to be a mutable reference: `&mut i32`
+...
+LL |       *input = self.0;
+   |       ^^^^^^^^^^^^^^^ `input` is a `&` reference, so the data it refers to cannot be written
+
+error[E0594]: cannot assign to `self.0` which is behind a `&` reference
+  --> $DIR/issue-68049-2.rs:17:5
+   |
+LL |   fn example(&self, input: &i32); // should suggest here
+   |              ----- help: consider changing that to be a mutable reference: `&mut self`
+...
+LL |     self.0 += *input;
+   |     ^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/suggestions/issue-84592.rs b/src/test/ui/suggestions/issue-84592.rs
new file mode 100644 (file)
index 0000000..aa246aa
--- /dev/null
@@ -0,0 +1,17 @@
+/* Checks whether issue #84592 has been resolved. The issue was
+ * that in this example, there are two expected/missing lifetime
+ * parameters with *different spans*, leading to incorrect
+ * suggestions from rustc.
+ */
+
+struct TwoLifetimes<'x, 'y> {
+    x: &'x (),
+    y: &'y (),
+}
+
+fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
+//~^ ERROR missing lifetime specifiers [E0106]
+    TwoLifetimes { x: &(), y: &() }
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/issue-84592.stderr b/src/test/ui/suggestions/issue-84592.stderr
new file mode 100644 (file)
index 0000000..02f9241
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0106]: missing lifetime specifiers
+  --> $DIR/issue-84592.rs:12:57
+   |
+LL | fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
+   |                            ---     ---                  ^^  ^^ expected named lifetime parameter
+   |                                                         |
+   |                                                         expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
+help: consider introducing a named lifetime parameter
+   |
+LL | fn two_lifetimes_needed<'a>(a: &'a (), b: &'a ()) -> TwoLifetimes<'a, 'a> {
+   |                        ^^^^    ^^^^^^     ^^^^^^                  ^^  ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0106`.
index 2cb63500e48b919e571026ce0a0c865a307da0cf..a7a44b511db8de6b3b12dc917bf902a4f564caa8 100644 (file)
@@ -44,6 +44,10 @@ note: these named lifetimes are available to use
    |
 LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X);
    |          ^^              ^^
+help: consider using one of the available lifetimes here
+   |
+LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &'lifetime X);
+   |                                        ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/missing-lt-for-hrtb.rs:5:41
diff --git a/src/test/ui/suggestions/return-elided-lifetime.rs b/src/test/ui/suggestions/return-elided-lifetime.rs
new file mode 100644 (file)
index 0000000..ca336bb
--- /dev/null
@@ -0,0 +1,37 @@
+/* Checks all four scenarios possible in report_elision_failure() of
+ * rustc_resolve::late::lifetimes::LifetimeContext related to returning
+ * borrowed values, in various configurations.
+ */
+
+fn f1() -> &i32 { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+fn f1_() -> (&i32, &i32) { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+//~^^ ERROR missing lifetime specifier [E0106]
+
+fn f2(a: i32, b: i32) -> &i32 { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+//~^^ ERROR missing lifetime specifier [E0106]
+
+struct S<'a, 'b> { a: &'a i32, b: &'b i32 }
+fn f3(s: &S) -> &i32 { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+//~^^ ERROR missing lifetime specifier [E0106]
+
+fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+//~^^ ERROR missing lifetime specifier [E0106]
+
+fn f5<'a>(a: &'a i32, b: &i32) -> &i32 { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
+//~^ ERROR missing lifetime specifier [E0106]
+//~^^ ERROR missing lifetime specifier [E0106]
+
+fn main() {}
diff --git a/src/test/ui/suggestions/return-elided-lifetime.stderr b/src/test/ui/suggestions/return-elided-lifetime.stderr
new file mode 100644 (file)
index 0000000..888cd5e
--- /dev/null
@@ -0,0 +1,198 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:6:12
+   |
+LL | fn f1() -> &i32 { loop {} }
+   |            ^ 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 f1() -> &'static i32 { loop {} }
+   |            ^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:8:14
+   |
+LL | fn f1_() -> (&i32, &i32) { loop {} }
+   |              ^ 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 f1_() -> (&'static i32, &i32) { loop {} }
+   |              ^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:8:20
+   |
+LL | fn f1_() -> (&i32, &i32) { loop {} }
+   |                    ^ 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 f1_() -> (&i32, &'static i32) { loop {} }
+   |                    ^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:12:26
+   |
+LL | fn f2(a: i32, b: i32) -> &i32 { loop {} }
+   |                          ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+help: consider using the `'static` lifetime
+   |
+LL | fn f2(a: i32, b: i32) -> &'static i32 { loop {} }
+   |                          ^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:14:28
+   |
+LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
+   |                            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+help: consider using the `'static` lifetime
+   |
+LL | fn f2_(a: i32, b: i32) -> (&'static i32, &i32) { loop {} }
+   |                            ^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:14:34
+   |
+LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
+   |                                  ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+help: consider using the `'static` lifetime
+   |
+LL | fn f2_(a: i32, b: i32) -> (&i32, &'static i32) { loop {} }
+   |                                  ^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:19:17
+   |
+LL | fn f3(s: &S) -> &i32 { loop {} }
+   |          --     ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say which one of `s`'s 3 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+   |
+LL | fn f3<'a>(s: &'a S) -> &'a i32 { loop {} }
+   |      ^^^^    ^^^^^     ^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:21:26
+   |
+LL | fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
+   |           --     --      ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `s`'s 3 lifetimes or one of `t`'s 3 lifetimes
+help: consider introducing a named lifetime parameter
+   |
+LL | fn f3_<'a>(s: &'a S, t: &'a S) -> (&'a i32, &i32) { loop {} }
+   |       ^^^^    ^^^^^     ^^^^^      ^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:21:32
+   |
+LL | fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
+   |           --     --            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `s`'s 3 lifetimes or one of `t`'s 3 lifetimes
+help: consider introducing a named lifetime parameter
+   |
+LL | fn f3_<'a>(s: &'a S, t: &'a S) -> (&i32, &'a i32) { loop {} }
+   |       ^^^^    ^^^^^     ^^^^^            ^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:25:42
+   |
+LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
+   |                  -------     -------     ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
+note: these named lifetimes are available to use
+  --> $DIR/return-elided-lifetime.rs:25:7
+   |
+LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
+   |       ^^  ^^
+help: consider using one of the available lifetimes here
+   |
+LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &'lifetime i32 { loop {} }
+   |                                          ^^^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:27:44
+   |
+LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
+   |                   -------     -------      ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
+note: these named lifetimes are available to use
+  --> $DIR/return-elided-lifetime.rs:27:8
+   |
+LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
+   |        ^^  ^^
+help: consider using one of the available lifetimes here
+   |
+LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&'lifetime i32, &i32) { loop {} }
+   |                                            ^^^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:27:50
+   |
+LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
+   |                   -------     -------            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
+note: these named lifetimes are available to use
+  --> $DIR/return-elided-lifetime.rs:27:8
+   |
+LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
+   |        ^^  ^^
+help: consider using one of the available lifetimes here
+   |
+LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &'lifetime i32) { loop {} }
+   |                                                  ^^^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:31:35
+   |
+LL | fn f5<'a>(a: &'a i32, b: &i32) -> &i32 { loop {} }
+   |              -------     ----     ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
+help: consider using the `'a` lifetime
+   |
+LL | fn f5<'a>(a: &'a i32, b: &i32) -> &'a i32 { loop {} }
+   |                                   ^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:33:37
+   |
+LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
+   |               -------     ----      ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
+help: consider using the `'a` lifetime
+   |
+LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&'a i32, &i32) { loop {} }
+   |                                     ^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-elided-lifetime.rs:33:43
+   |
+LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
+   |               -------     ----            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
+help: consider using the `'a` lifetime
+   |
+LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &'a i32) { loop {} }
+   |                                           ^^^
+
+error: aborting due to 15 previous errors
+
+For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/suggestions/unsized-function-parameter.fixed b/src/test/ui/suggestions/unsized-function-parameter.fixed
new file mode 100644 (file)
index 0000000..18e93cb
--- /dev/null
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+
+fn foo1(bar: &str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| 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
+
+fn foo2(_bar: &str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| 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
+
+fn foo3(_: &str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| 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
+
+fn main() {}
diff --git a/src/test/ui/suggestions/unsized-function-parameter.rs b/src/test/ui/suggestions/unsized-function-parameter.rs
new file mode 100644 (file)
index 0000000..344ee71
--- /dev/null
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+
+fn foo1(bar: str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| 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
+
+fn foo2(_bar: str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| 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
+
+fn foo3(_: str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| 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
+
+fn main() {}
diff --git a/src/test/ui/suggestions/unsized-function-parameter.stderr b/src/test/ui/suggestions/unsized-function-parameter.stderr
new file mode 100644 (file)
index 0000000..8cbd8bf
--- /dev/null
@@ -0,0 +1,42 @@
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-function-parameter.rs:5:9
+   |
+LL | fn foo1(bar: str) {}
+   |         ^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = 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 foo1(bar: &str) {}
+   |              ^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-function-parameter.rs:11:9
+   |
+LL | fn foo2(_bar: str) {}
+   |         ^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = 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 foo2(_bar: &str) {}
+   |               ^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-function-parameter.rs:17:9
+   |
+LL | fn foo3(_: str) {}
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = 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 foo3(_: &str) {}
+   |            ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/target-feature/rust-specific-name-no-warnings.rs b/src/test/ui/target-feature/rust-specific-name-no-warnings.rs
new file mode 100644 (file)
index 0000000..1708a71
--- /dev/null
@@ -0,0 +1,5 @@
+// build-pass
+// only-x86
+// compile-flags: -C target-feature=+pclmulqdq
+
+fn main() {}
index 4adb161d9ee4b09b17c2b2e2c4f8e676e9e22d2f..874dbdb42c33c133a14dfe75d19b5e33e2049333 100644 (file)
@@ -1,6 +1,6 @@
 // error-pattern:building tests with panic=abort is not supported
 // no-prefer-dynamic
-// compile-flags: --test -Cpanic=abort
+// compile-flags: --test -Cpanic=abort -Zpanic-abort-tests=no
 // run-flags: --test-threads=1
 
 // ignore-wasm no panic or subprocess support
index 313d39de36b6a98d6f156642c9049a109c4f1d0d..547e5445aa1b63f3207db8a157b708dc2c066134 100644 (file)
@@ -1,4 +1,4 @@
-#![feature(const_fn, thread_local)]
+#![feature(thread_local)]
 
 #[thread_local]
 static A: u32 = 1;
index b237b1c480e3a6b73aa599a5054c76be8d55ffbf..4d3c4e8accd8888bbfb535927bf773387322b6da 100644 (file)
@@ -1,4 +1,4 @@
-#![feature(cfg_target_thread_local, const_fn, thread_local)]
+#![feature(cfg_target_thread_local, thread_local)]
 #![crate_type = "lib"]
 
 #[cfg(target_thread_local)]
index a602d7667c48dd2201f643590f87d8e122075efe..4d81d0a5d2093f81a7532b10c944327beb486e98 100644 (file)
@@ -1,4 +1,3 @@
-#![feature(const_fn)]
 #![feature(thread_local)]
 #![feature(cfg_target_thread_local, thread_local_internals)]
 
index ee6a3b065d6eef292e7c55e41d1ffd938e3bcac1..c7b12a395a253b2ef85b68ae72166b2b3779fd1a 100644 (file)
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-43733.rs:18:5
+  --> $DIR/issue-43733.rs:17:5
    |
 LL |     __KEY.get(Default::default)
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     __KEY.get(Default::default)
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-43733.rs:22:5
+  --> $DIR/issue-43733.rs:21:5
    |
 LL |     std::thread::LocalKey::new(__getit);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
index 3b40a21d80d48233fa47a35df54d0f948fb63841..15a20899a78d1a24419303e24dde5591a9ecb19d 100644 (file)
@@ -1,7 +1,5 @@
 // run-pass
 
-#![feature(const_fn)]
-
 type Field1 = (i32, u32);
 type Field2 = f32;
 type Field3 = i64;
index 963d892931a7503b874cb5f9cf8d373694ffc01d..c7120e05007256dda4c0dedcc36076657a42c32e 100644 (file)
@@ -1,7 +1,6 @@
 #![stable(feature = "foo", since = "1.33.0")]
 #![feature(staged_api)]
 #![feature(const_raw_ptr_deref)]
-#![feature(const_fn)]
 
 #[stable(feature = "foo", since = "1.33.0")]
 #[rustc_const_unstable(feature = "const_foo", issue = "none")]
index 98d7ae9f854341209df4fad6111abec211eb5fd6..410d8d3fb4024a0922584484d1e7b63d11fe0f3e 100644 (file)
@@ -1,5 +1,5 @@
 error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-unstable-const-fn.rs:9:5
+  --> $DIR/unsafe-unstable-const-fn.rs:8:5
    |
 LL |     *a == b
    |     ^^ dereference of raw pointer
index f249d3f4574744fc0fa1859e36fc0279b490ee7c..0440cf488e8bb67374df02f075b9658dc1222f37 100644 (file)
@@ -1,4 +1,5 @@
 // run-pass
+// needs-unwind
 // ignore-windows target requires uwtable
 // ignore-wasm32-bare no proper panic=unwind support
 // compile-flags: -C panic=unwind -C force-unwind-tables=n
index e1136807b3c43e0f57bb9aebe2718d495a362049..2bf4cfc50032049e780b88db39ab6fe47ca0dfd8 100644 (file)
@@ -27,6 +27,7 @@ pub fn main() {
           target_os = "dragonfly",
           target_os = "emscripten",
           target_os = "freebsd",
+          target_os = "fuchsia",
           target_os = "linux",
           target_os = "macos",
           target_os = "netbsd",
index f3e13226d6d17a2bc5f325303494b43a45f53b7f..e51522ab3db23b0d8f1de54eb1f0113924896331 160000 (submodule)
@@ -1 +1 @@
-Subproject commit f3e13226d6d17a2bc5f325303494b43a45f53b7f
+Subproject commit e51522ab3db23b0d8f1de54eb1f0113924896331
index fc608eaabccf7827c946530eb9de7fff540eac42..54ff38e39dbe43af7c1427503481214f6dd72762 100644 (file)
@@ -85,7 +85,9 @@ fn main() {
     let cargo = &Path::new(cargo);
 
     for test in TEST_REPOS.iter().rev() {
-        test_repo(cargo, out_dir, test);
+        if args[3..].is_empty() || args[3..].iter().any(|s| s.contains(test.name)) {
+            test_repo(cargo, out_dir, test);
+        }
     }
 }
 
index 376528e30853ad9ff166b8ad2094940b7e9cea09..523bab1882836e9ddc5c0c6c81db7c0733f2bfdf 100644 (file)
@@ -29,6 +29,7 @@ out
 
 # gh pages docs
 util/gh-pages/lints.json
+**/metadata_collection.json
 
 # rustfmt backups
 *.rs.bk
index 02d2b63c9e199c936a7c86fbbd479f85ff924afe..7265d1b832376401f9a8997a9b5c4b8e2a2a8209 100644 (file)
@@ -118,7 +118,7 @@ which `IntelliJ Rust` will be able to understand.
 Run `cargo dev ide_setup --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
 you just cloned.
 The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
-Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
+Clippys `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
 Just make sure to remove the dependencies again before finally making a pull request!
 
 [rustc_repo]: https://github.com/rust-lang/rust/
index cade44a0a9ab4a8bc0cd4a3ad2489f86a4dded74..f010e6096049170c4a89256f9308da67bdfa8760 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.53"
+version = "0.1.54"
 authors = ["The Rust Clippy Developers"]
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
@@ -52,6 +52,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
 deny-warnings = []
 integration = ["tempfile"]
 internal-lints = ["clippy_lints/internal-lints"]
+metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"]
 
 [package.metadata.rust-analyzer]
 # This package uses #[feature(rustc_private)]
index 05cdd9d064a8e44b3d0d4d28361afe4b74ffc126..7ceb1da6a6ebb6712ad48e0d6dd981ea3fdf8709 100644 (file)
@@ -1,7 +1,7 @@
 [package]
 name = "clippy_lints"
 # begin automatic update
-version = "0.1.53"
+version = "0.1.54"
 # end automatic update
 authors = ["The Rust Clippy Developers"]
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
@@ -20,6 +20,7 @@ pulldown-cmark = { version = "0.8", default-features = false }
 quine-mc_cluskey = "0.2.2"
 regex-syntax = "0.6"
 serde = { version = "1.0", features = ["derive"] }
+serde_json = { version = "1.0", optional = true }
 toml = "0.5.3"
 unicode-normalization = "0.1"
 semver = "0.11"
@@ -32,6 +33,7 @@ url = { version = "2.1.0", features = ["serde"] }
 deny-warnings = []
 # build clippy with internal lints enabled, off by default
 internal-lints = ["clippy_utils/internal-lints"]
+metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"]
 
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
index 42e153909ce75b29f8264d6c4982ec732e2a435b..2a61d58e6537db119a1053ad4845cdf835fc0c39 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, is_else_clause, paths, SpanlessEq};
+use clippy_utils::{get_trait_def_id, if_sequence, in_constant, 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};
@@ -64,6 +64,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             return;
         }
 
+        if in_constant(cx, expr.hir_id) {
+            return;
+        }
+
         // Check that there exists at least one explicit else condition
         let (conds, _) = if_sequence(expr);
         if conds.len() < 2 {
index 710da8fe9e037ea6bc21eceeafc3afe205c07410..7a53d390bb45f00159ce682c485377c7f80b6504 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -75,6 +75,7 @@ pub struct Default {
 impl LateLintPass<'_> for Default {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
+            if !in_macro(expr.span);
             // Avoid cases already linted by `field_reassign_with_default`
             if !self.reassigned_linted.contains(&expr.span);
             if let ExprKind::Call(path, ..) = expr.kind;
index 762f64fe37ad6862f19edf05576a747ec76e158e..41acf55dd7d572ec8a75994ec4e924ad4e0c70be 100644 (file)
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
 use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
+use if_chain::if_chain;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         // Find a write to a local variable.
-        match expr.kind {
-            ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) => {
-                if let Some(var) = path_to_local(lhs) {
-                    let mut visitor = ReadVisitor {
-                        cx,
-                        var,
-                        write_expr: expr,
-                        last_expr: expr,
-                    };
-                    check_for_unsequenced_reads(&mut visitor);
-                }
-            },
-            _ => {},
-        }
+        let var = if_chain! {
+            if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind;
+            if let Some(var) = path_to_local(lhs);
+            if expr.span.desugaring_kind().is_none();
+            then { var } else { return; }
+        };
+        let mut visitor = ReadVisitor {
+            cx,
+            var,
+            write_expr: expr,
+            last_expr: expr,
+        };
+        check_for_unsequenced_reads(&mut visitor);
     }
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
         match stmt.kind {
@@ -305,7 +305,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
                     self.cx,
                     EVAL_ORDER_DEPENDENCE,
                     expr.span,
-                    "unsequenced read of a variable",
+                    &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)),
                     Some(self.write_expr.span),
                     "whether read occurs before this write depends on evaluation order",
                 );
index 6b379b0d59b2bb0c807d23687de2c578524cb12a..30174fa2100dbbd2f259bbb7e8de3e9ac2ebed87 100644 (file)
@@ -1,13 +1,16 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::match_panic_def_id;
-use clippy_utils::source::snippet_opt;
-use if_chain::if_chain;
+use clippy_utils::{
+    diagnostics::span_lint_and_sugg,
+    get_async_fn_body, is_async_fn,
+    source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
+    visitors::visit_break_exprs,
+};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_span::{Span, SyntaxContext};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for missing return statements at the end of a block.
 
 declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
 
-static LINT_BREAK: &str = "change `break` to `return` as shown";
-static LINT_RETURN: &str = "add `return` as shown";
-
-fn lint(cx: &LateContext<'_>, outer_span: Span, inner_span: Span, msg: &str) {
-    let outer_span = outer_span.source_callsite();
-    let inner_span = inner_span.source_callsite();
-
-    span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing `return` statement", |diag| {
-        if let Some(snippet) = snippet_opt(cx, inner_span) {
-            diag.span_suggestion(
-                outer_span,
-                msg,
-                format!("return {}", snippet),
-                Applicability::MachineApplicable,
-            );
-        }
-    });
+fn lint_return(cx: &LateContext<'_>, span: Span) {
+    let mut app = Applicability::MachineApplicable;
+    let snip = snippet_with_applicability(cx, span, "..", &mut app);
+    span_lint_and_sugg(
+        cx,
+        IMPLICIT_RETURN,
+        span,
+        "missing `return` statement",
+        "add `return` as shown",
+        format!("return {}", snip),
+        app,
+    );
+}
+
+fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) {
+    let mut app = Applicability::MachineApplicable;
+    let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
+    span_lint_and_sugg(
+        cx,
+        IMPLICIT_RETURN,
+        break_span,
+        "missing `return` statement",
+        "change `break` to `return` as shown",
+        format!("return {}", snip),
+        app,
+    )
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum LintLocation {
+    /// The lint was applied to a parent expression.
+    Parent,
+    /// The lint was applied to this expression, a child, or not applied.
+    Inner,
+}
+impl LintLocation {
+    fn still_parent(self, b: bool) -> Self {
+        if b { self } else { Self::Inner }
+    }
+
+    fn is_parent(self) -> bool {
+        self == Self::Parent
+    }
+}
+
+// Gets the call site if the span is in a child context. Otherwise returns `None`.
+fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
+    (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span))
 }
 
-fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) {
+fn lint_implicit_returns(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    // The context of the function body.
+    ctxt: SyntaxContext,
+    // Whether the expression is from a macro expansion.
+    call_site_span: Option<Span>,
+) -> LintLocation {
     match expr.kind {
-        // loops could be using `break` instead of `return`
-        ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
-            if let Some(expr) = &block.expr {
-                expr_match(cx, expr);
-            }
-            // only needed in the case of `break` with `;` at the end
-            else if let Some(stmt) = block.stmts.last() {
-                if_chain! {
-                    if let StmtKind::Semi(expr, ..) = &stmt.kind;
-                    // make sure it's a break, otherwise we want to skip
-                    if let ExprKind::Break(.., Some(break_expr)) = &expr.kind;
-                    then {
-                            lint(cx, expr.span, break_expr.span, LINT_BREAK);
-                    }
-                }
-            }
-        },
-        // use `return` instead of `break`
-        ExprKind::Break(.., break_expr) => {
-            if let Some(break_expr) = break_expr {
-                lint(cx, expr.span, break_expr.span, LINT_BREAK);
+        ExprKind::Block(
+            Block {
+                expr: Some(block_expr), ..
+            },
+            _,
+        ) => lint_implicit_returns(
+            cx,
+            block_expr,
+            ctxt,
+            call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)),
+        )
+        .still_parent(call_site_span.is_some()),
+
+        ExprKind::If(_, then_expr, Some(else_expr)) => {
+            // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't
+            // bother checking.
+            let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some());
+            if res.is_parent() {
+                // The return was added as a parent of this if expression.
+                return res;
             }
+            lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some())
         },
-        ExprKind::If(.., if_expr, else_expr) => {
-            expr_match(cx, if_expr);
 
-            if let Some(else_expr) = else_expr {
-                expr_match(cx, else_expr);
+        ExprKind::Match(_, arms, _) => {
+            for arm in arms {
+                let res = lint_implicit_returns(
+                    cx,
+                    arm.body,
+                    ctxt,
+                    call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)),
+                )
+                .still_parent(call_site_span.is_some());
+                if res.is_parent() {
+                    // The return was added as a parent of this match expression.
+                    return res;
+                }
             }
+            LintLocation::Inner
         },
-        ExprKind::Match(.., arms, source) => {
-            let check_all_arms = match source {
-                MatchSource::IfLetDesugar {
-                    contains_else_clause: has_else,
-                } => has_else,
-                _ => true,
-            };
-
-            if check_all_arms {
-                for arm in arms {
-                    expr_match(cx, arm.body);
+
+        ExprKind::Loop(block, ..) => {
+            let mut add_return = false;
+            visit_break_exprs(block, |break_expr, dest, sub_expr| {
+                if dest.target_id.ok() == Some(expr.hir_id) {
+                    if call_site_span.is_none() && break_expr.span.ctxt() == ctxt {
+                        lint_break(cx, break_expr.span, sub_expr.unwrap().span);
+                    } else {
+                        // the break expression is from a macro call, add a return to the loop
+                        add_return = true;
+                    }
+                }
+            });
+            if add_return {
+                #[allow(clippy::option_if_let_else)]
+                if let Some(span) = call_site_span {
+                    lint_return(cx, span);
+                    LintLocation::Parent
+                } else {
+                    lint_return(cx, expr.span);
+                    LintLocation::Inner
                 }
             } else {
-                expr_match(cx, arms.first().expect("`if let` doesn't have a single arm").body);
+                LintLocation::Inner
             }
         },
-        // skip if it already has a return statement
-        ExprKind::Ret(..) => (),
-        // make sure it's not a call that panics
-        ExprKind::Call(expr, ..) => {
-            if_chain! {
-                if let ExprKind::Path(qpath) = &expr.kind;
-                if let Some(path_def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id();
-                if match_panic_def_id(cx, path_def_id);
-                then { }
-                else {
-                    lint(cx, expr.span, expr.span, LINT_RETURN)
-                }
+
+        // If expressions without an else clause, and blocks without a final expression can only be the final expression
+        // if they are divergent, or return the unit type.
+        ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => {
+            LintLocation::Inner
+        },
+
+        // Any divergent expression doesn't need a return statement.
+        ExprKind::MethodCall(..)
+        | ExprKind::Call(..)
+        | ExprKind::Binary(..)
+        | ExprKind::Unary(..)
+        | ExprKind::Index(..)
+            if cx.typeck_results().expr_ty(expr).is_never() =>
+        {
+            LintLocation::Inner
+        },
+
+        _ =>
+        {
+            #[allow(clippy::option_if_let_else)]
+            if let Some(span) = call_site_span {
+                lint_return(cx, span);
+                LintLocation::Parent
+            } else {
+                lint_return(cx, expr.span);
+                LintLocation::Inner
             }
         },
-        // everything else is missing `return`
-        _ => lint(cx, expr.span, expr.span, LINT_RETURN),
     }
 }
 
@@ -129,19 +203,32 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
     fn check_fn(
         &mut self,
         cx: &LateContext<'tcx>,
-        _: FnKind<'tcx>,
-        _: &'tcx FnDecl<'_>,
+        kind: FnKind<'tcx>,
+        decl: &'tcx FnDecl<'_>,
         body: &'tcx Body<'_>,
         span: Span,
         _: HirId,
     ) {
-        if span.from_expansion() {
+        if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_)))
+            || span.ctxt() != body.value.span.ctxt()
+            || in_external_macro(cx.sess(), span)
+        {
             return;
         }
-        let body = cx.tcx.hir().body(body.id());
-        if cx.typeck_results().expr_ty(&body.value).is_unit() {
+
+        let res_ty = cx.typeck_results().expr_ty(&body.value);
+        if res_ty.is_unit() || res_ty.is_never() {
             return;
         }
-        expr_match(cx, &body.value);
+
+        let expr = if is_async_fn(kind) {
+            match get_async_fn_body(cx.tcx, body) {
+                Some(e) => e,
+                None => return,
+            }
+        } else {
+            &body.value
+        };
+        lint_implicit_returns(cx, expr, expr.span.ctxt(), None);
     }
 }
index 8c74284fa46ea3695c4804752b431abdd42bc42f..725aa54157ecf4a352c5cfd119f83993b0330b77 100644 (file)
@@ -383,6 +383,7 @@ macro_rules! extract_msrv_attr {
 // end lints modules, do not remove this comment, it’s used in `update_lints`
 
 pub use crate::utils::conf::Conf;
+use crate::utils::conf::TryConf;
 
 /// Register all pre expansion lints
 ///
@@ -400,56 +401,40 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
 }
 
 #[doc(hidden)]
-pub fn read_conf(args: &[rustc_ast::NestedMetaItem], sess: &Session) -> Conf {
+pub fn read_conf(sess: &Session) -> Conf {
     use std::path::Path;
-    match utils::conf::file_from_args(args) {
-        Ok(file_name) => {
-            // if the user specified a file, it must exist, otherwise default to `clippy.toml` but
-            // do not require the file to exist
-            let file_name = match file_name {
-                Some(file_name) => file_name,
-                None => match utils::conf::lookup_conf_file() {
-                    Ok(Some(path)) => path,
-                    Ok(None) => return Conf::default(),
-                    Err(error) => {
-                        sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
-                            .emit();
-                        return Conf::default();
-                    },
-                },
-            };
-
-            let file_name = if file_name.is_relative() {
-                sess.local_crate_source_file
-                    .as_deref()
-                    .and_then(Path::parent)
-                    .unwrap_or_else(|| Path::new(""))
-                    .join(file_name)
-            } else {
-                file_name
-            };
-
-            let (conf, errors) = utils::conf::read(&file_name);
-
-            // all conf errors are non-fatal, we just use the default conf in case of error
-            for error in errors {
-                sess.struct_err(&format!(
-                    "error reading Clippy's configuration file `{}`: {}",
-                    file_name.display(),
-                    error
-                ))
-                .emit();
-            }
-
-            conf
-        },
-        Err((err, span)) => {
-            sess.struct_span_err(span, err)
-                .span_note(span, "Clippy will use default configuration")
+    let file_name = match utils::conf::lookup_conf_file() {
+        Ok(Some(path)) => path,
+        Ok(None) => return Conf::default(),
+        Err(error) => {
+            sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
                 .emit();
-            Conf::default()
+            return Conf::default();
         },
+    };
+
+    let file_name = if file_name.is_relative() {
+        sess.local_crate_source_file
+            .as_deref()
+            .and_then(Path::parent)
+            .unwrap_or_else(|| Path::new(""))
+            .join(file_name)
+    } else {
+        file_name
+    };
+
+    let TryConf { conf, errors } = utils::conf::read(&file_name);
+    // all conf errors are non-fatal, we just use the default conf in case of error
+    for error in errors {
+        sess.struct_err(&format!(
+            "error reading Clippy's configuration file `{}`: {}",
+            file_name.display(),
+            error
+        ))
+        .emit();
     }
+
+    conf
 }
 
 /// Register all lints and lint groups with the rustc plugin registry
@@ -1020,6 +1005,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
         store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
     }
+    #[cfg(feature = "metadata-collector-lint")]
+    {
+        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
+            store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default());
+        }
+    }
+
     store.register_late_pass(|| box utils::author::Author);
     store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
     store.register_late_pass(|| box serde_api::SerdeApi);
index 4d73aef76e87e8604294791aac254627716f9f62..9662a0b22a3ab563629a4b9e5e79f56395efb399 100644 (file)
@@ -7,9 +7,10 @@
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind};
+use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
+
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::{MultiSpan, Span};
 
@@ -26,7 +27,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
         if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
         if let Some(generic_args) = chain_method.args;
         if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
-        let ty = cx.typeck_results().node_type(ty.hir_id);
+        if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id);
         if is_type_diagnostic_item(cx, ty, sym::vec_type)
             || is_type_diagnostic_item(cx, ty, sym::vecdeque_type)
             || match_type(cx, ty, &paths::BTREEMAP)
@@ -58,20 +59,33 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
 }
 
 fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
+    fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option<HirId> {
+        if let Some(ty) = ty {
+            return Some(ty.hir_id);
+        }
+
+        if let Some(generic_args) = method_args {
+            if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) {
+                return Some(ty.hir_id);
+            }
+        }
+
+        None
+    }
     if let ExprKind::Block(block, _) = expr.kind {
         for stmt in block.stmts {
             if_chain! {
                 if let StmtKind::Local(
                     Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
-                    init: Some(init_expr), .. }
+                    init: Some(init_expr), ty, .. }
                 ) = stmt.kind;
                 if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
                 if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
-                if let Some(generic_args) = method_name.args;
-                if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
-                if let ty = cx.typeck_results().node_type(ty.hir_id);
+                if let Some(hir_id) = get_hir_id(*ty, method_name.args);
+                if let Some(ty) = cx.typeck_results().node_type_opt(hir_id);
                 if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
                     is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
+                    is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
                     match_type(cx, ty, &paths::LINKED_LIST);
                 if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
                 if let [iter_call] = &*iter_calls;
index de267cc77d2b406f0c369cd8f2e2101a3bb3502a..55404b87ec9ce60400bf8137ac93a8dbe8a09e23 100644 (file)
@@ -28,11 +28,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'
         return;
     }
     let used_in_condition = &var_visitor.ids;
-    let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) {
-        used_in_condition.is_disjoint(&used_mutably)
-    } else {
-        return;
-    };
+    let mutated_in_body = mutated_variables(expr, cx);
+    let mutated_in_condition = mutated_variables(cond, cx);
+    let no_cond_variable_mutated =
+        if let (Some(used_mutably_body), Some(used_mutably_cond)) = (mutated_in_body, mutated_in_condition) {
+            used_in_condition.is_disjoint(&used_mutably_body) && used_in_condition.is_disjoint(&used_mutably_cond)
+        } else {
+            return;
+        };
     let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v);
 
     let mut has_break_or_return_visitor = HasBreakOrReturnVisitor {
index 13b2a834b0a962ac4db369573dab48ddfc9bda16..a70e8b26087efc1aa5bb2e0d346fe6eeb3789701 100644 (file)
@@ -1590,9 +1590,9 @@ fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
 fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
     if_chain! {
-        if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind;
+        if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
         if is_lang_ctor(cx, qpath, OptionSome);
-        if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
+        if let PatKind::Binding(rb, .., ident, _) = first_pat.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;
@@ -1712,6 +1712,7 @@ mod redundant_pattern_match {
     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_data_structures::fx::FxHashSet;
     use rustc_errors::Applicability;
     use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
     use rustc_hir::{
@@ -1739,6 +1740,13 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     /// 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 {
+        type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
+    }
+
+    fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+        if !seen.insert(ty) {
+            return false;
+        }
         if !ty.needs_drop(cx.tcx, cx.param_env) {
             false
         } else if !cx
@@ -1750,12 +1758,12 @@ fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
             // 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::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+                ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
                 ty::Adt(adt, subs) => adt
                     .all_fields()
                     .map(|f| f.ty(cx.tcx, subs))
-                    .any(|ty| type_needs_ordered_drop(cx, ty)),
+                    .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
                 _ => true,
             }
         }
@@ -1772,7 +1780,7 @@ fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
         {
             // Check all of the generic arguments.
             if let ty::Adt(_, subs) = ty.kind() {
-                subs.types().any(|ty| type_needs_ordered_drop(cx, ty))
+                subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
             } else {
                 true
             }
index e15dbb899b36aac49bd00dae5ebf6cc7058a4a5d..0b1b6304defcb9246352a05df39c46239f23a312 100644 (file)
@@ -2189,27 +2189,6 @@ fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
     ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 ];
 
-#[rustfmt::skip]
-const PATTERN_METHODS: [(&str, usize); 17] = [
-    ("contains", 1),
-    ("starts_with", 1),
-    ("ends_with", 1),
-    ("find", 1),
-    ("rfind", 1),
-    ("split", 1),
-    ("rsplit", 1),
-    ("split_terminator", 1),
-    ("rsplit_terminator", 1),
-    ("splitn", 2),
-    ("rsplitn", 2),
-    ("matches", 1),
-    ("rmatches", 1),
-    ("match_indices", 1),
-    ("rmatch_indices", 1),
-    ("trim_start_matches", 1),
-    ("trim_end_matches", 1),
-];
-
 #[derive(Clone, Copy, PartialEq, Debug)]
 enum SelfKind {
     Value,
index f4090c7c617d4499c07cc79b6c3bcd5030f43ef4..d313a3db479de6d51c5797985804403272cf32f0 100644 (file)
@@ -9,9 +9,31 @@
 
 use super::SINGLE_CHAR_PATTERN;
 
+const PATTERN_METHODS: [(&str, usize); 19] = [
+    ("contains", 1),
+    ("starts_with", 1),
+    ("ends_with", 1),
+    ("find", 1),
+    ("rfind", 1),
+    ("split", 1),
+    ("rsplit", 1),
+    ("split_terminator", 1),
+    ("rsplit_terminator", 1),
+    ("splitn", 2),
+    ("rsplitn", 2),
+    ("matches", 1),
+    ("rmatches", 1),
+    ("match_indices", 1),
+    ("rmatch_indices", 1),
+    ("strip_prefix", 1),
+    ("strip_suffix", 1),
+    ("trim_start_matches", 1),
+    ("trim_end_matches", 1),
+];
+
 /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
 pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
-    for &(method, pos) in &crate::methods::PATTERN_METHODS {
+    for &(method, pos) in &PATTERN_METHODS {
         if_chain! {
             if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind();
             if *ty.kind() == ty::Str;
index b61c4ffe9b3aebb5abd0a4d39ec94abf1d75e830..8b66587bfd1620910db1411507609967275976c9 100644 (file)
@@ -6,6 +6,7 @@
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
+use rustc_middle::ty::{self, TyS};
 use rustc_span::sym;
 
 use super::UNNECESSARY_FILTER_MAP;
@@ -28,25 +29,28 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
         found_mapping |= return_visitor.found_mapping;
         found_filtering |= return_visitor.found_filtering;
 
-        if !found_filtering {
-            span_lint(
-                cx,
-                UNNECESSARY_FILTER_MAP,
-                expr.span,
-                "this `.filter_map` can be written more simply using `.map`",
-            );
-            return;
-        }
-
-        if !found_mapping && !mutates_arg {
-            span_lint(
-                cx,
-                UNNECESSARY_FILTER_MAP,
-                expr.span,
-                "this `.filter_map` can be written more simply using `.filter`",
-            );
+        let sugg = if !found_filtering {
+            "map"
+        } else if !found_mapping && !mutates_arg {
+            let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
+            match cx.typeck_results().expr_ty(&body.value).kind() {
+                ty::Adt(adt, subst)
+                    if cx.tcx.is_diagnostic_item(sym::option_type, adt.did)
+                        && TyS::same_type(in_ty, subst.type_at(0)) =>
+                {
+                    "filter"
+                },
+                _ => return,
+            }
+        } else {
             return;
-        }
+        };
+        span_lint(
+            cx,
+            UNNECESSARY_FILTER_MAP,
+            expr.span,
+            &format!("this `.filter_map` can be written more simply using `.{}`", sugg),
+        );
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/misc_early.rs b/src/tools/clippy/clippy_lints/src/misc_early.rs
deleted file mode 100644 (file)
index 3c6a707..0000000
+++ /dev/null
@@ -1,569 +0,0 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
-use rustc_ast::ast::{
-    BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability,
-    NodeId, Pat, PatKind, UnOp,
-};
-use rustc_ast::visit::FnKind;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::Applicability;
-use rustc_hir::PrimTy;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for structure field patterns bound to wildcards.
-    ///
-    /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
-    /// the fields that are actually bound.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// # struct Foo {
-    /// #     a: i32,
-    /// #     b: i32,
-    /// #     c: i32,
-    /// # }
-    /// let f = Foo { a: 0, b: 0, c: 0 };
-    ///
-    /// // Bad
-    /// match f {
-    ///     Foo { a: _, b: 0, .. } => {},
-    ///     Foo { a: _, b: _, c: _ } => {},
-    /// }
-    ///
-    /// // Good
-    /// match f {
-    ///     Foo { b: 0, .. } => {},
-    ///     Foo { .. } => {},
-    /// }
-    /// ```
-    pub UNNEEDED_FIELD_PATTERN,
-    restriction,
-    "struct fields bound to a wildcard instead of using `..`"
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for function arguments having the similar names
-    /// differing by an underscore.
-    ///
-    /// **Why is this bad?** It affects code readability.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// // Bad
-    /// fn foo(a: i32, _a: i32) {}
-    ///
-    /// // Good
-    /// fn bar(a: i32, _b: i32) {}
-    /// ```
-    pub DUPLICATE_UNDERSCORE_ARGUMENT,
-    style,
-    "function arguments having names which only differ by an underscore"
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Detects expressions of the form `--x`.
-    ///
-    /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
-    /// decremented.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// let mut x = 3;
-    /// --x;
-    /// ```
-    pub DOUBLE_NEG,
-    style,
-    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Warns on hexadecimal literals with mixed-case letter
-    /// digits.
-    ///
-    /// **Why is this bad?** It looks confusing.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// // Bad
-    /// let y = 0x1a9BAcD;
-    ///
-    /// // Good
-    /// let y = 0x1A9BACD;
-    /// ```
-    pub MIXED_CASE_HEX_LITERALS,
-    style,
-    "hex literals whose letter digits are not consistently upper- or lowercased"
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Warns if literal suffixes are not separated by an
-    /// underscore.
-    ///
-    /// **Why is this bad?** It is much less readable.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// // Bad
-    /// let y = 123832i32;
-    ///
-    /// // Good
-    /// let y = 123832_i32;
-    /// ```
-    pub UNSEPARATED_LITERAL_SUFFIX,
-    pedantic,
-    "literals whose suffix is not separated by an underscore"
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Warns if an integral constant literal starts with `0`.
-    ///
-    /// **Why is this bad?** In some languages (including the infamous C language
-    /// and most of its
-    /// family), this marks an octal constant. In Rust however, this is a decimal
-    /// constant. This could
-    /// be confusing for both the writer and a reader of the constant.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    ///
-    /// In Rust:
-    /// ```rust
-    /// fn main() {
-    ///     let a = 0123;
-    ///     println!("{}", a);
-    /// }
-    /// ```
-    ///
-    /// prints `123`, while in C:
-    ///
-    /// ```c
-    /// #include <stdio.h>
-    ///
-    /// int main() {
-    ///     int a = 0123;
-    ///     printf("%d\n", a);
-    /// }
-    /// ```
-    ///
-    /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
-    pub ZERO_PREFIXED_LITERAL,
-    complexity,
-    "integer literals starting with `0`"
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Warns if a generic shadows a built-in type.
-    ///
-    /// **Why is this bad?** This gives surprising type errors.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    ///
-    /// ```ignore
-    /// impl<u32> Foo<u32> {
-    ///     fn impl_func(&self) -> u32 {
-    ///         42
-    ///     }
-    /// }
-    /// ```
-    pub BUILTIN_TYPE_SHADOW,
-    style,
-    "shadowing a builtin type"
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for patterns in the form `name @ _`.
-    ///
-    /// **Why is this bad?** It's almost always more readable to just use direct
-    /// bindings.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// # let v = Some("abc");
-    ///
-    /// // Bad
-    /// match v {
-    ///     Some(x) => (),
-    ///     y @ _ => (),
-    /// }
-    ///
-    /// // Good
-    /// match v {
-    ///     Some(x) => (),
-    ///     y => (),
-    /// }
-    /// ```
-    pub REDUNDANT_PATTERN,
-    style,
-    "using `name @ _` in a pattern"
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for tuple patterns with a wildcard
-    /// pattern (`_`) is next to a rest pattern (`..`).
-    ///
-    /// _NOTE_: While `_, ..` means there is at least one element left, `..`
-    /// means there are 0 or more elements left. This can make a difference
-    /// when refactoring, but shouldn't result in errors in the refactored code,
-    /// since the wildcard pattern isn't used anyway.
-    /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
-    /// can match that element as well.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// # struct TupleStruct(u32, u32, u32);
-    /// # let t = TupleStruct(1, 2, 3);
-    /// // Bad
-    /// match t {
-    ///     TupleStruct(0, .., _) => (),
-    ///     _ => (),
-    /// }
-    ///
-    /// // Good
-    /// match t {
-    ///     TupleStruct(0, ..) => (),
-    ///     _ => (),
-    /// }
-    /// ```
-    pub UNNEEDED_WILDCARD_PATTERN,
-    complexity,
-    "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
-}
-
-declare_lint_pass!(MiscEarlyLints => [
-    UNNEEDED_FIELD_PATTERN,
-    DUPLICATE_UNDERSCORE_ARGUMENT,
-    DOUBLE_NEG,
-    MIXED_CASE_HEX_LITERALS,
-    UNSEPARATED_LITERAL_SUFFIX,
-    ZERO_PREFIXED_LITERAL,
-    BUILTIN_TYPE_SHADOW,
-    REDUNDANT_PATTERN,
-    UNNEEDED_WILDCARD_PATTERN,
-]);
-
-impl EarlyLintPass for MiscEarlyLints {
-    fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
-        for param in &gen.params {
-            if let GenericParamKind::Type { .. } = param.kind {
-                if let Some(prim_ty) = PrimTy::from_name(param.ident.name) {
-                    span_lint(
-                        cx,
-                        BUILTIN_TYPE_SHADOW,
-                        param.ident.span,
-                        &format!("this generic shadows the built-in type `{}`", prim_ty.name()),
-                    );
-                }
-            }
-        }
-    }
-
-    fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
-        if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind {
-            let mut wilds = 0;
-            let type_name = npat
-                .segments
-                .last()
-                .expect("A path must have at least one segment")
-                .ident
-                .name;
-
-            for field in pfields {
-                if let PatKind::Wild = field.pat.kind {
-                    wilds += 1;
-                }
-            }
-            if !pfields.is_empty() && wilds == pfields.len() {
-                span_lint_and_help(
-                    cx,
-                    UNNEEDED_FIELD_PATTERN,
-                    pat.span,
-                    "all the struct fields are matched to a wildcard pattern, consider using `..`",
-                    None,
-                    &format!("try with `{} {{ .. }}` instead", type_name),
-                );
-                return;
-            }
-            if wilds > 0 {
-                for field in pfields {
-                    if let PatKind::Wild = field.pat.kind {
-                        wilds -= 1;
-                        if wilds > 0 {
-                            span_lint(
-                                cx,
-                                UNNEEDED_FIELD_PATTERN,
-                                field.span,
-                                "you matched a field with a wildcard pattern, consider using `..` instead",
-                            );
-                        } else {
-                            let mut normal = vec![];
-
-                            for field in pfields {
-                                match field.pat.kind {
-                                    PatKind::Wild => {},
-                                    _ => {
-                                        if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
-                                            normal.push(n);
-                                        }
-                                    },
-                                }
-                            }
-
-                            span_lint_and_help(
-                                cx,
-                                UNNEEDED_FIELD_PATTERN,
-                                field.span,
-                                "you matched a field with a wildcard pattern, consider using `..` \
-                                 instead",
-                                None,
-                                &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
-                            );
-                        }
-                    }
-                }
-            }
-        }
-
-        if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind {
-            let left_binding = match left {
-                BindingMode::ByRef(Mutability::Mut) => "ref mut ",
-                BindingMode::ByRef(Mutability::Not) => "ref ",
-                BindingMode::ByValue(..) => "",
-            };
-
-            if let PatKind::Wild = right.kind {
-                span_lint_and_sugg(
-                    cx,
-                    REDUNDANT_PATTERN,
-                    pat.span,
-                    &format!(
-                        "the `{} @ _` pattern can be written as just `{}`",
-                        ident.name, ident.name,
-                    ),
-                    "try",
-                    format!("{}{}", left_binding, ident.name),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-
-        check_unneeded_wildcard_pattern(cx, pat);
-    }
-
-    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
-        let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
-
-        for arg in &fn_kind.decl().inputs {
-            if let PatKind::Ident(_, ident, None) = arg.pat.kind {
-                let arg_name = ident.to_string();
-
-                if let Some(arg_name) = arg_name.strip_prefix('_') {
-                    if let Some(correspondence) = registered_names.get(arg_name) {
-                        span_lint(
-                            cx,
-                            DUPLICATE_UNDERSCORE_ARGUMENT,
-                            *correspondence,
-                            &format!(
-                                "`{}` already exists, having another argument having almost the same \
-                                 name makes code comprehension and documentation more difficult",
-                                arg_name
-                            ),
-                        );
-                    }
-                } else {
-                    registered_names.insert(arg_name, arg.pat.span);
-                }
-            }
-        }
-    }
-
-    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-        match expr.kind {
-            ExprKind::Unary(UnOp::Neg, ref inner) => {
-                if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
-                    span_lint(
-                        cx,
-                        DOUBLE_NEG,
-                        expr.span,
-                        "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
-                    );
-                }
-            },
-            ExprKind::Lit(ref lit) => Self::check_lit(cx, lit),
-            _ => (),
-        }
-    }
-}
-
-impl MiscEarlyLints {
-    fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
-        // We test if first character in snippet is a number, because the snippet could be an expansion
-        // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
-        // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
-        // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
-        // FIXME: Find a better way to detect those cases.
-        let lit_snip = match snippet_opt(cx, lit.span) {
-            Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
-            _ => return,
-        };
-
-        if let LitKind::Int(value, lit_int_type) = lit.kind {
-            let suffix = match lit_int_type {
-                LitIntType::Signed(ty) => ty.name_str(),
-                LitIntType::Unsigned(ty) => ty.name_str(),
-                LitIntType::Unsuffixed => "",
-            };
-
-            let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
-                val
-            } else {
-                return; // It's useless so shouldn't lint.
-            };
-            // Do not lint when literal is unsuffixed.
-            if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
-                span_lint_and_sugg(
-                    cx,
-                    UNSEPARATED_LITERAL_SUFFIX,
-                    lit.span,
-                    "integer type suffix should be separated by an underscore",
-                    "add an underscore",
-                    format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
-                    Applicability::MachineApplicable,
-                );
-            }
-
-            if lit_snip.starts_with("0x") {
-                if maybe_last_sep_idx <= 2 {
-                    // It's meaningless or causes range error.
-                    return;
-                }
-                let mut seen = (false, false);
-                for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
-                    match ch {
-                        b'a'..=b'f' => seen.0 = true,
-                        b'A'..=b'F' => seen.1 = true,
-                        _ => {},
-                    }
-                    if seen.0 && seen.1 {
-                        span_lint(
-                            cx,
-                            MIXED_CASE_HEX_LITERALS,
-                            lit.span,
-                            "inconsistent casing in hexadecimal literal",
-                        );
-                        break;
-                    }
-                }
-            } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
-                /* nothing to do */
-            } else if value != 0 && lit_snip.starts_with('0') {
-                span_lint_and_then(
-                    cx,
-                    ZERO_PREFIXED_LITERAL,
-                    lit.span,
-                    "this is a decimal constant",
-                    |diag| {
-                        diag.span_suggestion(
-                            lit.span,
-                            "if you mean to use a decimal constant, remove the `0` to avoid confusion",
-                            lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
-                            Applicability::MaybeIncorrect,
-                        );
-                        diag.span_suggestion(
-                            lit.span,
-                            "if you mean to use an octal constant, use `0o`",
-                            format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
-                            Applicability::MaybeIncorrect,
-                        );
-                    },
-                );
-            }
-        } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
-            let suffix = float_ty.name_str();
-            let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
-                val
-            } else {
-                return; // It's useless so shouldn't lint.
-            };
-            if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
-                span_lint_and_sugg(
-                    cx,
-                    UNSEPARATED_LITERAL_SUFFIX,
-                    lit.span,
-                    "float type suffix should be separated by an underscore",
-                    "add an underscore",
-                    format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-    }
-}
-
-fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
-    if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
-        fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
-            span_lint_and_sugg(
-                cx,
-                UNNEEDED_WILDCARD_PATTERN,
-                span,
-                if only_one {
-                    "this pattern is unneeded as the `..` pattern can match that element"
-                } else {
-                    "these patterns are unneeded as the `..` pattern can match those elements"
-                },
-                if only_one { "remove it" } else { "remove them" },
-                "".to_string(),
-                Applicability::MachineApplicable,
-            );
-        }
-
-        if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
-            if let Some((left_index, left_pat)) = patterns[..rest_index]
-                .iter()
-                .rev()
-                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
-                .enumerate()
-                .last()
-            {
-                span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
-            }
-
-            if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
-                .iter()
-                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
-                .enumerate()
-                .last()
-            {
-                span_lint(
-                    cx,
-                    patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
-                    right_index == 0,
-                );
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs b/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs
new file mode 100644 (file)
index 0000000..9f6b0bd
--- /dev/null
@@ -0,0 +1,19 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_ast::ast::{GenericParam, GenericParamKind};
+use rustc_hir::PrimTy;
+use rustc_lint::EarlyContext;
+
+use super::BUILTIN_TYPE_SHADOW;
+
+pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) {
+    if let GenericParamKind::Type { .. } = param.kind {
+        if let Some(prim_ty) = PrimTy::from_name(param.ident.name) {
+            span_lint(
+                cx,
+                BUILTIN_TYPE_SHADOW,
+                param.ident.span,
+                &format!("this generic shadows the built-in type `{}`", prim_ty.name()),
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/double_neg.rs b/src/tools/clippy/clippy_lints/src/misc_early/double_neg.rs
new file mode 100644 (file)
index 0000000..6f65778
--- /dev/null
@@ -0,0 +1,23 @@
+use super::MiscEarlyLints;
+use clippy_utils::diagnostics::span_lint;
+use rustc_ast::ast::{Expr, ExprKind, UnOp};
+use rustc_lint::EarlyContext;
+
+use super::DOUBLE_NEG;
+
+pub(super) fn check(cx: &EarlyContext<'_>, expr: &Expr) {
+    match expr.kind {
+        ExprKind::Unary(UnOp::Neg, ref inner) => {
+            if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
+                span_lint(
+                    cx,
+                    DOUBLE_NEG,
+                    expr.span,
+                    "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
+                );
+            }
+        },
+        ExprKind::Lit(ref lit) => MiscEarlyLints::check_lit(cx, lit),
+        _ => (),
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
new file mode 100644 (file)
index 0000000..80e2421
--- /dev/null
@@ -0,0 +1,34 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_ast::ast::Lit;
+use rustc_lint::EarlyContext;
+
+use super::MIXED_CASE_HEX_LITERALS;
+
+pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) {
+    let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
+        val
+    } else {
+        return; // It's useless so shouldn't lint.
+    };
+    if maybe_last_sep_idx <= 2 {
+        // It's meaningless or causes range error.
+        return;
+    }
+    let mut seen = (false, false);
+    for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
+        match ch {
+            b'a'..=b'f' => seen.0 = true,
+            b'A'..=b'F' => seen.1 = true,
+            _ => {},
+        }
+        if seen.0 && seen.1 {
+            span_lint(
+                cx,
+                MIXED_CASE_HEX_LITERALS,
+                lit.span,
+                "inconsistent casing in hexadecimal literal",
+            );
+            break;
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
new file mode 100644 (file)
index 0000000..dd38316
--- /dev/null
@@ -0,0 +1,348 @@
+mod builtin_type_shadow;
+mod double_neg;
+mod mixed_case_hex_literals;
+mod redundant_pattern;
+mod unneeded_field_pattern;
+mod unneeded_wildcard_pattern;
+mod unseparated_literal_suffix;
+mod zero_prefixed_literal;
+
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::snippet_opt;
+use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
+use rustc_ast::visit::FnKind;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for structure field patterns bound to wildcards.
+    ///
+    /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
+    /// the fields that are actually bound.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// # struct Foo {
+    /// #     a: i32,
+    /// #     b: i32,
+    /// #     c: i32,
+    /// # }
+    /// let f = Foo { a: 0, b: 0, c: 0 };
+    ///
+    /// // Bad
+    /// match f {
+    ///     Foo { a: _, b: 0, .. } => {},
+    ///     Foo { a: _, b: _, c: _ } => {},
+    /// }
+    ///
+    /// // Good
+    /// match f {
+    ///     Foo { b: 0, .. } => {},
+    ///     Foo { .. } => {},
+    /// }
+    /// ```
+    pub UNNEEDED_FIELD_PATTERN,
+    restriction,
+    "struct fields bound to a wildcard instead of using `..`"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for function arguments having the similar names
+    /// differing by an underscore.
+    ///
+    /// **Why is this bad?** It affects code readability.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// // Bad
+    /// fn foo(a: i32, _a: i32) {}
+    ///
+    /// // Good
+    /// fn bar(a: i32, _b: i32) {}
+    /// ```
+    pub DUPLICATE_UNDERSCORE_ARGUMENT,
+    style,
+    "function arguments having names which only differ by an underscore"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Detects expressions of the form `--x`.
+    ///
+    /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
+    /// decremented.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// let mut x = 3;
+    /// --x;
+    /// ```
+    pub DOUBLE_NEG,
+    style,
+    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Warns on hexadecimal literals with mixed-case letter
+    /// digits.
+    ///
+    /// **Why is this bad?** It looks confusing.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// // Bad
+    /// let y = 0x1a9BAcD;
+    ///
+    /// // Good
+    /// let y = 0x1A9BACD;
+    /// ```
+    pub MIXED_CASE_HEX_LITERALS,
+    style,
+    "hex literals whose letter digits are not consistently upper- or lowercased"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Warns if literal suffixes are not separated by an
+    /// underscore.
+    ///
+    /// **Why is this bad?** It is much less readable.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// // Bad
+    /// let y = 123832i32;
+    ///
+    /// // Good
+    /// let y = 123832_i32;
+    /// ```
+    pub UNSEPARATED_LITERAL_SUFFIX,
+    pedantic,
+    "literals whose suffix is not separated by an underscore"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Warns if an integral constant literal starts with `0`.
+    ///
+    /// **Why is this bad?** In some languages (including the infamous C language
+    /// and most of its
+    /// family), this marks an octal constant. In Rust however, this is a decimal
+    /// constant. This could
+    /// be confusing for both the writer and a reader of the constant.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// In Rust:
+    /// ```rust
+    /// fn main() {
+    ///     let a = 0123;
+    ///     println!("{}", a);
+    /// }
+    /// ```
+    ///
+    /// prints `123`, while in C:
+    ///
+    /// ```c
+    /// #include <stdio.h>
+    ///
+    /// int main() {
+    ///     int a = 0123;
+    ///     printf("%d\n", a);
+    /// }
+    /// ```
+    ///
+    /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
+    pub ZERO_PREFIXED_LITERAL,
+    complexity,
+    "integer literals starting with `0`"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Warns if a generic shadows a built-in type.
+    ///
+    /// **Why is this bad?** This gives surprising type errors.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```ignore
+    /// impl<u32> Foo<u32> {
+    ///     fn impl_func(&self) -> u32 {
+    ///         42
+    ///     }
+    /// }
+    /// ```
+    pub BUILTIN_TYPE_SHADOW,
+    style,
+    "shadowing a builtin type"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for patterns in the form `name @ _`.
+    ///
+    /// **Why is this bad?** It's almost always more readable to just use direct
+    /// bindings.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// # let v = Some("abc");
+    ///
+    /// // Bad
+    /// match v {
+    ///     Some(x) => (),
+    ///     y @ _ => (),
+    /// }
+    ///
+    /// // Good
+    /// match v {
+    ///     Some(x) => (),
+    ///     y => (),
+    /// }
+    /// ```
+    pub REDUNDANT_PATTERN,
+    style,
+    "using `name @ _` in a pattern"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for tuple patterns with a wildcard
+    /// pattern (`_`) is next to a rest pattern (`..`).
+    ///
+    /// _NOTE_: While `_, ..` means there is at least one element left, `..`
+    /// means there are 0 or more elements left. This can make a difference
+    /// when refactoring, but shouldn't result in errors in the refactored code,
+    /// since the wildcard pattern isn't used anyway.
+    /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
+    /// can match that element as well.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// # struct TupleStruct(u32, u32, u32);
+    /// # let t = TupleStruct(1, 2, 3);
+    /// // Bad
+    /// match t {
+    ///     TupleStruct(0, .., _) => (),
+    ///     _ => (),
+    /// }
+    ///
+    /// // Good
+    /// match t {
+    ///     TupleStruct(0, ..) => (),
+    ///     _ => (),
+    /// }
+    /// ```
+    pub UNNEEDED_WILDCARD_PATTERN,
+    complexity,
+    "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
+}
+
+declare_lint_pass!(MiscEarlyLints => [
+    UNNEEDED_FIELD_PATTERN,
+    DUPLICATE_UNDERSCORE_ARGUMENT,
+    DOUBLE_NEG,
+    MIXED_CASE_HEX_LITERALS,
+    UNSEPARATED_LITERAL_SUFFIX,
+    ZERO_PREFIXED_LITERAL,
+    BUILTIN_TYPE_SHADOW,
+    REDUNDANT_PATTERN,
+    UNNEEDED_WILDCARD_PATTERN,
+]);
+
+impl EarlyLintPass for MiscEarlyLints {
+    fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
+        for param in &gen.params {
+            builtin_type_shadow::check(cx, param);
+        }
+    }
+
+    fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
+        unneeded_field_pattern::check(cx, pat);
+        redundant_pattern::check(cx, pat);
+        unneeded_wildcard_pattern::check(cx, pat);
+    }
+
+    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
+        let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
+
+        for arg in &fn_kind.decl().inputs {
+            if let PatKind::Ident(_, ident, None) = arg.pat.kind {
+                let arg_name = ident.to_string();
+
+                if let Some(arg_name) = arg_name.strip_prefix('_') {
+                    if let Some(correspondence) = registered_names.get(arg_name) {
+                        span_lint(
+                            cx,
+                            DUPLICATE_UNDERSCORE_ARGUMENT,
+                            *correspondence,
+                            &format!(
+                                "`{}` already exists, having another argument having almost the same \
+                                 name makes code comprehension and documentation more difficult",
+                                arg_name
+                            ),
+                        );
+                    }
+                } else {
+                    registered_names.insert(arg_name, arg.pat.span);
+                }
+            }
+        }
+    }
+
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if in_external_macro(cx.sess(), expr.span) {
+            return;
+        }
+        double_neg::check(cx, expr)
+    }
+}
+
+impl MiscEarlyLints {
+    fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
+        // We test if first character in snippet is a number, because the snippet could be an expansion
+        // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
+        // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
+        // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
+        // FIXME: Find a better way to detect those cases.
+        let lit_snip = match snippet_opt(cx, lit.span) {
+            Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
+            _ => return,
+        };
+
+        if let LitKind::Int(value, lit_int_type) = lit.kind {
+            let suffix = match lit_int_type {
+                LitIntType::Signed(ty) => ty.name_str(),
+                LitIntType::Unsigned(ty) => ty.name_str(),
+                LitIntType::Unsuffixed => "",
+            };
+            unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "integer");
+            if lit_snip.starts_with("0x") {
+                mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip)
+            } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
+                /* nothing to do */
+            } else if value != 0 && lit_snip.starts_with('0') {
+                zero_prefixed_literal::check(cx, lit, &lit_snip)
+            }
+        } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
+            let suffix = float_ty.name_str();
+            unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "float")
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs
new file mode 100644 (file)
index 0000000..525dbf7
--- /dev/null
@@ -0,0 +1,31 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::ast::{BindingMode, Mutability, Pat, PatKind};
+use rustc_errors::Applicability;
+use rustc_lint::EarlyContext;
+
+use super::REDUNDANT_PATTERN;
+
+pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
+    if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind {
+        let left_binding = match left {
+            BindingMode::ByRef(Mutability::Mut) => "ref mut ",
+            BindingMode::ByRef(Mutability::Not) => "ref ",
+            BindingMode::ByValue(..) => "",
+        };
+
+        if let PatKind::Wild = right.kind {
+            span_lint_and_sugg(
+                cx,
+                REDUNDANT_PATTERN,
+                pat.span,
+                &format!(
+                    "the `{} @ _` pattern can be written as just `{}`",
+                    ident.name, ident.name,
+                ),
+                "try",
+                format!("{}{}", left_binding, ident.name),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs
new file mode 100644 (file)
index 0000000..329a000
--- /dev/null
@@ -0,0 +1,72 @@
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use rustc_ast::ast::{Pat, PatKind};
+use rustc_lint::{EarlyContext, LintContext};
+
+use super::UNNEEDED_FIELD_PATTERN;
+
+pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
+    if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind {
+        let mut wilds = 0;
+        let type_name = npat
+            .segments
+            .last()
+            .expect("A path must have at least one segment")
+            .ident
+            .name;
+
+        for field in pfields {
+            if let PatKind::Wild = field.pat.kind {
+                wilds += 1;
+            }
+        }
+        if !pfields.is_empty() && wilds == pfields.len() {
+            span_lint_and_help(
+                cx,
+                UNNEEDED_FIELD_PATTERN,
+                pat.span,
+                "all the struct fields are matched to a wildcard pattern, consider using `..`",
+                None,
+                &format!("try with `{} {{ .. }}` instead", type_name),
+            );
+            return;
+        }
+        if wilds > 0 {
+            for field in pfields {
+                if let PatKind::Wild = field.pat.kind {
+                    wilds -= 1;
+                    if wilds > 0 {
+                        span_lint(
+                            cx,
+                            UNNEEDED_FIELD_PATTERN,
+                            field.span,
+                            "you matched a field with a wildcard pattern, consider using `..` instead",
+                        );
+                    } else {
+                        let mut normal = vec![];
+
+                        for field in pfields {
+                            match field.pat.kind {
+                                PatKind::Wild => {},
+                                _ => {
+                                    if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
+                                        normal.push(n);
+                                    }
+                                },
+                            }
+                        }
+
+                        span_lint_and_help(
+                            cx,
+                            UNNEEDED_FIELD_PATTERN,
+                            field.span,
+                            "you matched a field with a wildcard pattern, consider using `..` \
+                             instead",
+                            None,
+                            &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
+                        );
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
new file mode 100644 (file)
index 0000000..4dd032d
--- /dev/null
@@ -0,0 +1,52 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::ast::{Pat, PatKind};
+use rustc_errors::Applicability;
+use rustc_lint::EarlyContext;
+use rustc_span::source_map::Span;
+
+use super::UNNEEDED_WILDCARD_PATTERN;
+
+pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
+    if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
+        if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
+            if let Some((left_index, left_pat)) = patterns[..rest_index]
+                .iter()
+                .rev()
+                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
+                .enumerate()
+                .last()
+            {
+                span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
+            }
+
+            if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
+                .iter()
+                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
+                .enumerate()
+                .last()
+            {
+                span_lint(
+                    cx,
+                    patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
+                    right_index == 0,
+                );
+            }
+        }
+    }
+}
+
+fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
+    span_lint_and_sugg(
+        cx,
+        UNNEEDED_WILDCARD_PATTERN,
+        span,
+        if only_one {
+            "this pattern is unneeded as the `..` pattern can match that element"
+        } else {
+            "these patterns are unneeded as the `..` pattern can match those elements"
+        },
+        if only_one { "remove it" } else { "remove them" },
+        "".to_string(),
+        Applicability::MachineApplicable,
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unseparated_literal_suffix.rs b/src/tools/clippy/clippy_lints/src/misc_early/unseparated_literal_suffix.rs
new file mode 100644 (file)
index 0000000..2018aa6
--- /dev/null
@@ -0,0 +1,26 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::ast::Lit;
+use rustc_errors::Applicability;
+use rustc_lint::EarlyContext;
+
+use super::UNSEPARATED_LITERAL_SUFFIX;
+
+pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) {
+    let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
+        val
+    } else {
+        return; // It's useless so shouldn't lint.
+    };
+    // Do not lint when literal is unsuffixed.
+    if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
+        span_lint_and_sugg(
+            cx,
+            UNSEPARATED_LITERAL_SUFFIX,
+            lit.span,
+            &format!("{} type suffix should be separated by an underscore", sugg_type),
+            "add an underscore",
+            format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
+            Applicability::MachineApplicable,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs
new file mode 100644 (file)
index 0000000..4963bba
--- /dev/null
@@ -0,0 +1,29 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::ast::Lit;
+use rustc_errors::Applicability;
+use rustc_lint::EarlyContext;
+
+use super::ZERO_PREFIXED_LITERAL;
+
+pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) {
+    span_lint_and_then(
+        cx,
+        ZERO_PREFIXED_LITERAL,
+        lit.span,
+        "this is a decimal constant",
+        |diag| {
+            diag.span_suggestion(
+                lit.span,
+                "if you mean to use a decimal constant, remove the `0` to avoid confusion",
+                lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
+                Applicability::MaybeIncorrect,
+            );
+            diag.span_suggestion(
+                lit.span,
+                "if you mean to use an octal constant, use `0o`",
+                format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
+                Applicability::MaybeIncorrect,
+            );
+        },
+    );
+}
index 191781be000cf8177919236a38389dafac24c3df..a9ae2b77119bcaa1bda2f9d9d6bfa4264c2b2899 100644 (file)
@@ -7,7 +7,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, NestedVisitorMap, Visitor};
 use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind};
-use rustc_lint::{LateContext, LateLintPass, Lint};
+use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
@@ -157,26 +157,16 @@ fn lint_initialization<'tcx>(
         vec_alloc: &VecAllocation<'_>,
     ) {
         match initialization {
-            InitializationType::Extend(e) | InitializationType::Resize(e) => Self::emit_lint(
-                cx,
-                e,
-                vec_alloc,
-                "slow zero-filling initialization",
-                SLOW_VECTOR_INITIALIZATION,
-            ),
+            InitializationType::Extend(e) | InitializationType::Resize(e) => {
+                Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization")
+            },
         };
     }
 
-    fn emit_lint<'tcx>(
-        cx: &LateContext<'tcx>,
-        slow_fill: &Expr<'_>,
-        vec_alloc: &VecAllocation<'_>,
-        msg: &str,
-        lint: &'static Lint,
-    ) {
+    fn emit_lint<'tcx>(cx: &LateContext<'tcx>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) {
         let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len");
 
-        span_lint_and_then(cx, lint, slow_fill.span, msg, |diag| {
+        span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| {
             diag.span_suggestion(
                 vec_alloc.allocation_expr.span,
                 "consider replace allocation with",
index ce2d0b3ab2f24962599d2ca957d5564641a2e193..e14945651f5010551820308a272693951cc992ab 100644 (file)
@@ -47,7 +47,9 @@ fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
         if_chain! {
             if let Some(stmt) = block.stmts.last();
             if let ast::StmtKind::Expr(ref expr) = stmt.kind;
-            if is_unit_expr(expr) && !stmt.span.from_expansion();
+            if is_unit_expr(expr);
+            let ctxt = block.span.ctxt();
+            if stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt;
             then {
                 let sp = expr.span;
                 span_lint_and_sugg(
index d56855a71c159b2e1cdc351d7708e9f8371b3e46..52c1dc3bdd239335438e40463066087a52d6b35f 100644 (file)
 //! Read configurations files.
 
-#![deny(clippy::missing_docs_in_private_items)]
+#![allow(clippy::module_name_repetitions)]
 
-use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
-use rustc_span::source_map;
-use source_map::Span;
-use std::lazy::SyncLazy;
+use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
+use serde::Deserialize;
+use std::error::Error;
 use std::path::{Path, PathBuf};
-use std::sync::Mutex;
 use std::{env, fmt, fs, io};
 
-/// Gets the configuration file from arguments.
-pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
-    for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
-        if arg.has_name(sym!(conf_file)) {
-            return match arg.kind {
-                MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
-                MetaItemKind::NameValue(ref value) => {
-                    if let LitKind::Str(ref file, _) = value.kind {
-                        Ok(Some(file.to_string().into()))
-                    } else {
-                        Err(("`conf_file` value must be a string", value.span))
-                    }
-                },
-            };
-        }
-    }
-
-    Ok(None)
-}
-
-/// Error from reading a configuration file.
-#[derive(Debug)]
-pub enum Error {
-    /// An I/O error.
-    Io(io::Error),
-    /// Not valid toml or doesn't fit the expected config format
-    Toml(String),
+/// Conf with parse errors
+#[derive(Default)]
+pub struct TryConf {
+    pub conf: Conf,
+    pub errors: Vec<String>,
 }
 
-impl fmt::Display for Error {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Io(err) => err.fmt(f),
-            Self::Toml(err) => err.fmt(f),
+impl TryConf {
+    fn from_error(error: impl Error) -> Self {
+        Self {
+            conf: Conf::default(),
+            errors: vec![error.to_string()],
         }
     }
 }
 
-impl From<io::Error> for Error {
-    fn from(e: io::Error) -> Self {
-        Self::Io(e)
-    }
-}
+macro_rules! define_Conf {
+    ($(
+        #[$doc:meta]
+        $(#[conf_deprecated($dep:literal)])?
+        ($name:ident: $ty:ty = $default:expr),
+    )*) => {
+        /// Clippy lint configuration
+        pub struct Conf {
+            $(#[$doc] pub $name: $ty,)*
+        }
 
-/// Vec of errors that might be collected during config toml parsing
-static ERRORS: SyncLazy<Mutex<Vec<Error>>> = SyncLazy::new(|| Mutex::new(Vec::new()));
+        mod defaults {
+            $(pub fn $name() -> $ty { $default })*
+        }
 
-macro_rules! define_Conf {
-    ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
-        mod helpers {
-            use serde::Deserialize;
-            /// Type used to store lint configuration.
-            #[derive(Deserialize)]
-            #[serde(rename_all = "kebab-case", deny_unknown_fields)]
-            pub struct Conf {
-                $(
-                    #[$doc]
-                    #[serde(default = $config_str)]
-                    #[serde(with = $config_str)]
-                    pub $config: $Ty,
-                )+
-                #[allow(dead_code)]
-                #[serde(default)]
-                third_party: Option<::toml::Value>,
+        impl Default for Conf {
+            fn default() -> Self {
+                Self { $($name: defaults::$name(),)* }
             }
+        }
 
-            $(
-                mod $config {
-                    use serde::Deserialize;
-                    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
-                        use super::super::{ERRORS, Error};
+        impl<'de> Deserialize<'de> for TryConf {
+            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
+                deserializer.deserialize_map(ConfVisitor)
+            }
+        }
 
-                        Ok(
-                            <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
-                                ERRORS
-                                    .lock()
-                                    .expect("no threading here")
-                                    .push(Error::Toml(e.to_string()));
-                                super::$config()
-                            })
-                        )
-                    }
-                }
+        #[derive(Deserialize)]
+        #[serde(field_identifier, rename_all = "kebab-case")]
+        #[allow(non_camel_case_types)]
+        enum Field { $($name,)* third_party, }
+
+        struct ConfVisitor;
+
+        impl<'de> Visitor<'de> for ConfVisitor {
+            type Value = TryConf;
 
-                #[must_use]
-                fn $config() -> $Ty {
-                    let x = $default;
-                    x
+            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+                formatter.write_str("Conf")
+            }
+
+            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
+                let mut errors = Vec::new();
+                $(let mut $name = None;)*
+                // could get `Field` here directly, but get `str` first for diagnostics
+                while let Some(name) = map.next_key::<&str>()? {
+                    match Field::deserialize(name.into_deserializer())? {
+                        $(Field::$name => {
+                            $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
+                            match map.next_value() {
+                                Err(e) => errors.push(e.to_string()),
+                                Ok(value) => match $name {
+                                    Some(_) => errors.push(format!("duplicate field `{}`", name)),
+                                    None => $name = Some(value),
+                                }
+                            }
+                        })*
+                        // white-listed; ignore
+                        Field::third_party => drop(map.next_value::<IgnoredAny>())
+                    }
                 }
-            )+
+                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
+                Ok(TryConf { conf, errors })
+            }
         }
     };
 }
 
-pub use self::helpers::Conf;
+// N.B., this macro is parsed by util/lintlib.py
 define_Conf! {
-    /// 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: 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, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports
+    (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()),
+    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
     /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
-    (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
+    (cognitive_complexity_threshold: u64 = 25),
     /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
-    (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option<u64>, None),
+    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
+    (cyclomatic_complexity_threshold: Option<u64> = None),
     /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
-    (doc_valid_idents, "doc_valid_idents": Vec<String>, [
+    (doc_valid_idents: Vec<String> = [
         "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
         "DirectX",
         "ECMAScript",
@@ -136,54 +125,47 @@ fn $config() -> $Ty {
         "CamelCase",
     ].iter().map(ToString::to_string).collect()),
     /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
-    (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
+    (too_many_arguments_threshold: u64 = 7),
     /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
-    (type_complexity_threshold, "type_complexity_threshold": u64, 250),
+    (type_complexity_threshold: u64 = 250),
     /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
-    (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
+    (single_char_binding_names_threshold: u64 = 4),
     /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
-    (too_large_for_stack, "too_large_for_stack": u64, 200),
+    (too_large_for_stack: u64 = 200),
     /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
-    (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
+    (enum_variant_name_threshold: u64 = 3),
     /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
-    (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200),
+    (enum_variant_size_threshold: u64 = 200),
     /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
-    (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1),
+    (verbose_bit_mask_threshold: u64 = 1),
     /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
-    (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
+    (literal_representation_threshold: u64 = 16384),
     /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
-    (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
+    (trivial_copy_size_limit: Option<u64> = None),
     /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
-    (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256),
+    (pass_by_value_size_limit: u64 = 256),
     /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
-    (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
+    (too_many_lines_threshold: u64 = 100),
     /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
-    (array_size_threshold, "array_size_threshold": u64, 512_000),
+    (array_size_threshold: u64 = 512_000),
     /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
-    (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
+    (vec_box_size_threshold: u64 = 4096),
     /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
-    (max_trait_bounds, "max_trait_bounds": u64, 3),
+    (max_trait_bounds: u64 = 3),
     /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
-    (max_struct_bools, "max_struct_bools": u64, 3),
+    (max_struct_bools: u64 = 3),
     /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
-    (max_fn_params_bools, "max_fn_params_bools": u64, 3),
+    (max_fn_params_bools: u64 = 3),
     /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
-    (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
+    (warn_on_all_wildcard_imports: bool = false),
     /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
-    (disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
+    (disallowed_methods: Vec<String> = Vec::new()),
     /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
-    (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),
+    (unreadable_literal_lint_fractions: bool = true),
     /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
-    (upper_case_acronyms_aggressive, "upper_case_acronyms_aggressive": bool, false),
+    (upper_case_acronyms_aggressive: bool = false),
     /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
-    (cargo_ignore_publish, "cargo_ignore_publish": bool, false),
-}
-
-impl Default for Conf {
-    #[must_use]
-    fn default() -> Self {
-        toml::from_str("").expect("we never error on empty config files")
-    }
+    (cargo_ignore_publish: bool = false),
 }
 
 /// Search for the configuration file.
@@ -217,43 +199,13 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
     }
 }
 
-/// Produces a `Conf` filled with the default values and forwards the errors
-///
-/// Used internally for convenience
-fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
-    (Conf::default(), errors)
-}
-
 /// Read the `toml` configuration file.
 ///
 /// In case of error, the function tries to continue as much as possible.
-pub fn read(path: &Path) -> (Conf, Vec<Error>) {
+pub fn read(path: &Path) -> TryConf {
     let content = match fs::read_to_string(path) {
+        Err(e) => return TryConf::from_error(e),
         Ok(content) => content,
-        Err(err) => return default(vec![err.into()]),
     };
-
-    assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
-    match toml::from_str(&content) {
-        Ok(toml) => {
-            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
-
-            let toml_ref: &Conf = &toml;
-
-            let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;
-
-            if cyc_field.is_some() {
-                let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
-                errors.push(Error::Toml(cyc_err));
-            }
-
-            (toml, errors)
-        },
-        Err(e) => {
-            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
-            errors.push(Error::Toml(e.to_string()));
-
-            default(errors)
-        },
-    }
+    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
 }
index 3d3d0e19d26224190bdd8e0546ae7e31599fb9bf..ee7be24eae8013f05680ef3b7056aae0a2c973a3 100644 (file)
@@ -32,6 +32,9 @@
 
 use std::borrow::{Borrow, Cow};
 
+#[cfg(feature = "metadata-collector-lint")]
+pub mod metadata_collector;
+
 declare_clippy_lint! {
     /// **What it does:** Checks for various things we like to keep tidy in clippy.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
new file mode 100644 (file)
index 0000000..e85637c
--- /dev/null
@@ -0,0 +1,632 @@
+//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
+//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
+//!
+//! This module and therefor the entire lint is guarded by a feature flag called
+//! `metadata-collector-lint`
+//!
+//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
+//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
+//! a simple mistake)
+
+// # NITs
+// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames
+
+use if_chain::if_chain;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::{
+    self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
+};
+use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{sym, Loc, Span, Symbol};
+use serde::{ser::SerializeStruct, Serialize, Serializer};
+use std::collections::BinaryHeap;
+use std::fs::{self, OpenOptions};
+use std::io::prelude::*;
+use std::path::Path;
+
+use crate::utils::internal_lints::is_lint_ref_type;
+use clippy_utils::{
+    diagnostics::span_lint, last_path_segment, match_function_call, match_path, paths, ty::match_type,
+    ty::walk_ptrs_ty_depth,
+};
+
+/// This is the output file of the lint collector.
+const OUTPUT_FILE: &str = "../util/gh-pages/metadata_collection.json";
+/// These lints are excluded from the export.
+const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"];
+/// These groups will be ignored by the lint group matcher. This is useful for collections like
+/// `clippy::all`
+const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
+/// Lints within this group will be excluded from the collection
+const EXCLUDED_LINT_GROUPS: [&str; 1] = ["clippy::internal"];
+
+const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
+    &["clippy_utils", "diagnostics", "span_lint"],
+    &["clippy_utils", "diagnostics", "span_lint_and_help"],
+    &["clippy_utils", "diagnostics", "span_lint_and_note"],
+    &["clippy_utils", "diagnostics", "span_lint_hir"],
+    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
+    &["clippy_utils", "diagnostics", "span_lint_and_then"],
+    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
+];
+const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
+    ("span_suggestion", false),
+    ("span_suggestion_short", false),
+    ("span_suggestion_verbose", false),
+    ("span_suggestion_hidden", false),
+    ("tool_only_span_suggestion", false),
+    ("multipart_suggestion", true),
+    ("multipart_suggestions", true),
+    ("tool_only_multipart_suggestion", true),
+    ("span_suggestions", true),
+];
+const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
+    &["clippy_utils", "diagnostics", "multispan_sugg"],
+    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
+];
+
+/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
+const APPLICABILITY_NAME_INDEX: usize = 2;
+
+declare_clippy_lint! {
+    /// **What it does:** Collects metadata about clippy lints for the website.
+    ///
+    /// This lint will be used to report problems of syntax parsing. You should hopefully never
+    /// see this but never say never I guess ^^
+    ///
+    /// **Why is this bad?** This is not a bad thing but definitely a hacky way to do it. See
+    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
+    /// about the implementation.
+    ///
+    /// **Known problems:** Hopefully none. It would be pretty uncool to have a problem here :)
+    ///
+    /// **Example output:**
+    /// ```json,ignore
+    /// {
+    ///     "id": "internal_metadata_collector",
+    ///     "id_span": {
+    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
+    ///         "line": 1
+    ///     },
+    ///     "group": "clippy::internal",
+    ///     "docs": " **What it does:** Collects metadata about clippy lints for the website. [...] "
+    /// }
+    /// ```
+    pub INTERNAL_METADATA_COLLECTOR,
+    internal_warn,
+    "A busy bee collection metadata about lints"
+}
+
+impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
+
+#[allow(clippy::module_name_repetitions)]
+#[derive(Debug, Clone, Default)]
+pub struct MetadataCollector {
+    /// All collected lints
+    ///
+    /// We use a Heap here to have the lints added in alphabetic order in the export
+    lints: BinaryHeap<LintMetadata>,
+    applicability_info: FxHashMap<String, ApplicabilityInfo>,
+}
+
+impl Drop for MetadataCollector {
+    /// You might ask: How hacky is this?
+    /// My answer:     YES
+    fn drop(&mut self) {
+        // The metadata collector gets dropped twice, this makes sure that we only write
+        // when the list is full
+        if self.lints.is_empty() {
+            return;
+        }
+
+        let mut applicability_info = std::mem::take(&mut self.applicability_info);
+
+        // Mapping the final data
+        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
+        lints
+            .iter_mut()
+            .for_each(|x| x.applicability = applicability_info.remove(&x.id));
+
+        // Outputting
+        if Path::new(OUTPUT_FILE).exists() {
+            fs::remove_file(OUTPUT_FILE).unwrap();
+        }
+        let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
+        writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
+    }
+}
+
+#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
+struct LintMetadata {
+    id: String,
+    id_span: SerializableSpan,
+    group: String,
+    docs: String,
+    /// This field is only used in the output and will only be
+    /// mapped shortly before the actual output.
+    applicability: Option<ApplicabilityInfo>,
+}
+
+impl LintMetadata {
+    fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self {
+        Self {
+            id,
+            id_span,
+            group,
+            docs,
+            applicability: None,
+        }
+    }
+}
+
+#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
+struct SerializableSpan {
+    path: String,
+    line: usize,
+}
+
+impl std::fmt::Display for SerializableSpan {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
+    }
+}
+
+impl SerializableSpan {
+    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
+        Self::from_span(cx, item.ident.span)
+    }
+
+    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
+        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
+
+        Self {
+            path: format!("{}", loc.file.name),
+            line: loc.line,
+        }
+    }
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
+struct ApplicabilityInfo {
+    /// Indicates if any of the lint emissions uses multiple spans. This is related to
+    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
+    /// currently not be applied automatically.
+    is_multi_part_suggestion: bool,
+    applicability: Option<usize>,
+}
+
+impl Serialize for ApplicabilityInfo {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let index = self.applicability.unwrap_or_default();
+
+        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
+        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
+        s.serialize_field(
+            "applicability",
+            &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
+        )?;
+        s.end()
+    }
+}
+
+impl<'hir> LateLintPass<'hir> for MetadataCollector {
+    /// Collecting lint declarations like:
+    /// ```rust, ignore
+    /// declare_clippy_lint! {
+    ///     /// **What it does:** Something IDK.
+    ///     pub SOME_LINT,
+    ///     internal,
+    ///     "Who am I?"
+    /// }
+    /// ```
+    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
+        if_chain! {
+            // item validation
+            if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind;
+            if is_lint_ref_type(cx, ty);
+            // blacklist check
+            let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
+            if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
+            // metadata extraction
+            if let Some(group) = get_lint_group_or_lint(cx, &lint_name, item);
+            if let Some(docs) = extract_attr_docs_or_lint(cx, item);
+            then {
+                self.lints.push(LintMetadata::new(
+                    lint_name,
+                    SerializableSpan::from_item(cx, item),
+                    group,
+                    docs,
+                ));
+            }
+        }
+    }
+
+    /// Collecting constant applicability from the actual lint emissions
+    ///
+    /// Example:
+    /// ```rust, ignore
+    /// span_lint_and_sugg(
+    ///     cx,
+    ///     SOME_LINT,
+    ///     item.span,
+    ///     "Le lint message",
+    ///     "Here comes help:",
+    ///     "#![allow(clippy::all)]",
+    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
+    /// );
+    /// ```
+    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
+        if let Some(args) = match_lint_emission(cx, expr) {
+            let mut emission_info = extract_emission_info(cx, args);
+            if emission_info.is_empty() {
+                // See:
+                // - src/misc.rs:734:9
+                // - src/methods/mod.rs:3545:13
+                // - src/methods/mod.rs:3496:13
+                // We are basically unable to resolve the lint name it self.
+                return;
+            }
+
+            for (lint_name, applicability, is_multi_part) in emission_info.drain(..) {
+                let app_info = self.applicability_info.entry(lint_name).or_default();
+                app_info.applicability = applicability;
+                app_info.is_multi_part_suggestion = is_multi_part;
+            }
+        }
+    }
+}
+
+// ==================================================================
+// Lint definition extraction
+// ==================================================================
+fn sym_to_string(sym: Symbol) -> String {
+    sym.as_str().to_string()
+}
+
+fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
+    extract_attr_docs(cx, item).or_else(|| {
+        lint_collection_error_item(cx, item, "could not collect the lint documentation");
+        None
+    })
+}
+
+/// This function collects all documentation that has been added to an item using
+/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
+///
+/// ```ignore
+/// #[doc = r"Hello world!"]
+/// #[doc = r"=^.^="]
+/// struct SomeItem {}
+/// ```
+///
+/// Would result in `Hello world!\n=^.^=\n`
+fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
+    cx.tcx
+        .hir()
+        .attrs(item.hir_id())
+        .iter()
+        .filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string()))
+        .reduce(|mut acc, sym| {
+            acc.push_str(&sym);
+            acc.push('\n');
+            acc
+        })
+}
+
+fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Item<'_>) -> Option<String> {
+    let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy));
+    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
+        get_lint_group(cx, lint_lst[0])
+            .or_else(|| {
+                lint_collection_error_item(cx, item, "Unable to determine lint group");
+                None
+            })
+            .filter(|group| !EXCLUDED_LINT_GROUPS.contains(&group.as_str()))
+    } else {
+        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
+        None
+    }
+}
+
+fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
+    for (group_name, lints, _) in &cx.lint_store.get_lint_groups() {
+        if IGNORED_LINT_GROUPS.contains(group_name) {
+            continue;
+        }
+
+        if lints.iter().any(|x| *x == lint_id) {
+            return Some((*group_name).to_string());
+        }
+    }
+
+    None
+}
+
+// ==================================================================
+// Lint emission
+// ==================================================================
+fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
+    span_lint(
+        cx,
+        INTERNAL_METADATA_COLLECTOR,
+        item.ident.span,
+        &format!("metadata collection error for `{}`: {}", item.ident.name, message),
+    );
+}
+
+// ==================================================================
+// Applicability
+// ==================================================================
+/// This function checks if a given expression is equal to a simple lint emission function call.
+/// It will return the function arguments if the emission matched any function.
+fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
+    LINT_EMISSION_FUNCTIONS
+        .iter()
+        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
+}
+
+fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
+    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
+}
+
+fn extract_emission_info<'hir>(
+    cx: &LateContext<'hir>,
+    args: &'hir [hir::Expr<'hir>],
+) -> Vec<(String, Option<usize>, bool)> {
+    let mut lints = Vec::new();
+    let mut applicability = None;
+    let mut multi_part = false;
+
+    for arg in args {
+        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(&arg));
+
+        if match_type(cx, arg_ty, &paths::LINT) {
+            // If we found the lint arg, extract the lint name
+            let mut resolved_lints = resolve_lints(cx, arg);
+            lints.append(&mut resolved_lints);
+        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
+            applicability = resolve_applicability(cx, arg);
+        } else if arg_ty.is_closure() {
+            multi_part |= check_is_multi_part(cx, arg);
+            // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison
+            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
+        }
+    }
+
+    lints
+        .drain(..)
+        .map(|lint_name| (lint_name, applicability, multi_part))
+        .collect()
+}
+
+/// Resolves the possible lints that this expression could reference
+fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
+    let mut resolver = LintResolver::new(cx);
+    resolver.visit_expr(expr);
+    resolver.lints
+}
+
+/// This function tries to resolve the linked applicability to the given expression.
+fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
+    let mut resolver = ApplicabilityResolver::new(cx);
+    resolver.visit_expr(expr);
+    resolver.complete()
+}
+
+fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
+    if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind {
+        let mut scanner = IsMultiSpanScanner::new(cx);
+        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id));
+        return scanner.is_multi_part();
+    } else if let Some(local) = get_parent_local(cx, closure_expr) {
+        if let Some(local_init) = local.init {
+            return check_is_multi_part(cx, local_init);
+        }
+    }
+
+    false
+}
+
+struct LintResolver<'a, 'hir> {
+    cx: &'a LateContext<'hir>,
+    lints: Vec<String>,
+}
+
+impl<'a, 'hir> LintResolver<'a, 'hir> {
+    fn new(cx: &'a LateContext<'hir>) -> Self {
+        Self {
+            cx,
+            lints: Vec::<String>::default(),
+        }
+    }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
+    type Map = Map<'hir>;
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
+    }
+
+    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+        if_chain! {
+            if let ExprKind::Path(qpath) = &expr.kind;
+            if let QPath::Resolved(_, path) = qpath;
+
+            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr));
+            if match_type(self.cx, expr_ty, &paths::LINT);
+            then {
+                if let hir::def::Res::Def(DefKind::Static, _) = path.res {
+                    let lint_name = last_path_segment(qpath).ident.name;
+                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
+                } else if let Some(local) = get_parent_local(self.cx, expr) {
+                    if let Some(local_init) = local.init {
+                        intravisit::walk_expr(self, local_init);
+                    }
+                }
+            }
+        }
+
+        intravisit::walk_expr(self, expr);
+    }
+}
+
+/// This visitor finds the highest applicability value in the visited expressions
+struct ApplicabilityResolver<'a, 'hir> {
+    cx: &'a LateContext<'hir>,
+    /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
+    applicability_index: Option<usize>,
+}
+
+impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
+    fn new(cx: &'a LateContext<'hir>) -> Self {
+        Self {
+            cx,
+            applicability_index: None,
+        }
+    }
+
+    fn add_new_index(&mut self, new_index: usize) {
+        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
+    }
+
+    fn complete(self) -> Option<usize> {
+        self.applicability_index
+    }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
+    type Map = Map<'hir>;
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
+    }
+
+    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
+        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
+            if match_path(path, enum_value) {
+                self.add_new_index(index);
+                return;
+            }
+        }
+    }
+
+    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr));
+
+        if_chain! {
+            if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
+            if let Some(local) = get_parent_local(self.cx, expr);
+            if let Some(local_init) = local.init;
+            then {
+                intravisit::walk_expr(self, local_init);
+            }
+        };
+
+        // TODO xFrednet 2021-03-01: support function arguments?
+
+        intravisit::walk_expr(self, expr);
+    }
+}
+
+/// This returns the parent local node if the expression is a reference one
+fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
+    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
+        if let hir::def::Res::Local(local_hir) = path.res {
+            return get_parent_local_hir_id(cx, local_hir);
+        }
+    }
+
+    None
+}
+
+fn get_parent_local_hir_id(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
+    let map = cx.tcx.hir();
+
+    match map.find(map.get_parent_node(hir_id)) {
+        Some(hir::Node::Local(local)) => Some(local),
+        Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
+        _ => None,
+    }
+}
+
+/// This visitor finds the highest applicability value in the visited expressions
+struct IsMultiSpanScanner<'a, 'hir> {
+    cx: &'a LateContext<'hir>,
+    suggestion_count: usize,
+}
+
+impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
+    fn new(cx: &'a LateContext<'hir>) -> Self {
+        Self {
+            cx,
+            suggestion_count: 0,
+        }
+    }
+
+    /// Add a new single expression suggestion to the counter
+    fn add_single_span_suggestion(&mut self) {
+        self.suggestion_count += 1;
+    }
+
+    /// Signals that a suggestion with possible multiple spans was found
+    fn add_multi_part_suggestion(&mut self) {
+        self.suggestion_count += 2;
+    }
+
+    /// Checks if the suggestions include multiple spanns
+    fn is_multi_part(&self) -> bool {
+        self.suggestion_count > 1
+    }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
+    type Map = Map<'hir>;
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
+    }
+
+    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+        // Early return if the lint is already multi span
+        if self.is_multi_part() {
+            return;
+        }
+
+        match &expr.kind {
+            ExprKind::Call(fn_expr, _args) => {
+                let found_function = SUGGESTION_FUNCTIONS
+                    .iter()
+                    .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
+                if found_function {
+                    // These functions are all multi part suggestions
+                    self.add_single_span_suggestion()
+                }
+            },
+            ExprKind::MethodCall(path, _path_span, arg, _arg_span) => {
+                let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
+                if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
+                    let called_method = path.ident.name.as_str().to_string();
+                    for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
+                        if *method_name == called_method {
+                            if *is_multi_part {
+                                self.add_multi_part_suggestion();
+                            } else {
+                                self.add_single_span_suggestion();
+                            }
+                            break;
+                        }
+                    }
+                }
+            },
+            _ => {},
+        }
+
+        intravisit::walk_expr(self, expr);
+    }
+}
index d8b31344e6d8bd68b91284c3a47dabfad3837b95..b67448e3a57406be07c7589baec3e72f17a964b2 100644 (file)
@@ -1,5 +1,5 @@
 pub mod author;
 pub mod conf;
 pub mod inspector;
-#[cfg(feature = "internal-lints")]
+#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 pub mod internal_lints;
index d04c5f889dda0f24a6f9ff408cc02002b1a098a9..0a1d4e1114285b18640658a9eaa0c48bd359aa64 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.53"
+version = "0.1.54"
 authors = ["The Rust Clippy Developers"]
 edition = "2018"
 publish = false
@@ -15,6 +15,7 @@ rustc-semver="1.1.0"
 
 [features]
 internal-lints = []
+metadata-collector-lint = []
 
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
index 7f827f1759d2f9542a6fa53668b6aa7204f0507e..a4efae54894fb10463ba54f745c594830604b047 100644 (file)
@@ -1,4 +1,12 @@
 //! Clippy wrappers around rustc's diagnostic functions.
+//!
+//! These functions are used by the `INTERNAL_METADATA_COLLECTOR` lint to collect the corresponding
+//! lint applicability. Please make sure that you update the `LINT_EMISSION_FUNCTIONS` variable in
+//! `clippy_lints::utils::internal_lints::metadata_collector` when a new function is added
+//! or renamed.
+//!
+//! Thank you!
+//! ~The `INTERNAL_METADATA_COLLECTOR` lint
 
 use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir::HirId;
index 07ae6e924e28b7daee63fe73710532859c40b5e5..3b01158acd91f972a634b652afeebd6e28a3f7cb 100644 (file)
@@ -713,7 +713,7 @@ pub fn hash_expr(&mut self, e: &Expr<'_>) {
                 self.hash_expr(e);
 
                 for arm in arms {
-                    // TODO: arm.pat?
+                    self.hash_pat(arm.pat);
                     if let Some(ref e) = arm.guard {
                         self.hash_guard(e);
                     }
@@ -791,6 +791,72 @@ pub fn hash_qpath(&mut self, p: &QPath<'_>) {
         // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
     }
 
+    pub fn hash_pat(&mut self, pat: &Pat<'_>) {
+        std::mem::discriminant(&pat.kind).hash(&mut self.s);
+        match pat.kind {
+            PatKind::Binding(ann, _, _, pat) => {
+                ann.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
+                if let Some(pat) = pat {
+                    self.hash_pat(pat);
+                }
+            },
+            PatKind::Box(pat) => self.hash_pat(pat),
+            PatKind::Lit(expr) => self.hash_expr(expr),
+            PatKind::Or(pats) => {
+                for pat in pats {
+                    self.hash_pat(pat);
+                }
+            },
+            PatKind::Path(ref qpath) => self.hash_qpath(qpath),
+            PatKind::Range(s, e, i) => {
+                if let Some(s) = s {
+                    self.hash_expr(s);
+                }
+                if let Some(e) = e {
+                    self.hash_expr(e);
+                }
+                i.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
+            },
+            PatKind::Ref(pat, m) => {
+                self.hash_pat(pat);
+                m.hash(&mut self.s);
+            },
+            PatKind::Slice(l, m, r) => {
+                for pat in l {
+                    self.hash_pat(pat);
+                }
+                if let Some(pat) = m {
+                    self.hash_pat(pat);
+                }
+                for pat in r {
+                    self.hash_pat(pat);
+                }
+            },
+            PatKind::Struct(ref qpath, fields, e) => {
+                self.hash_qpath(qpath);
+                for f in fields {
+                    self.hash_name(f.ident.name);
+                    self.hash_pat(f.pat);
+                }
+                e.hash(&mut self.s)
+            },
+            PatKind::Tuple(pats, e) => {
+                for pat in pats {
+                    self.hash_pat(pat);
+                }
+                e.hash(&mut self.s);
+            },
+            PatKind::TupleStruct(ref qpath, pats, e) => {
+                self.hash_qpath(qpath);
+                for pat in pats {
+                    self.hash_pat(pat);
+                }
+                e.hash(&mut self.s);
+            },
+            PatKind::Wild => {},
+        }
+    }
+
     pub fn hash_path(&mut self, path: &Path<'_>) {
         match path.res {
             // constant hash since equality is dependant on inter-expression context
@@ -808,6 +874,7 @@ pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
 
         match &b.kind {
             StmtKind::Local(local) => {
+                self.hash_pat(local.pat);
                 if let Some(ref init) = local.init {
                     self.hash_expr(init);
                 }
@@ -827,7 +894,7 @@ pub fn hash_guard(&mut self, g: &Guard<'_>) {
         }
     }
 
-    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
+    pub fn hash_lifetime(&mut self, lifetime: Lifetime) {
         std::mem::discriminant(&lifetime.name).hash(&mut self.s);
         if let LifetimeName::Param(ref name) = lifetime.name {
             std::mem::discriminant(name).hash(&mut self.s);
@@ -844,12 +911,8 @@ pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
     }
 
     pub fn hash_ty(&mut self, ty: &Ty<'_>) {
-        self.hash_tykind(&ty.kind);
-    }
-
-    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
-        std::mem::discriminant(ty).hash(&mut self.s);
-        match ty {
+        std::mem::discriminant(&ty.kind).hash(&mut self.s);
+        match ty.kind {
             TyKind::Slice(ty) => {
                 self.hash_ty(ty);
             },
@@ -857,11 +920,11 @@ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
                 self.hash_ty(ty);
                 self.hash_body(anon_const.body);
             },
-            TyKind::Ptr(mut_ty) => {
+            TyKind::Ptr(ref mut_ty) => {
                 self.hash_ty(&mut_ty.ty);
                 mut_ty.mutbl.hash(&mut self.s);
             },
-            TyKind::Rptr(lifetime, mut_ty) => {
+            TyKind::Rptr(lifetime, ref mut_ty) => {
                 self.hash_lifetime(lifetime);
                 self.hash_ty(&mut_ty.ty);
                 mut_ty.mutbl.hash(&mut self.s);
@@ -883,11 +946,11 @@ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
                 bfn.decl.c_variadic.hash(&mut self.s);
             },
             TyKind::Tup(ty_list) => {
-                for ty in *ty_list {
+                for ty in ty_list {
                     self.hash_ty(ty);
                 }
             },
-            TyKind::Path(qpath) => match qpath {
+            TyKind::Path(ref qpath) => match qpath {
                 QPath::Resolved(ref maybe_ty, ref path) => {
                     if let Some(ref ty) = maybe_ty {
                         self.hash_ty(ty);
@@ -927,9 +990,9 @@ pub fn hash_body(&mut self, body_id: BodyId) {
 
     fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
         for arg in arg_list {
-            match arg {
-                GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
-                GenericArg::Type(ref ty) => self.hash_ty(&ty),
+            match *arg {
+                GenericArg::Lifetime(l) => self.hash_lifetime(l),
+                GenericArg::Type(ref ty) => self.hash_ty(ty),
                 GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
             }
         }
index e81a92eb74ca7c74afd2bb56aed669ec8a9cadb1..f5ee49c7d5f99f31f3cc4d912451fd99c8eabc0c 100644 (file)
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::LangItem::{ResultErr, ResultOk};
 use rustc_hir::{
     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,
+    ImplItem, ImplItemKind, IsAsync, 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;
@@ -821,7 +821,13 @@ pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
 
 /// Gets the parent expression, if any –- this is useful to constrain a lint.
 pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    match get_parent_node(cx.tcx, e.hir_id) {
+    get_parent_expr_for_hir(cx, e.hir_id)
+}
+
+/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
+/// constraint lints
+pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
+    match get_parent_node(cx.tcx, hir_id) {
         Some(Node::Expr(parent)) => Some(parent),
         _ => None,
     }
@@ -1301,6 +1307,40 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
     (conds, blocks)
 }
 
+/// Checks if the given function kind is an async function.
+pub fn is_async_fn(kind: FnKind<'_>) -> bool {
+    matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async)
+}
+
+/// Peels away all the compiler generated code surrounding the body of an async function,
+pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Call(
+        _,
+        &[Expr {
+            kind: ExprKind::Closure(_, _, body, _, _),
+            ..
+        }],
+    ) = body.value.kind
+    {
+        if let ExprKind::Block(
+            Block {
+                stmts: [],
+                expr:
+                    Some(Expr {
+                        kind: ExprKind::DropTemps(expr),
+                        ..
+                    }),
+                ..
+            },
+            _,
+        ) = tcx.hir().body(body).value.kind
+        {
+            return Some(expr);
+        }
+    };
+    None
+}
+
 // 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))
index 1fa439639b24aebab5a0a4467496538faf4500dc..8037d670500be736973135845d9592dc8424742f 100644 (file)
@@ -5,6 +5,17 @@
 //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 
 pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
+#[cfg(feature = "metadata-collector-lint")]
+pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
+#[cfg(feature = "metadata-collector-lint")]
+pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
+    ["rustc_lint_defs", "Applicability", "Unspecified"],
+    ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
+    ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
+    ["rustc_lint_defs", "Applicability", "MachineApplicable"],
+];
+#[cfg(feature = "metadata-collector-lint")]
+pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 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"];
@@ -72,7 +83,7 @@
 #[cfg(feature = "internal-lints")]
 pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
-#[cfg(feature = "internal-lints")]
+#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
 pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
index 53180d1f9f54f699014b50c35f0d1e1eff33b9cc..4d49b43bde9ecd9751bc825b55afad614e5f1fd7 100644 (file)
@@ -280,17 +280,17 @@ pub fn snippet_with_context(
     default: &'a str,
     applicability: &mut Applicability,
 ) -> (Cow<'a, str>, bool) {
-    let outer_span = hygiene::walk_chain(span, outer);
-    let (span, is_macro_call) = if outer_span.ctxt() == outer {
-        (outer_span, span.ctxt() != outer)
-    } else {
-        // The span is from a macro argument, and the outer context is the macro using the argument
-        if *applicability != Applicability::Unspecified {
-            *applicability = Applicability::MaybeIncorrect;
-        }
-        // TODO: get the argument span.
-        (span, false)
-    };
+    let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else(
+        || {
+            // The span is from a macro argument, and the outer context is the macro using the argument
+            if *applicability != Applicability::Unspecified {
+                *applicability = Applicability::MaybeIncorrect;
+            }
+            // TODO: get the argument span.
+            (span, false)
+        },
+        |outer_span| (outer_span, span.ctxt() != outer),
+    );
 
     (
         snippet_with_applicability(cx, span, default, applicability),
@@ -298,6 +298,37 @@ pub fn snippet_with_context(
     )
 }
 
+/// Walks the span up to the target context, thereby returning the macro call site if the span is
+/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
+/// case of the span being in a macro expansion, but the target context is from expanding a macro
+/// argument.
+///
+/// Given the following
+///
+/// ```rust,ignore
+/// macro_rules! m { ($e:expr) => { f($e) }; }
+/// g(m!(0))
+/// ```
+///
+/// If called with a span of the call to `f` and a context of the call to `g` this will return a
+/// span containing `m!(0)`. However, if called with a span of the literal `0` this will give a span
+/// containing `0` as the context is the same as the outer context.
+///
+/// This will traverse through multiple macro calls. Given the following:
+///
+/// ```rust,ignore
+/// macro_rules! m { ($e:expr) => { n!($e, 0) }; }
+/// macro_rules! n { ($e:expr, $f:expr) => { f($e, $f) }; }
+/// g(m!(0))
+/// ```
+///
+/// If called with a span of the call to `f` and a context of the call to `g` this will return a
+/// span containing `m!(0)`.
+pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
+    let outer_span = hygiene::walk_chain(span, outer);
+    (outer_span.ctxt() == outer).then(|| outer_span)
+}
+
 /// Removes block comments from the given `Vec` of lines.
 ///
 /// # Examples
index 5a8c629e3338c41683de26a237968a33bb1cda8b..d431bdf34eeeea2c62f0662f8ca562df16e35ac2 100644 (file)
@@ -1,7 +1,7 @@
 use crate::path_to_local_id;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Arm, Body, Expr, HirId, Stmt};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 
@@ -188,3 +188,54 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
         NestedVisitorMap::OnlyBodies(self.hir)
     }
 }
+
+pub trait Visitable<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V);
+}
+impl Visitable<'tcx> for &'tcx Expr<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_expr(self)
+    }
+}
+impl Visitable<'tcx> for &'tcx Block<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_block(self)
+    }
+}
+impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_stmt(self)
+    }
+}
+impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_body(self)
+    }
+}
+impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> {
+    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
+        v.visit_arm(self)
+    }
+}
+
+pub fn visit_break_exprs<'tcx>(
+    node: impl Visitable<'tcx>,
+    f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
+) {
+    struct V<F>(F);
+    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V<F> {
+        type Map = ErasedMap<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            if let ExprKind::Break(dest, sub_expr) = e.kind {
+                self.0(e, dest, sub_expr)
+            }
+            walk_expr(self, e);
+        }
+    }
+
+    node.visit(&mut V(f));
+}
index d6cc6d0c2c76394842dd80d085e811bf96b78725..5a06afedbf4c2e7b86afff20847a30456b7a6d41 100644 (file)
@@ -454,7 +454,7 @@ in `clippy_lints/src/utils/conf.rs`:
 ```rust
 define_Conf! {
     /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
-    (msrv, "msrv": Option<String>, None),
+    (msrv: Option<String> = None),
     ...
 }
 ```
@@ -562,7 +562,7 @@ in the following steps:
     like this:
     ```rust
     /// Lint: LINT_NAME. <The configuration field doc comment>
-    (configuration_ident, "configuration_value": Type, DefaultValue),
+    (configuration_ident: Type = DefaultValue),
     ```
     The configuration value and identifier should usually be the same. The doc comment will be
     automatically added to the lint documentation.
index cd398451783d6bc53b03596e5b7a55956c9a19d3..593162f09a788ca221c0abda08d1fa589ff44a88 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-04-22"
+channel = "nightly-2021-05-06"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index 750a23e8c9841f4dcf29670cecb2b8f6e047b182..0c82f37d6a22e32572c15956bdf6ecd7e0c32d80 100644 (file)
@@ -106,7 +106,7 @@ fn config(&mut self, config: &mut interface::Config) {
                 (previous)(sess, lint_store);
             }
 
-            let conf = clippy_lints::read_conf(&[], sess);
+            let conf = clippy_lints::read_conf(sess);
             clippy_lints::register_plugins(lint_store, sess, &conf);
             clippy_lints::register_pre_expansion_lints(lint_store);
             clippy_lints::register_renamed(lint_store);
index d92530f073f56285738fcc93d9c4eaef779691d0..6524fd4706ce13f37694cff7407c8515cfeec45c 100644 (file)
@@ -1,3 +1,8 @@
+//! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and
+//! long error messages
+//!
+//! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
+
 // Dogfood cannot run on Windows
 #![cfg(not(windows))]
 #![feature(once_cell)]
@@ -17,12 +22,14 @@ fn dogfood_clippy() {
         return;
     }
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+    let enable_metadata_collection = std::env::var("ENABLE_METADATA_COLLECTION").unwrap_or_else(|_| "0".to_string());
 
     let mut command = Command::new(&*CLIPPY_PATH);
     command
         .current_dir(root_dir)
         .env("CLIPPY_DOGFOOD", "1")
         .env("CARGO_INCREMENTAL", "0")
+        .env("ENABLE_METADATA_COLLECTION", &enable_metadata_collection)
         .arg("clippy")
         .arg("--all-targets")
         .arg("--all-features")
index efd02bcbb6e28968bdb1e227e74a4190289c1110..c7bc261de6c5a3f8c10af3ffc10ae0a47df5d579 100644 (file)
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `blacklisted-names`
 
 error: aborting due to previous error
 
index 34267c0daf7c2cfd50fc647b6cd3f2b891e32856..90021a034a3d3b358ae1eb472f733904ee8649d1 100644 (file)
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
 
 error: aborting due to previous error
 
diff --git a/src/tools/clippy/tests/ui/builtin-type-shadow.rs b/src/tools/clippy/tests/ui/builtin-type-shadow.rs
deleted file mode 100644 (file)
index 69b8b6a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#![warn(clippy::builtin_type_shadow)]
-#![allow(non_camel_case_types)]
-
-fn foo<u32>(a: u32) -> u32 {
-    42
-    // ^ rustc's type error
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/builtin-type-shadow.stderr b/src/tools/clippy/tests/ui/builtin-type-shadow.stderr
deleted file mode 100644 (file)
index f42b246..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-error: this generic shadows the built-in type `u32`
-  --> $DIR/builtin-type-shadow.rs:4:8
-   |
-LL | fn foo<u32>(a: u32) -> u32 {
-   |        ^^^
-   |
-   = note: `-D clippy::builtin-type-shadow` implied by `-D warnings`
-
-error[E0308]: mismatched types
-  --> $DIR/builtin-type-shadow.rs:5:5
-   |
-LL | fn foo<u32>(a: u32) -> u32 {
-   |        ---             --- expected `u32` because of return type
-   |        |
-   |        this type parameter
-LL |     42
-   |     ^^ expected type parameter `u32`, found integer
-   |
-   = note: expected type parameter `u32`
-                        found type `{integer}`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/tools/clippy/tests/ui/builtin_type_shadow.rs b/src/tools/clippy/tests/ui/builtin_type_shadow.rs
new file mode 100644 (file)
index 0000000..69b8b6a
--- /dev/null
@@ -0,0 +1,9 @@
+#![warn(clippy::builtin_type_shadow)]
+#![allow(non_camel_case_types)]
+
+fn foo<u32>(a: u32) -> u32 {
+    42
+    // ^ rustc's type error
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/builtin_type_shadow.stderr b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr
new file mode 100644 (file)
index 0000000..47a8a1e
--- /dev/null
@@ -0,0 +1,24 @@
+error: this generic shadows the built-in type `u32`
+  --> $DIR/builtin_type_shadow.rs:4:8
+   |
+LL | fn foo<u32>(a: u32) -> u32 {
+   |        ^^^
+   |
+   = note: `-D clippy::builtin-type-shadow` implied by `-D warnings`
+
+error[E0308]: mismatched types
+  --> $DIR/builtin_type_shadow.rs:5:5
+   |
+LL | fn foo<u32>(a: u32) -> u32 {
+   |        ---             --- expected `u32` because of return type
+   |        |
+   |        this type parameter
+LL |     42
+   |     ^^ expected type parameter `u32`, found integer
+   |
+   = note: expected type parameter `u32`
+                        found type `{integer}`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
index 3b03f8c7dfe7c71f6642646d2543a719ce3cbb4f..c12c6a310275db9c77d39849235c02058c5ee4bb 100644 (file)
@@ -203,4 +203,32 @@ fn same_operation_not_equals() {
     }
 }
 
+enum Sign {
+    Negative,
+    Positive,
+    Zero,
+}
+
+impl Sign {
+    const fn sign_i8(n: i8) -> Self {
+        if n == 0 {
+            Sign::Zero
+        } else if n > 0 {
+            Sign::Positive
+        } else {
+            Sign::Negative
+        }
+    }
+}
+
+const fn sign_i8(n: i8) -> Sign {
+    if n == 0 {
+        Sign::Zero
+    } else if n > 0 {
+        Sign::Positive
+    } else {
+        Sign::Negative
+    }
+}
+
 fn main() {}
index e3d5928be231b3d18ef3e0509c779b8ee948db74..ae67ebded437496e0d3e40c23c21a490e2b2901a 100644 (file)
@@ -16,8 +16,6 @@ fn next(&mut self) -> Option<u8> {
 
 fn main() {
     let my_iterator = Countdown(5);
-    let a: Vec<_> = my_iterator.take(1).collect();
-    assert_eq!(a.len(), 1);
-    let b: Vec<_> = my_iterator.collect();
-    assert_eq!(b.len(), 5);
+    assert_eq!(my_iterator.take(1).count(), 1);
+    assert_eq!(my_iterator.count(), 5);
 }
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7169.rs b/src/tools/clippy/tests/ui/crashes/ice-7169.rs
new file mode 100644 (file)
index 0000000..82095fe
--- /dev/null
@@ -0,0 +1,9 @@
+#[derive(Default)]
+struct A<T> {
+    a: Vec<A<T>>,
+    b: T,
+}
+
+fn main() {
+    if let Ok(_) = Ok::<_, ()>(A::<String>::default()) {}
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7169.stderr b/src/tools/clippy/tests/ui/crashes/ice-7169.stderr
new file mode 100644 (file)
index 0000000..5a9cd32
--- /dev/null
@@ -0,0 +1,10 @@
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/ice-7169.rs:8:12
+   |
+LL |     if let Ok(_) = Ok::<_, ()>(A::<String>::default()) {}
+   |     -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::<String>::default()).is_ok()`
+   |
+   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: aborting due to previous error
+
index 3e125c1dab568a7f137b12a7521cb41cc27b9ce0..e5de839dbc508d3754462ac913cc8ad2b2eb661a 100644 (file)
@@ -84,11 +84,5 @@ error: lint `clippy::filter_map` has been removed: this lint has been replaced b
 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 15 previous errors
+error: aborting due to 14 previous errors
 
index b8550078c4600b7b3595f1413227a463718bf7eb..8043ab0058a5e283a07b86659a74a9063066b86c 100644 (file)
@@ -18,11 +18,5 @@ error: lint `misaligned_transmute` has been removed: this lint has been split in
 LL | #[warn(misaligned_transmute)]
    |        ^^^^^^^^^^^^^^^^^^^^
 
-error: lint `unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
-  --> $DIR/deprecated_old.rs:1:8
-   |
-LL | #[warn(unstable_as_slice)]
-   |        ^^^^^^^^^^^^^^^^^
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
index d806bc6d40102e6f1efef7166d9f2ad22be0fc15..d742856bc4163af0999ad145af09e9a6d6dc942d 100644 (file)
@@ -1,3 +1,5 @@
+// edition:2018
+
 #[warn(clippy::eval_order_dependence)]
 #[allow(
     unused_assignments,
@@ -107,3 +109,7 @@ struct Foo {
         },
     );
 }
+
+async fn issue_6925() {
+    let _ = vec![async { true }.await, async { false }.await];
+}
index 8f4fa2228f7f4d7f7eff5594a51a23ef28116900..35eb85e95a320bf72497178054bf1a1a312bc20c 100644 (file)
@@ -1,48 +1,48 @@
-error: unsequenced read of a variable
-  --> $DIR/eval_order_dependence.rs:15:9
+error: unsequenced read of `x`
+  --> $DIR/eval_order_dependence.rs:17:9
    |
 LL |     } + x;
    |         ^
    |
    = note: `-D clippy::eval-order-dependence` implied by `-D warnings`
 note: whether read occurs before this write depends on evaluation order
-  --> $DIR/eval_order_dependence.rs:13:9
+  --> $DIR/eval_order_dependence.rs:15:9
    |
 LL |         x = 1;
    |         ^^^^^
 
-error: unsequenced read of a variable
-  --> $DIR/eval_order_dependence.rs:18:5
+error: unsequenced read of `x`
+  --> $DIR/eval_order_dependence.rs:20:5
    |
 LL |     x += {
    |     ^
    |
 note: whether read occurs before this write depends on evaluation order
-  --> $DIR/eval_order_dependence.rs:19:9
+  --> $DIR/eval_order_dependence.rs:21:9
    |
 LL |         x = 20;
    |         ^^^^^^
 
-error: unsequenced read of a variable
-  --> $DIR/eval_order_dependence.rs:31:12
+error: unsequenced read of `x`
+  --> $DIR/eval_order_dependence.rs:33:12
    |
 LL |         a: x,
    |            ^
    |
 note: whether read occurs before this write depends on evaluation order
-  --> $DIR/eval_order_dependence.rs:33:13
+  --> $DIR/eval_order_dependence.rs:35:13
    |
 LL |             x = 6;
    |             ^^^^^
 
-error: unsequenced read of a variable
-  --> $DIR/eval_order_dependence.rs:40:9
+error: unsequenced read of `x`
+  --> $DIR/eval_order_dependence.rs:42:9
    |
 LL |         x += {
    |         ^
    |
 note: whether read occurs before this write depends on evaluation order
-  --> $DIR/eval_order_dependence.rs:41:13
+  --> $DIR/eval_order_dependence.rs:43:13
    |
 LL |             x = 20;
    |             ^^^^^^
index 59f7ad9c1062490f66039c203abba078c53fbbbe..7698b88a88c8aa80a143ac27ab11e145a880ef51 100644 (file)
@@ -1,7 +1,8 @@
+// edition:2018
 // run-rustfix
 
 #![warn(clippy::implicit_return)]
-#![allow(clippy::needless_return, unused)]
+#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
 
 fn test_end_of_fn() -> bool {
     if true {
@@ -12,7 +13,6 @@ fn test_end_of_fn() -> bool {
     return true
 }
 
-#[allow(clippy::needless_bool)]
 fn test_if_block() -> bool {
     if true { return true } else { return false }
 }
@@ -25,7 +25,6 @@ fn test_match(x: bool) -> bool {
     }
 }
 
-#[allow(clippy::needless_return)]
 fn test_match_with_unreachable(x: bool) -> bool {
     match x {
         true => return false,
@@ -33,14 +32,12 @@ fn test_match_with_unreachable(x: bool) -> bool {
     }
 }
 
-#[allow(clippy::never_loop)]
 fn test_loop() -> bool {
     loop {
         return true;
     }
 }
 
-#[allow(clippy::never_loop)]
 fn test_loop_with_block() -> bool {
     loop {
         {
@@ -49,7 +46,6 @@ fn test_loop_with_block() -> bool {
     }
 }
 
-#[allow(clippy::never_loop)]
 fn test_loop_with_nests() -> bool {
     loop {
         if true {
@@ -83,15 +79,53 @@ fn test_return_macro() -> String {
     return format!("test {}", "test")
 }
 
-fn main() {
-    let _ = test_end_of_fn();
-    let _ = test_if_block();
-    let _ = test_match(true);
-    let _ = test_match_with_unreachable(true);
-    let _ = test_loop();
-    let _ = test_loop_with_block();
-    let _ = test_loop_with_nests();
-    let _ = test_loop_with_if_let();
-    test_closure();
-    let _ = test_return_macro();
+fn macro_branch_test() -> bool {
+    macro_rules! m {
+        ($t:expr, $f:expr) => {
+            if true { $t } else { $f }
+        };
+    }
+    return m!(true, false)
+}
+
+fn loop_test() -> bool {
+    'outer: loop {
+        if true {
+            return true;
+        }
+
+        let _ = loop {
+            if false {
+                return false;
+            }
+            if true {
+                break true;
+            }
+        };
+    }
 }
+
+fn loop_macro_test() -> bool {
+    macro_rules! m {
+        ($e:expr) => {
+            break $e
+        };
+    }
+    return loop {
+        m!(true);
+    }
+}
+
+fn divergent_test() -> bool {
+    fn diverge() -> ! {
+        panic!()
+    }
+    diverge()
+}
+
+// issue #6940
+async fn foo() -> bool {
+    return true
+}
+
+fn main() {}
index 2c1bc046515089a7ea73f93969df248168a7164b..45bbc2ec670e05463d1a0f8d6f8d5fc557561cfe 100644 (file)
@@ -1,7 +1,8 @@
+// edition:2018
 // run-rustfix
 
 #![warn(clippy::implicit_return)]
-#![allow(clippy::needless_return, unused)]
+#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
 
 fn test_end_of_fn() -> bool {
     if true {
@@ -12,7 +13,6 @@ fn test_end_of_fn() -> bool {
     true
 }
 
-#[allow(clippy::needless_bool)]
 fn test_if_block() -> bool {
     if true { true } else { false }
 }
@@ -25,7 +25,6 @@ fn test_match(x: bool) -> bool {
     }
 }
 
-#[allow(clippy::needless_return)]
 fn test_match_with_unreachable(x: bool) -> bool {
     match x {
         true => return false,
@@ -33,14 +32,12 @@ fn test_match_with_unreachable(x: bool) -> bool {
     }
 }
 
-#[allow(clippy::never_loop)]
 fn test_loop() -> bool {
     loop {
         break true;
     }
 }
 
-#[allow(clippy::never_loop)]
 fn test_loop_with_block() -> bool {
     loop {
         {
@@ -49,7 +46,6 @@ fn test_loop_with_block() -> bool {
     }
 }
 
-#[allow(clippy::never_loop)]
 fn test_loop_with_nests() -> bool {
     loop {
         if true {
@@ -83,15 +79,53 @@ fn test_return_macro() -> String {
     format!("test {}", "test")
 }
 
-fn main() {
-    let _ = test_end_of_fn();
-    let _ = test_if_block();
-    let _ = test_match(true);
-    let _ = test_match_with_unreachable(true);
-    let _ = test_loop();
-    let _ = test_loop_with_block();
-    let _ = test_loop_with_nests();
-    let _ = test_loop_with_if_let();
-    test_closure();
-    let _ = test_return_macro();
+fn macro_branch_test() -> bool {
+    macro_rules! m {
+        ($t:expr, $f:expr) => {
+            if true { $t } else { $f }
+        };
+    }
+    m!(true, false)
+}
+
+fn loop_test() -> bool {
+    'outer: loop {
+        if true {
+            break true;
+        }
+
+        let _ = loop {
+            if false {
+                break 'outer false;
+            }
+            if true {
+                break true;
+            }
+        };
+    }
+}
+
+fn loop_macro_test() -> bool {
+    macro_rules! m {
+        ($e:expr) => {
+            break $e
+        };
+    }
+    loop {
+        m!(true);
+    }
+}
+
+fn divergent_test() -> bool {
+    fn diverge() -> ! {
+        panic!()
+    }
+    diverge()
 }
+
+// issue #6940
+async fn foo() -> bool {
+    true
+}
+
+fn main() {}
index 3608319e5bd2cc5e770cac96b0fcad9c839f4c6c..16fe9ed444ff696d20c0c3411cb56913a631a6d2 100644 (file)
@@ -1,5 +1,5 @@
 error: missing `return` statement
-  --> $DIR/implicit_return.rs:12:5
+  --> $DIR/implicit_return.rs:13:5
    |
 LL |     true
    |     ^^^^ help: add `return` as shown: `return true`
@@ -31,40 +31,79 @@ LL |         false => { true },
    |                    ^^^^ help: add `return` as shown: `return true`
 
 error: missing `return` statement
-  --> $DIR/implicit_return.rs:39:9
+  --> $DIR/implicit_return.rs:37:9
    |
 LL |         break true;
    |         ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
 
 error: missing `return` statement
-  --> $DIR/implicit_return.rs:47:13
+  --> $DIR/implicit_return.rs:44:13
    |
 LL |             break true;
    |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
 
 error: missing `return` statement
-  --> $DIR/implicit_return.rs:56:13
+  --> $DIR/implicit_return.rs:52:13
    |
 LL |             break true;
    |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
 
 error: missing `return` statement
-  --> $DIR/implicit_return.rs:74:18
+  --> $DIR/implicit_return.rs:70:18
    |
 LL |     let _ = || { true };
    |                  ^^^^ help: add `return` as shown: `return true`
 
 error: missing `return` statement
-  --> $DIR/implicit_return.rs:75:16
+  --> $DIR/implicit_return.rs:71:16
    |
 LL |     let _ = || true;
    |                ^^^^ help: add `return` as shown: `return true`
 
 error: missing `return` statement
-  --> $DIR/implicit_return.rs:83:5
+  --> $DIR/implicit_return.rs:79:5
    |
 LL |     format!("test {}", "test")
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")`
 
-error: aborting due to 11 previous errors
+error: missing `return` statement
+  --> $DIR/implicit_return.rs:88:5
+   |
+LL |     m!(true, false)
+   |     ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)`
+
+error: missing `return` statement
+  --> $DIR/implicit_return.rs:94:13
+   |
+LL |             break true;
+   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+
+error: missing `return` statement
+  --> $DIR/implicit_return.rs:99:17
+   |
+LL |                 break 'outer false;
+   |                 ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false`
+
+error: missing `return` statement
+  --> $DIR/implicit_return.rs:114:5
+   |
+LL | /     loop {
+LL | |         m!(true);
+LL | |     }
+   | |_____^
+   |
+help: add `return` as shown
+   |
+LL |     return loop {
+LL |         m!(true);
+LL |     }
+   |
+
+error: missing `return` statement
+  --> $DIR/implicit_return.rs:128:5
+   |
+LL |     true
+   |     ^^^^ help: add `return` as shown: `return true`
+
+error: aborting due to 16 previous errors
 
index 72591f12baf852811efbc511a1ee7cf965929fe7..3d8fb8507e515c470a8ff1d2a59d5002b7ef3fd1 100644 (file)
@@ -192,11 +192,23 @@ fn while_loop_with_break_and_return() {
     }
 }
 
+fn immutable_condition_false_positive(mut n: u64) -> u32 {
+    let mut count = 0;
+    while {
+        n >>= 1;
+        n != 0
+    } {
+        count += 1;
+    }
+    count
+}
+
 fn main() {
     immutable_condition();
     unused_var();
     used_immutable();
     internally_mutable();
+    immutable_condition_false_positive(5);
 
     let mut c = Counter { count: 0 };
     c.inc_n(5);
index 0918a6868ab4b08c4958f4975209012c24faa09f..2458bf1e490bbf9c672de2987677c9157ed9b0a5 100644 (file)
@@ -1,4 +1,4 @@
-use std::collections::{HashMap, VecDeque};
+use std::collections::{BinaryHeap, HashMap, LinkedList, VecDeque};
 
 fn main() {
     let sample = [1; 5];
@@ -43,3 +43,35 @@ fn main() {
             .collect::<Vec<_>>();
     }
 }
+
+mod issue7110 {
+    // #7110 - lint for type annotation cases
+    use super::*;
+
+    fn lint_vec(string: &str) -> usize {
+        let buffer: Vec<&str> = string.split('/').collect();
+        buffer.len()
+    }
+    fn lint_vec_deque() -> usize {
+        let sample = [1; 5];
+        let indirect_len: VecDeque<_> = sample.iter().collect();
+        indirect_len.len()
+    }
+    fn lint_linked_list() -> usize {
+        let sample = [1; 5];
+        let indirect_len: LinkedList<_> = sample.iter().collect();
+        indirect_len.len()
+    }
+    fn lint_binary_heap() -> usize {
+        let sample = [1; 5];
+        let indirect_len: BinaryHeap<_> = sample.iter().collect();
+        indirect_len.len()
+    }
+    fn dont_lint(string: &str) -> usize {
+        let buffer: Vec<&str> = string.split('/').collect();
+        for buff in &buffer {
+            println!("{}", buff);
+        }
+        buffer.len()
+    }
+}
index c773b841f3b4b711c5c1f1321edd13452ecfe733..f094e182a48f31eb1cb9e945581f7cf32613829d 100644 (file)
@@ -69,5 +69,61 @@ LL |
 LL |     sample.into_iter().any(|x| x == a);
    |
 
-error: aborting due to 5 previous errors
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:52:51
+   |
+LL |         let buffer: Vec<&str> = string.split('/').collect();
+   |                                                   ^^^^^^^
+LL |         buffer.len()
+   |         ------------ the iterator could be used here instead
+   |
+help: take the original Iterator's count instead of collecting it and finding the length
+   |
+LL |         
+LL |         string.split('/').count()
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:57:55
+   |
+LL |         let indirect_len: VecDeque<_> = sample.iter().collect();
+   |                                                       ^^^^^^^
+LL |         indirect_len.len()
+   |         ------------------ the iterator could be used here instead
+   |
+help: take the original Iterator's count instead of collecting it and finding the length
+   |
+LL |         
+LL |         sample.iter().count()
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:62:57
+   |
+LL |         let indirect_len: LinkedList<_> = sample.iter().collect();
+   |                                                         ^^^^^^^
+LL |         indirect_len.len()
+   |         ------------------ the iterator could be used here instead
+   |
+help: take the original Iterator's count instead of collecting it and finding the length
+   |
+LL |         
+LL |         sample.iter().count()
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:67:57
+   |
+LL |         let indirect_len: BinaryHeap<_> = sample.iter().collect();
+   |                                                         ^^^^^^^
+LL |         indirect_len.len()
+   |         ------------------ the iterator could be used here instead
+   |
+help: take the original Iterator's count instead of collecting it and finding the length
+   |
+LL |         
+LL |         sample.iter().count()
+   |
+
+error: aborting due to 9 previous errors
 
index 64659b63f469934e3f0aecc5e8359fbaeb557b78..58094646b505cad518d16b3a65665336e47b9856 100644 (file)
@@ -1,4 +1,3 @@
-#![feature(const_fn)]
 #![allow(dead_code, clippy::missing_safety_doc)]
 #![warn(clippy::new_without_default)]
 
index 973836f75a905d02165931646bef411e422ff52b..56c5fe1c6189acaa638acfdf3fe88c97e5be2829 100644 (file)
@@ -1,5 +1,5 @@
 error: you should consider adding a `Default` implementation for `Foo`
-  --> $DIR/new_without_default.rs:8:5
+  --> $DIR/new_without_default.rs:7:5
    |
 LL | /     pub fn new() -> Foo {
 LL | |         Foo
@@ -17,7 +17,7 @@ LL | }
    |
 
 error: you should consider adding a `Default` implementation for `Bar`
-  --> $DIR/new_without_default.rs:16:5
+  --> $DIR/new_without_default.rs:15:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         Bar
@@ -34,7 +34,7 @@ LL | }
    |
 
 error: you should consider adding a `Default` implementation for `LtKo<'c>`
-  --> $DIR/new_without_default.rs:80:5
+  --> $DIR/new_without_default.rs:79:5
    |
 LL | /     pub fn new() -> LtKo<'c> {
 LL | |         unimplemented!()
@@ -51,7 +51,7 @@ LL | }
    |
 
 error: you should consider adding a `Default` implementation for `NewNotEqualToDerive`
-  --> $DIR/new_without_default.rs:157:5
+  --> $DIR/new_without_default.rs:156:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         NewNotEqualToDerive { foo: 1 }
@@ -68,7 +68,7 @@ LL | }
    |
 
 error: you should consider adding a `Default` implementation for `FooGenerics<T>`
-  --> $DIR/new_without_default.rs:165:5
+  --> $DIR/new_without_default.rs:164:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         Self(Default::default())
@@ -85,7 +85,7 @@ LL | }
    |
 
 error: you should consider adding a `Default` implementation for `BarGenerics<T>`
-  --> $DIR/new_without_default.rs:172:5
+  --> $DIR/new_without_default.rs:171:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         Self(Default::default())
index a9e803946041e9e8cd269e0fc147f16716c52263..c5d633ff86bfcc77357493953f8e3f82d867d316 100644 (file)
@@ -24,11 +24,5 @@ error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redunda
 LL | #[warn(clippy::const_static_lifetime)]
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
-error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> $DIR/rename.rs:10:9
-   |
-LL | #![warn(clippy::cyclomatic_complexity)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
-
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
 
index d8b5f19e144dcfd0567610bc8ef6302109125eb3..fcbe9af9f5616c177b860960e5276fd2e735b86e 100644 (file)
@@ -33,6 +33,8 @@ fn main() {
     x.rmatch_indices('x');
     x.trim_start_matches('x');
     x.trim_end_matches('x');
+    x.strip_prefix('x');
+    x.strip_suffix('x');
     // Make sure we escape characters correctly.
     x.split('\n');
     x.split('\'');
index a7bc73e3756dfb13e30925159b237321d296a62c..b8bc20f4070fc4a91f797a8d3d6dc51c85223f6b 100644 (file)
@@ -33,6 +33,8 @@ fn main() {
     x.rmatch_indices("x");
     x.trim_start_matches("x");
     x.trim_end_matches("x");
+    x.strip_prefix("x");
+    x.strip_suffix("x");
     // Make sure we escape characters correctly.
     x.split("\n");
     x.split("'");
index ee4e7e50efd17371a24a1eb904a8db47e4b12a27..6d94d8a34e39095dfe8c4086e5c8e05929d7e0ac 100644 (file)
@@ -121,64 +121,76 @@ LL |     x.trim_end_matches("x");
    |                        ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:37:13
+  --> $DIR/single_char_pattern.rs:36:20
+   |
+LL |     x.strip_prefix("x");
+   |                    ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:37:20
+   |
+LL |     x.strip_suffix("x");
+   |                    ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:39:13
    |
 LL |     x.split("/n");
    |             ^^^^ help: try using a `char` instead: `'/n'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:38:13
+  --> $DIR/single_char_pattern.rs:40:13
    |
 LL |     x.split("'");
    |             ^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:39:13
+  --> $DIR/single_char_pattern.rs:41:13
    |
 LL |     x.split("/'");
    |             ^^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:44:31
+  --> $DIR/single_char_pattern.rs:46:31
    |
 LL |     x.replace(";", ",").split(","); // issue #2978
    |                               ^^^ help: try using a `char` instead: `','`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:45:19
+  --> $DIR/single_char_pattern.rs:47:19
    |
 LL |     x.starts_with("/x03"); // issue #2996
    |                   ^^^^^^ help: try using a `char` instead: `'/x03'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:52:13
+  --> $DIR/single_char_pattern.rs:54:13
    |
 LL |     x.split(r"a");
    |             ^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:53:13
+  --> $DIR/single_char_pattern.rs:55:13
    |
 LL |     x.split(r#"a"#);
    |             ^^^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:54:13
+  --> $DIR/single_char_pattern.rs:56:13
    |
 LL |     x.split(r###"a"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:55:13
+  --> $DIR/single_char_pattern.rs:57:13
    |
 LL |     x.split(r###"'"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:56:13
+  --> $DIR/single_char_pattern.rs:58:13
    |
 LL |     x.split(r###"#"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'#'`
 
-error: aborting due to 30 previous errors
+error: aborting due to 32 previous errors
 
index 94a667e5898918d9362f7aa241ceb7f6fafd0471..421bf5ffa9a70c1ff6238113adb7adab2995b0d2 100644 (file)
@@ -48,11 +48,5 @@ error: unknown lint: `clippy::const_static_lifetim`
 LL | #[warn(clippy::const_static_lifetim)]
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes`
 
-error: unknown lint: `clippy::All`
-  --> $DIR/unknown_clippy_lints.rs:5:10
-   |
-LL | #![allow(clippy::All)]
-   |          ^^^^^^^^^^^ help: did you mean: `clippy::all`
-
-error: aborting due to 9 previous errors
+error: aborting due to 8 previous errors
 
index af858e4abcf5595f350a87d183bfbee5c4b6ae7d..c58181f518d58e10fdc42e9267a14012c549a858 100644 (file)
@@ -15,3 +15,7 @@ fn main() {
 
     let _ = (0..4).filter_map(i32::checked_abs);
 }
+
+fn filter_map_none_changes_item_type() -> impl Iterator<Item = bool> {
+    "".chars().filter_map(|_| None)
+}
index a192ebde3ebf4e73047dde8152c6dee234cf6bfc..7bb43cf7ae82ded49dc035f3e03f798993547cc6 100644 (file)
@@ -80,3 +80,10 @@ fn test2(){}
 
 #[rustfmt::skip]
 fn test3(){}
+
+fn macro_expr() {
+    macro_rules! e {
+        () => (());
+    }
+    e!()
+}
index 96041a7dd850e3807504697a84b87026447a5955..21073fb802adab910d8832a307472ef04cafd341 100644 (file)
@@ -80,3 +80,10 @@ fn test2() ->(){}
 
 #[rustfmt::skip]
 fn test3()-> (){}
+
+fn macro_expr() {
+    macro_rules! e {
+        () => (());
+    }
+    e!()
+}
index d0d9beb9b2d9fa686d907f650dfb55343d62feb0..5707cf0ce0f8c08177f68866b7065c6c9feb2182 100644 (file)
@@ -14,7 +14,7 @@ lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''')
 group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''')
 conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE)
 confvar_re = re.compile(
-    r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE)
+    r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\(([^:]+):\s*([^\s=]+)\s*=\s*([^\.\)]+).*\),''', re.MULTILINE)
 comment_re = re.compile(r'''\s*/// ?(.*)''')
 
 lint_levels = {
index b7693a3cb1431b9275079e51c9d66e36800e84c4..408c0b8da0b210375a8bf3855896a1b31d740d5b 100644 (file)
@@ -171,6 +171,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum PanicStrategy {
+    Unwind,
+    Abort,
+}
+
 /// Configuration for compiletest
 #[derive(Debug, Clone)]
 pub struct Config {
@@ -249,6 +255,9 @@ pub struct Config {
     /// Force the pass mode of a check/build/run-pass test to this mode.
     pub force_pass_mode: Option<PassMode>,
 
+    /// Explicitly enable or disable running.
+    pub run: Option<bool>,
+
     /// Write out a parseable log of tests that were run
     pub logfile: Option<PathBuf>,
 
@@ -262,6 +271,10 @@ pub struct Config {
     /// Flags to pass to the compiler when building for the target
     pub target_rustcflags: Option<String>,
 
+    /// What panic strategy the target is built with.  Unwind supports Abort, but
+    /// not vice versa.
+    pub target_panic: PanicStrategy,
+
     /// Target system to be tested
     pub target: String,
 
@@ -348,6 +361,15 @@ pub struct Config {
     pub npm: Option<String>,
 }
 
+impl Config {
+    pub fn run_enabled(&self) -> bool {
+        self.run.unwrap_or_else(|| {
+            // Auto-detect whether to run based on the platform.
+            !self.target.ends_with("-fuchsia")
+        })
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct TestPaths {
     pub file: PathBuf,         // e.g., compile-test/foo/bar/baz.rs
index f31a24738df6c7bae1312e96f037b3bdab330df9..983934d129a2e5291c6cb228f6a8dae64dc0ac1c 100644 (file)
@@ -7,7 +7,7 @@
 
 use tracing::*;
 
-use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PassMode};
+use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode};
 use crate::util;
 use crate::{extract_cdb_version, extract_gdb_version};
 
@@ -85,6 +85,10 @@ pub fn from_reader<R: Read>(config: &Config, testfile: &Path, rdr: R) -> Self {
                     props.ignore = true;
                 }
 
+                if !config.run_enabled() && config.parse_name_directive(ln, "needs-run-enabled") {
+                    props.ignore = true;
+                }
+
                 if !rustc_has_sanitizer_support
                     && config.parse_name_directive(ln, "needs-sanitizer-support")
                 {
@@ -111,6 +115,12 @@ pub fn from_reader<R: Read>(config: &Config, testfile: &Path, rdr: R) -> Self {
                     props.ignore = true;
                 }
 
+                if config.target_panic == PanicStrategy::Abort
+                    && config.parse_name_directive(ln, "needs-unwind")
+                {
+                    props.ignore = true;
+                }
+
                 if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) {
                     props.ignore = true;
                 }
index d1798a52df7c4b021b3abf456155f1c792a2b8cd..d53e19f2908e4ad85681ca6cc34b47e0bd329f1a 100644 (file)
@@ -5,7 +5,9 @@
 
 extern crate test;
 
-use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
+use crate::common::{
+    expected_output_path, output_base_dir, output_relative_path, PanicStrategy, UI_EXTENSIONS,
+};
 use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, Pretty, TestPaths};
 use crate::util::logv;
 use getopts::Options;
@@ -87,6 +89,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "force {check,build,run}-pass tests to this mode.",
             "check | build | run",
         )
+        .optopt("", "run", "whether to execute run-* tests", "auto | always | never")
         .optflag("", "ignored", "run tests marked as ignored")
         .optflag("", "exact", "filters match exactly")
         .optopt(
@@ -96,8 +99,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
              (eg. emulator, valgrind)",
             "PROGRAM",
         )
-        .optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
-        .optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
+        .optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
+        .optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
+        .optopt("", "target-panic", "what panic strategy the target supports", "unwind | abort")
         .optflag("", "verbose", "run tests verbosely, showing all output")
         .optflag(
             "",
@@ -234,10 +238,21 @@ fn make_absolute(path: PathBuf) -> PathBuf {
             mode.parse::<PassMode>()
                 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
         }),
+        run: matches.opt_str("run").and_then(|mode| match mode.as_str() {
+            "auto" => None,
+            "always" => Some(true),
+            "never" => Some(false),
+            _ => panic!("unknown `--run` option `{}` given", mode),
+        }),
         logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
         runtool: matches.opt_str("runtool"),
-        host_rustcflags: matches.opt_str("host-rustcflags"),
-        target_rustcflags: matches.opt_str("target-rustcflags"),
+        host_rustcflags: Some(matches.opt_strs("host-rustcflags").join(" ")),
+        target_rustcflags: Some(matches.opt_strs("target-rustcflags").join(" ")),
+        target_panic: match matches.opt_str("target-panic").as_deref() {
+            Some("unwind") | None => PanicStrategy::Unwind,
+            Some("abort") => PanicStrategy::Abort,
+            _ => panic!("unknown `--target-panic` option `{}` given", mode),
+        },
         target,
         host: opt_str2(matches.opt_str("host")),
         cdb,
index ecbaccf744dcdd3d61b41b25b6ed82d66c282022..c606aa1dfbfd42b06045176855533a817fc4d558 100644 (file)
@@ -259,6 +259,7 @@ pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
 pub fn compute_stamp_hash(config: &Config) -> String {
     let mut hash = DefaultHasher::new();
     config.stage_id.hash(&mut hash);
+    config.run.hash(&mut hash);
 
     match config.debugger {
         Some(Debugger::Cdb) => {
@@ -317,6 +318,7 @@ enum TestOutput {
 enum WillExecute {
     Yes,
     No,
+    Disabled,
 }
 
 /// Should `--emit metadata` be used?
@@ -357,14 +359,17 @@ fn pass_mode(&self) -> Option<PassMode> {
     }
 
     fn should_run(&self, pm: Option<PassMode>) -> WillExecute {
-        match self.config.mode {
-            Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => {
-                WillExecute::Yes
-            }
-            MirOpt if pm == Some(PassMode::Run) => WillExecute::Yes,
-            Ui | MirOpt => WillExecute::No,
+        let test_should_run = match self.config.mode {
+            Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => true,
+            MirOpt if pm == Some(PassMode::Run) => true,
+            Ui | MirOpt => false,
             mode => panic!("unimplemented for mode {:?}", mode),
-        }
+        };
+        if test_should_run { self.run_if_enabled() } else { WillExecute::No }
+    }
+
+    fn run_if_enabled(&self) -> WillExecute {
+        if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled }
     }
 
     fn should_run_successfully(&self, pm: Option<PassMode>) -> bool {
@@ -439,12 +444,17 @@ fn run_cfail_test(&self) {
 
     fn run_rfail_test(&self) {
         let pm = self.pass_mode();
-        let proc_res = self.compile_test(WillExecute::Yes, self.should_emit_metadata(pm));
+        let should_run = self.run_if_enabled();
+        let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm));
 
         if !proc_res.status.success() {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
 
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
         let proc_res = self.exec_compiled_test();
 
         // The value our Makefile configures valgrind to return on failure
@@ -483,12 +493,17 @@ fn check_correct_failure_status(&self, proc_res: &ProcRes) {
 
     fn run_rpass_test(&self) {
         let emit_metadata = self.should_emit_metadata(self.pass_mode());
-        let proc_res = self.compile_test(WillExecute::Yes, emit_metadata);
+        let should_run = self.run_if_enabled();
+        let proc_res = self.compile_test(should_run, emit_metadata);
 
         if !proc_res.status.success() {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
 
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
         // FIXME(#41968): Move this check to tidy?
         let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
         assert!(
@@ -510,12 +525,17 @@ fn run_valgrind_test(&self) {
             return self.run_rpass_test();
         }
 
-        let mut proc_res = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+        let should_run = self.run_if_enabled();
+        let mut proc_res = self.compile_test(should_run, EmitMetadata::No);
 
         if !proc_res.status.success() {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
 
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
         let mut new_config = self.config.clone();
         new_config.runtool = new_config.valgrind_path.clone();
         let new_cx = TestCx { config: &new_config, ..*self };
@@ -732,10 +752,14 @@ fn run_debuginfo_cdb_test(&self) {
 
     fn run_debuginfo_cdb_test_no_opt(&self) {
         // compile test file (it should have 'compile-flags:-g' in the header)
-        let compile_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+        let should_run = self.run_if_enabled();
+        let compile_result = self.compile_test(should_run, EmitMetadata::No);
         if !compile_result.status.success() {
             self.fatal_proc_rec("compilation failed!", &compile_result);
         }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
 
         let exe_file = self.make_exe_name();
 
@@ -826,10 +850,14 @@ fn run_debuginfo_gdb_test_no_opt(&self) {
         let mut cmds = commands.join("\n");
 
         // compile test file (it should have 'compile-flags:-g' in the header)
-        let compiler_run_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+        let should_run = self.run_if_enabled();
+        let compiler_run_result = self.compile_test(should_run, EmitMetadata::No);
         if !compiler_run_result.status.success() {
             self.fatal_proc_rec("compilation failed!", &compiler_run_result);
         }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
 
         let exe_file = self.make_exe_name();
 
@@ -1044,10 +1072,14 @@ fn run_debuginfo_lldb_test(&self) {
 
     fn run_debuginfo_lldb_test_no_opt(&self) {
         // compile test file (it should have 'compile-flags:-g' in the header)
-        let compile_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+        let should_run = self.run_if_enabled();
+        let compile_result = self.compile_test(should_run, EmitMetadata::No);
         if !compile_result.status.success() {
             self.fatal_proc_rec("compilation failed!", &compile_result);
         }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
 
         let exe_file = self.make_exe_name();
 
@@ -1531,7 +1563,9 @@ fn compile_test_general(
         // Only use `make_exe_name` when the test ends up being executed.
         let output_file = match will_execute {
             WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()),
-            WillExecute::No => TargetLocation::ThisDirectory(self.output_base_dir()),
+            WillExecute::No | WillExecute::Disabled => {
+                TargetLocation::ThisDirectory(self.output_base_dir())
+            }
         };
 
         let allow_unused = match self.config.mode {
index 67c04afc251ad7d80ea22e2056c93349e7e9df58..38b5f236d2c62ff0b1017efd183b193f5db33123 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 67c04afc251ad7d80ea22e2056c93349e7e9df58
+Subproject commit 38b5f236d2c62ff0b1017efd183b193f5db33123
index 359513ce678efba186972e4f280dbc7046cac15f..e33f4e68496b296dedb100e297dc4451f169b2b3 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 359513ce678efba186972e4f280dbc7046cac15f
+Subproject commit e33f4e68496b296dedb100e297dc4451f169b2b3
index 617535393bb5ccc7adf0bac8a3b9a9c306454e79..fd109fb587904cfecc1149e068814bfd38feb83c 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 617535393bb5ccc7adf0bac8a3b9a9c306454e79
+Subproject commit fd109fb587904cfecc1149e068814bfd38feb83c
index a67e2455478f88a14a7fb6c794c83f4cb74030c1..298fc7519facc0e775e8ffbc19fb47231840faeb 100644 (file)
@@ -3,29 +3,30 @@
 // ```
 // npm install browser-ui-test
 // ```
-const path = require('path');
+const fs = require("fs");
+const path = require("path");
 const {Options, runTest} = require('browser-ui-test');
 
 function showHelp() {
     console.log("rustdoc-js options:");
     console.log("  --doc-folder [PATH]        : location of the generated doc folder");
     console.log("  --help                     : show this message then quit");
-    console.log("  --test-file [PATH]         : location of the JS test file");
+    console.log("  --tests-folder [PATH]      : location of the .GOML tests folder");
 }
 
 function parseOptions(args) {
     var opts = {
         "doc_folder": "",
-        "test_file": "",
+        "tests_folder": "",
     };
     var correspondances = {
         "--doc-folder": "doc_folder",
-        "--test-file": "test_file",
+        "--tests-folder": "tests_folder",
     };
 
     for (var i = 0; i < args.length; ++i) {
         if (args[i] === "--doc-folder"
-            || args[i] === "--test-file") {
+            || args[i] === "--tests-folder") {
             i += 1;
             if (i >= args.length) {
                 console.log("Missing argument after `" + args[i - 1] + "` option.");
@@ -41,8 +42,8 @@ function parseOptions(args) {
             return null;
         }
     }
-    if (opts["test_file"].length < 1) {
-        console.log("Missing `--test-file` option.");
+    if (opts["tests_folder"].length < 1) {
+        console.log("Missing `--tests-folder` option.");
     } else if (opts["doc_folder"].length < 1) {
         console.log("Missing `--doc-folder` option.");
     } else {
@@ -51,15 +52,8 @@ function parseOptions(args) {
     return null;
 }
 
-function checkFile(test_file, opts, loaded, index) {
-    const test_name = path.basename(test_file, ".js");
-
-    process.stdout.write('Checking "' + test_name + '" ... ');
-    return runChecks(test_file, loaded, index);
-}
-
-function main(argv) {
-    var opts = parseOptions(argv.slice(2));
+async function main(argv) {
+    let opts = parseOptions(argv.slice(2));
     if (opts === null) {
         process.exit(1);
     }
@@ -68,7 +62,7 @@ function main(argv) {
     try {
         // This is more convenient that setting fields one by one.
         options.parseArguments([
-            '--no-screenshot',
+            "--no-screenshot",
             "--variable", "DOC_PATH", opts["doc_folder"],
         ]);
     } catch (error) {
@@ -76,14 +70,26 @@ function main(argv) {
         process.exit(1);
     }
 
-    runTest(opts["test_file"], options).then(out => {
-        const [output, nb_failures] = out;
-        console.log(output);
-        process.exit(nb_failures);
-    }).catch(err => {
-        console.error(err);
+    let failed = false;
+    let files = fs.readdirSync(opts["tests_folder"]).filter(file => path.extname(file) == ".goml");
+
+    files.sort();
+    for (var i = 0; i < files.length; ++i) {
+        const testPath = path.join(opts["tests_folder"], files[i]);
+        await runTest(testPath, options).then(out => {
+            const [output, nb_failures] = out;
+            console.log(output);
+            if (nb_failures > 0) {
+                failed = true;
+            }
+        }).catch(err => {
+            console.error(err);
+            failed = true;
+        });
+    }
+    if (failed) {
         process.exit(1);
-    });
+    }
 }
 
 main(process.argv);
index b604b39967ec76411ed5e143af5c4c17e616c46f..064dd716521f51dda66b7afa0667b7a12ea0edf8 100644 (file)
     ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
 ];
 
+const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
+    ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-entity", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-jit", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-module", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-native", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-object", "Apache-2.0 WITH LLVM-exception"),
+    ("libloading", "ISC"),
+    ("mach", "BSD-2-Clause"),
+    ("regalloc", "Apache-2.0 WITH LLVM-exception"),
+    ("target-lexicon", "Apache-2.0 WITH LLVM-exception"),
+];
+
 /// These are the root crates that are part of the runtime. The licenses for
 /// these and all their dependencies *must not* be in the exception list.
 const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"];
 
 /// Crates whose dependencies must be explicitly permitted.
-const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_middle", "rustc_codegen_llvm"];
+const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_driver", "rustc_codegen_llvm"];
 
 /// Crates rustc is allowed to depend on. Avoid adding to the list if possible.
 ///
     "cc",
     "cfg-if",
     "chalk-derive",
+    "chalk-engine",
     "chalk-ir",
+    "chalk-solve",
+    "chrono",
     "cmake",
     "compiler_builtins",
     "cpuid-bool",
     "expect-test",
     "fake-simd",
     "filetime",
+    "fixedbitset",
     "flate2",
     "fortanix-sgx-abi",
     "fuchsia-zircon",
     "indexmap",
     "instant",
     "itertools",
+    "itoa",
     "jobserver",
     "kernel32-sys",
     "lazy_static",
     "libz-sys",
     "lock_api",
     "log",
+    "matchers",
     "maybe-uninit",
     "md-5",
     "measureme",
     "memoffset",
     "miniz_oxide",
     "num_cpus",
+    "num-integer",
+    "num-traits",
     "object",
     "once_cell",
     "opaque-debug",
     "parking_lot_core",
     "pathdiff",
     "perf-event-open-sys",
+    "petgraph",
     "pin-project-lite",
     "pkg-config",
     "polonius-engine",
     "rand_xorshift",
     "redox_syscall",
     "regex",
+    "regex-automata",
     "regex-syntax",
     "remove_dir_all",
+    "rls-data",
+    "rls-span",
     "rustc-demangle",
     "rustc-hash",
     "rustc-rayon",
     "rustc-rayon-core",
     "rustc_version",
+    "ryu",
     "scoped-tls",
     "scopeguard",
     "semver",
     "semver-parser",
     "serde",
     "serde_derive",
+    "serde_json",
     "sha-1",
     "sha2",
     "smallvec",
+    "sharded-slab",
     "snap",
     "stable_deref_trait",
     "stacker",
     "termcolor",
     "termize",
     "thread_local",
+    "time",
+    "tinyvec",
     "tracing",
     "tracing-attributes",
     "tracing-core",
+    "tracing-log",
+    "tracing-serde",
+    "tracing-subscriber",
+    "tracing-tree",
     "typenum",
     "unicode-normalization",
     "unicode-script",
     "yansi-term",
 ];
 
+const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
+    "anyhow",
+    "ar",
+    "autocfg",
+    "bitflags",
+    "byteorder",
+    "cfg-if",
+    "cranelift-bforest",
+    "cranelift-codegen",
+    "cranelift-codegen-meta",
+    "cranelift-codegen-shared",
+    "cranelift-entity",
+    "cranelift-frontend",
+    "cranelift-jit",
+    "cranelift-module",
+    "cranelift-native",
+    "cranelift-object",
+    "crc32fast",
+    "errno",
+    "errno-dragonfly",
+    "gcc",
+    "gimli",
+    "hashbrown",
+    "indexmap",
+    "libc",
+    "libloading",
+    "log",
+    "mach",
+    "object",
+    "proc-macro2",
+    "quote",
+    "regalloc",
+    "region",
+    "rustc-hash",
+    "smallvec",
+    "syn",
+    "target-lexicon",
+    "thiserror",
+    "thiserror-impl",
+    "unicode-xid",
+    "winapi",
+    "winapi-i686-pc-windows-gnu",
+    "winapi-x86_64-pc-windows-gnu",
+];
+
+const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
+    // These two crates take quite a long time to build, so don't allow two versions of them
+    // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
+    // under control.
+    "cargo",
+    "rustc-ap-rustc_ast",
+];
+
 /// Dependency checks.
 ///
 /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
@@ -203,17 +294,39 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
         .manifest_path(root.join("Cargo.toml"))
         .features(cargo_metadata::CargoOpt::AllFeatures);
     let metadata = t!(cmd.exec());
-    check_exceptions(&metadata, bad);
-    check_dependencies(&metadata, bad);
-    check_crate_duplicate(&metadata, bad);
+    let runtime_ids = compute_runtime_crates(&metadata);
+    check_exceptions(&metadata, EXCEPTIONS, runtime_ids, bad);
+    check_dependencies(&metadata, PERMITTED_DEPENDENCIES, RESTRICTED_DEPENDENCY_CRATES, bad);
+    check_crate_duplicate(&metadata, FORBIDDEN_TO_HAVE_DUPLICATES, bad);
+
+    // Check rustc_codegen_cranelift independently as it has it's own workspace.
+    let mut cmd = cargo_metadata::MetadataCommand::new();
+    cmd.cargo_path(cargo)
+        .manifest_path(root.join("compiler/rustc_codegen_cranelift/Cargo.toml"))
+        .features(cargo_metadata::CargoOpt::AllFeatures);
+    let metadata = t!(cmd.exec());
+    let runtime_ids = HashSet::new();
+    check_exceptions(&metadata, EXCEPTIONS_CRANELIFT, runtime_ids, bad);
+    check_dependencies(
+        &metadata,
+        PERMITTED_CRANELIFT_DEPENDENCIES,
+        &["rustc_codegen_cranelift"],
+        bad,
+    );
+    check_crate_duplicate(&metadata, &[], bad);
 }
 
 /// Check that all licenses are in the valid list in `LICENSES`.
 ///
 /// Packages listed in `EXCEPTIONS` are allowed for tools.
-fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
+fn check_exceptions(
+    metadata: &Metadata,
+    exceptions: &[(&str, &str)],
+    runtime_ids: HashSet<&PackageId>,
+    bad: &mut bool,
+) {
     // Validate the EXCEPTIONS list hasn't changed.
-    for (name, license) in EXCEPTIONS {
+    for (name, license) in exceptions {
         // Check that the package actually exists.
         if !metadata.packages.iter().any(|p| p.name == *name) {
             tidy_error!(
@@ -225,13 +338,6 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
         }
         // Check that the license hasn't changed.
         for pkg in metadata.packages.iter().filter(|p| p.name == *name) {
-            if pkg.name == "fuchsia-cprng" {
-                // This package doesn't declare a license expression. Manual
-                // inspection of the license file is necessary, which appears
-                // to be BSD-3-Clause.
-                assert!(pkg.license.is_none());
-                continue;
-            }
             match &pkg.license {
                 None => {
                     tidy_error!(
@@ -242,14 +348,6 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
                 }
                 Some(pkg_license) => {
                     if pkg_license.as_str() != *license {
-                        if *name == "crossbeam-queue"
-                            && *license == "MIT/Apache-2.0 AND BSD-2-Clause"
-                        {
-                            // We have two versions of crossbeam-queue and both
-                            // are fine.
-                            continue;
-                        }
-
                         println!("dependency exception `{}` license has changed", name);
                         println!("    previously `{}` now `{}`", license, pkg_license);
                         println!("    update EXCEPTIONS for the new license");
@@ -260,8 +358,7 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
         }
     }
 
-    let exception_names: Vec<_> = EXCEPTIONS.iter().map(|(name, _license)| *name).collect();
-    let runtime_ids = compute_runtime_crates(metadata);
+    let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
 
     // Check if any package does not have a valid license.
     for pkg in &metadata.packages {
@@ -296,9 +393,14 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
 /// `true` if a check failed.
 ///
 /// Specifically, this checks that the dependencies are on the `PERMITTED_DEPENDENCIES`.
-fn check_dependencies(metadata: &Metadata, bad: &mut bool) {
+fn check_dependencies(
+    metadata: &Metadata,
+    permitted_dependencies: &[&'static str],
+    restricted_dependency_crates: &[&'static str],
+    bad: &mut bool,
+) {
     // Check that the PERMITTED_DEPENDENCIES does not have unused entries.
-    for name in PERMITTED_DEPENDENCIES {
+    for name in permitted_dependencies {
         if !metadata.packages.iter().any(|p| p.name == *name) {
             tidy_error!(
                 bad,
@@ -309,12 +411,12 @@ fn check_dependencies(metadata: &Metadata, bad: &mut bool) {
         }
     }
     // Get the list in a convenient form.
-    let permitted_dependencies: HashSet<_> = PERMITTED_DEPENDENCIES.iter().cloned().collect();
+    let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
 
     // Check dependencies.
     let mut visited = BTreeSet::new();
     let mut unapproved = BTreeSet::new();
-    for &krate in RESTRICTED_DEPENDENCY_CRATES.iter() {
+    for &krate in restricted_dependency_crates.iter() {
         let pkg = pkg_from_name(metadata, krate);
         let mut bad =
             check_crate_dependencies(&permitted_dependencies, metadata, &mut visited, pkg);
@@ -367,16 +469,12 @@ fn check_crate_dependencies<'a>(
 }
 
 /// Prevents multiple versions of some expensive crates.
-fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) {
-    const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
-        // These two crates take quite a long time to build, so don't allow two versions of them
-        // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
-        // under control.
-        "cargo",
-        "rustc-ap-rustc_ast",
-    ];
-
-    for &name in FORBIDDEN_TO_HAVE_DUPLICATES {
+fn check_crate_duplicate(
+    metadata: &Metadata,
+    forbidden_to_have_duplicates: &[&str],
+    bad: &mut bool,
+) {
+    for &name in forbidden_to_have_duplicates {
         let matches: Vec<_> = metadata.packages.iter().filter(|pkg| pkg.name == name).collect();
         match matches.len() {
             0 => {
@@ -456,16 +554,7 @@ fn normal_deps_of_r<'a>(
         .iter()
         .find(|n| &n.id == pkg_id)
         .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id));
-    // Don't care about dev-dependencies.
-    // Build dependencies *shouldn't* matter unless they do some kind of
-    // codegen. For now we'll assume they don't.
-    let deps = node.deps.iter().filter(|node_dep| {
-        node_dep
-            .dep_kinds
-            .iter()
-            .any(|kind_info| kind_info.kind == cargo_metadata::DependencyKind::Normal)
-    });
-    for dep in deps {
+    for dep in &node.deps {
         normal_deps_of_r(resolve, &dep.pkg, result);
     }
 }
index b14b5aeb57236672600b9d1e3c864f5affa1643a..a7e700b935e04b2a9df6fdca3b94642a9b18f54e 100644 (file)
@@ -51,6 +51,14 @@ pub struct Feature {
     pub has_gate_test: bool,
     pub tracking_issue: Option<NonZeroU32>,
 }
+impl Feature {
+    fn tracking_issue_display(&self) -> impl fmt::Display {
+        match self.tracking_issue {
+            None => "none".to_string(),
+            Some(x) => x.to_string(),
+        }
+    }
+}
 
 pub type Features = HashMap<String, Feature>;
 
@@ -361,10 +369,12 @@ fn get_and_check_lib_features(
                     if f.tracking_issue != s.tracking_issue && f.level != Status::Stable {
                         tidy_error!(
                             bad,
-                            "{}:{}: mismatches the `issue` in {}",
+                            "{}:{}: `issue` \"{}\" mismatches the {} `issue` of \"{}\"",
                             file.display(),
                             line,
-                            display
+                            f.tracking_issue_display(),
+                            display,
+                            s.tracking_issue_display(),
                         );
                     }
                 }
index 144529d8641eee4c56bb02f00b1b0fcedae6e18c..db177f75ceae9e906899bbe6a4b03da899cb3cba 100644 (file)
@@ -21,8 +21,7 @@
 //! - libunwind may have platform-specific code.
 //! - other crates in the std facade may not.
 //! - std may have platform-specific code in the following places:
-//!   - `sys/unix/`
-//!   - `sys/windows/`
+//!   - `sys/`
 //!   - `os/`
 //!
 //! `std/sys_common` should _not_ contain platform-specific code.
 
 // Paths that may contain platform-specific code.
 const EXCEPTION_PATHS: &[&str] = &[
-    // std crates
     "library/panic_abort",
     "library/panic_unwind",
     "library/unwind",
-    "library/std/src/sys/", // Platform-specific code for std lives here.
-    // This has the trailing slash so that sys_common is not excepted.
-    "library/std/src/os", // Platform-specific public interfaces
-    "library/rtstartup",  // Not sure what to do about this. magic stuff for mingw
-    // Integration test for platform-specific run-time feature detection:
-    "library/std/tests/run-time-detect.rs",
-    "library/std/src/net/test.rs",
-    "library/std/src/net/addr",
-    "library/std/src/net/udp",
-    "library/std/src/sys_common/remutex.rs",
-    "library/std/src/sync/mutex.rs",
-    "library/std/src/sync/rwlock.rs",
-    "library/term", // Not sure how to make this crate portable, but test crate needs it.
-    "library/test", // Probably should defer to unstable `std::sys` APIs.
-    // std testing crates, okay for now at least
-    "library/core/tests",
-    "library/alloc/tests/lib.rs",
-    "library/alloc/benches/lib.rs",
+    "library/rtstartup", // Not sure what to do about this. magic stuff for mingw
+    "library/term",      // Not sure how to make this crate portable, but test crate needs it.
+    "library/test",      // Probably should defer to unstable `std::sys` APIs.
     // The `VaList` implementation must have platform specific code.
     // The Windows implementation of a `va_list` is always a character
     // pointer regardless of the target architecture. As a result,
     // we must use `#[cfg(windows)]` to conditionally compile the
     // correct `VaList` structure for windows.
     "library/core/src/ffi.rs",
+    "library/std/src/sys/", // Platform-specific code for std lives here.
+    "library/std/src/os",   // Platform-specific public interfaces
+    // Temporary `std` exceptions
+    // FIXME: platform-specific code should be moved to `sys`
+    "library/std/src/io/copy.rs",
+    "library/std/src/io/stdio.rs",
+    "library/std/src/f32.rs",
+    "library/std/src/f64.rs",
+    "library/std/src/path.rs",
+    "library/std/src/thread/available_concurrency.rs",
+    "library/std/src/sys_common", // Should only contain abstractions over platforms
+    "library/std/src/net/test.rs", // Utility helpers for tests
 ];
 
 pub fn check(path: &Path, bad: &mut bool) {
@@ -82,6 +77,11 @@ pub fn check(path: &Path, bad: &mut bool) {
             return;
         }
 
+        // exclude tests and benchmarks as some platforms do not support all tests
+        if filestr.contains("tests") || filestr.contains("benches") {
+            return;
+        }
+
         check_cfgs(contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
     });
 
@@ -96,9 +96,6 @@ fn check_cfgs(
     saw_target_arch: &mut bool,
     saw_cfg_bang: &mut bool,
 ) {
-    // For now it's ok to have platform-specific code after 'mod tests'.
-    let mod_tests_idx = find_test_mod(contents);
-    let contents = &contents[..mod_tests_idx];
     // Pull out all `cfg(...)` and `cfg!(...)` strings.
     let cfgs = parse_cfgs(contents);
 
@@ -149,39 +146,22 @@ fn check_cfgs(
             continue;
         }
 
-        err(idx, cfg);
-    }
-}
-
-fn find_test_mod(contents: &str) -> usize {
-    if let Some(mod_tests_idx) = contents.find("mod tests") {
-        // Also capture a previous line indicating that "mod tests" is cfg'd out.
-        let prev_newline_idx = contents[..mod_tests_idx].rfind('\n').unwrap_or(mod_tests_idx);
-        let prev_newline_idx = contents[..prev_newline_idx].rfind('\n');
-        if let Some(nl) = prev_newline_idx {
-            let prev_line = &contents[nl + 1..mod_tests_idx];
-            if prev_line.contains("cfg(all(test, not(target_os")
-                || prev_line.contains("cfg(all(test, not(any(target_os")
-            {
-                nl
-            } else {
-                mod_tests_idx
-            }
-        } else {
-            mod_tests_idx
+        // exclude tests as some platforms do not support all tests
+        if cfg.contains("test") {
+            continue;
         }
-    } else {
-        contents.len()
+
+        err(idx, cfg);
     }
 }
 
-fn parse_cfgs<'a>(contents: &'a str) -> Vec<(usize, &'a str)> {
+fn parse_cfgs(contents: &str) -> Vec<(usize, &str)> {
     let candidate_cfgs = contents.match_indices("cfg");
     let candidate_cfg_idxs = candidate_cfgs.map(|(i, _)| i);
     // This is puling out the indexes of all "cfg" strings
     // that appear to be tokens followed by a parenthesis.
     let cfgs = candidate_cfg_idxs.filter(|i| {
-        let pre_idx = i.saturating_sub(*i);
+        let pre_idx = i.saturating_sub(1);
         let succeeds_non_ident = !contents
             .as_bytes()
             .get(pre_idx)