From 4f9fe856a25ab29345b90e7725509e9ee38a37be Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:41 +0200 Subject: Adding upstream version 1.69.0+dfsg1. Signed-off-by: Daniel Baumann --- src/bootstrap/Cargo.lock | 4 +- src/bootstrap/Cargo.toml | 5 +- src/bootstrap/README.md | 199 +--- src/bootstrap/bin/main.rs | 36 +- src/bootstrap/bootstrap.py | 252 ++--- src/bootstrap/builder.rs | 20 +- src/bootstrap/builder/tests.rs | 2 + src/bootstrap/check.rs | 95 +- src/bootstrap/clean.rs | 1 + src/bootstrap/compile.rs | 130 ++- src/bootstrap/config.rs | 28 +- src/bootstrap/config/tests.rs | 12 + src/bootstrap/configure.py | 28 +- src/bootstrap/defaults/config.compiler.toml | 2 + src/bootstrap/defaults/config.library.toml | 2 + src/bootstrap/dist.rs | 33 +- src/bootstrap/doc.rs | 38 +- src/bootstrap/download.rs | 168 ++-- src/bootstrap/flags.rs | 34 +- src/bootstrap/format.rs | 60 +- src/bootstrap/install.rs | 12 - src/bootstrap/lib.rs | 143 +-- src/bootstrap/metadata.rs | 2 +- src/bootstrap/metrics.rs | 2 +- src/bootstrap/native.rs | 117 ++- src/bootstrap/run.rs | 1 + src/bootstrap/setup.rs | 268 ++++- src/bootstrap/setup/tests.rs | 14 + src/bootstrap/test.rs | 25 +- src/bootstrap/tool.rs | 72 +- src/bootstrap/toolstate.rs | 2 +- .../docker/host-x86_64/dist-s390x-linux/Dockerfile | 2 +- .../host-x86_64/dist-x86_64-linux/Dockerfile | 2 +- src/ci/docker/host-x86_64/mingw-check/Dockerfile | 4 +- .../host-x86_64/mingw-check/validate-toolstate.sh | 6 - .../x86_64-gnu-llvm-13-stage1/Dockerfile | 49 - .../host-x86_64/x86_64-gnu-llvm-13/Dockerfile | 69 -- .../x86_64-gnu-llvm-14-stage1/Dockerfile | 49 + .../host-x86_64/x86_64-gnu-llvm-14/Dockerfile | 67 ++ .../host-x86_64/x86_64-gnu-llvm-15/Dockerfile | 67 ++ .../x86_64-gnu-tools/browser-ui-test.version | 2 +- src/ci/docker/scripts/fuchsia-test-runner.py | 172 +++- src/ci/github-actions/ci.yml | 34 +- src/ci/pgo.sh | 230 ----- src/ci/run.sh | 10 + src/ci/scripts/install-mingw.sh | 23 +- src/ci/scripts/should-skip-this.sh | 37 +- src/ci/stage-build.py | 842 ++++++++++++++++ src/doc/book/.github/workflows/main.yml | 4 +- src/doc/book/CONTRIBUTING.md | 4 +- src/doc/book/README.md | 4 +- src/doc/book/ci/dictionary.txt | 1 + .../listing-02-04/output.txt | 1 + .../no-listing-02-without-expect/output.txt | 4 +- .../output.txt | 2 +- .../no-listing-04-cant-use-after-move/output.txt | 4 + .../output.txt | 11 +- .../no-listing-10-non-exhaustive-match/output.txt | 4 + .../listing-07-12/output.txt | 12 +- .../listing-08-19/output.txt | 1 - .../listing-11-03/output.txt | 2 +- .../listing-11-10/output.txt | 2 +- .../no-listing-03-introducing-a-bug/output.txt | 2 +- .../no-listing-04-bug-in-add-two/output.txt | 2 +- .../no-listing-06-greeter-with-bug/output.txt | 2 +- .../output.txt | 2 +- .../output.txt | 2 +- .../output-only-01-show-output/output.txt | 2 +- .../ch12-an-io-project/listing-12-12/output.txt | 4 +- .../ch12-an-io-project/listing-12-16/output.txt | 2 +- .../listing-13-14/output.txt | 4 +- .../ch15-smart-pointers/listing-15-03/output.txt | 4 +- .../ch15-smart-pointers/listing-15-23/output.txt | 2 +- .../listing-16-14/output.txt | 3 + .../listing-18-08/output.txt | 8 + .../listing-18-10/output.txt | 2 +- .../listing-19-20/output.txt | 4 +- .../ch20-web-server/listing-20-22/output.txt | 1 + .../output.txt | 1 + src/doc/book/rust-toolchain | 2 +- src/doc/book/src/ch00-00-introduction.md | 2 +- .../book/src/ch03-01-variables-and-mutability.md | 2 +- src/doc/book/src/ch10-01-syntax.md | 8 +- src/doc/book/src/title-page.md | 4 +- src/doc/embedded-book/src/assets/verify.jpeg | Bin 155671 -> 100961 bytes src/doc/embedded-book/src/collections/index.md | 9 +- .../src/interoperability/c-with-rust.md | 2 - src/doc/embedded-book/src/intro/install/macos.md | 20 +- src/doc/embedded-book/src/intro/install/verify.md | 2 +- .../embedded-book/src/peripherals/singletons.md | 3 +- src/doc/embedded-book/src/start/registers.md | 2 +- src/doc/index.md | 228 +++-- src/doc/nomicon/src/dropck.md | 2 +- src/doc/nomicon/src/ffi.md | 21 +- src/doc/nomicon/src/send-and-sync.md | 5 +- src/doc/nomicon/src/subtyping.md | 2 +- .../reference/src/behavior-considered-undefined.md | 2 +- src/doc/reference/src/destructors.md | 2 +- src/doc/reference/src/expressions/if-expr.md | 2 +- src/doc/reference/src/expressions/operator-expr.md | 2 +- src/doc/reference/src/items/external-blocks.md | 2 + src/doc/reference/src/linkage.md | 2 +- src/doc/reference/src/patterns.md | 47 +- src/doc/reference/src/trait-bounds.md | 2 +- .../src/error/option_unwrap/and_then.md | 5 +- .../rust-by-example/src/error/result/result_map.md | 2 +- src/doc/rust-by-example/src/generics/bounds.md | 4 +- src/doc/rust-by-example/src/hello/comment.md | 2 +- src/doc/rust-by-example/src/index.md | 2 +- src/doc/rust-by-example/src/primitives/tuples.md | 2 +- .../rust-by-example/src/std_misc/file/create.md | 2 +- src/doc/rustc-dev-guide/.github/workflows/ci.yml | 5 +- src/doc/rustc-dev-guide/CITATION.cff | 9 + src/doc/rustc-dev-guide/README.md | 2 +- src/doc/rustc-dev-guide/book.toml | 12 +- src/doc/rustc-dev-guide/ci/date-check/src/main.rs | 2 +- src/doc/rustc-dev-guide/examples/README | 11 + .../examples/rustc-driver-example.rs | 19 +- .../examples/rustc-driver-getting-diagnostics.rs | 9 +- .../rustc-driver-interacting-with-the-ast.rs | 14 +- src/doc/rustc-dev-guide/src/SUMMARY.md | 16 +- src/doc/rustc-dev-guide/src/appendix/glossary.md | 4 +- src/doc/rustc-dev-guide/src/asm.md | 2 +- src/doc/rustc-dev-guide/src/ast-validation.md | 36 +- .../src/backend/backend-agnostic.md | 23 +- src/doc/rustc-dev-guide/src/backend/monomorph.md | 2 +- .../rustc-dev-guide/src/backend/updating-llvm.md | 17 +- .../region_inference/lifetime_parameters.md | 4 +- .../rustc-dev-guide/src/building/bootstrapping.md | 124 +-- .../src/building/how-to-build-and-run.md | 15 +- .../rustc-dev-guide/src/building/prerequisites.md | 2 +- src/doc/rustc-dev-guide/src/building/suggested.md | 96 +- src/doc/rustc-dev-guide/src/closure.md | 2 +- src/doc/rustc-dev-guide/src/compiler-debugging.md | 37 +- src/doc/rustc-dev-guide/src/compiler-src.md | 4 +- src/doc/rustc-dev-guide/src/constants.md | 2 +- src/doc/rustc-dev-guide/src/contributing.md | 10 +- src/doc/rustc-dev-guide/src/crates-io.md | 7 +- src/doc/rustc-dev-guide/src/diagnostics.md | 17 +- .../src/diagnostics/diagnostic-items.md | 2 +- .../src/diagnostics/diagnostic-structs.md | 4 +- .../rustc-dev-guide/src/diagnostics/translation.md | 2 +- src/doc/rustc-dev-guide/src/getting-started.md | 6 +- src/doc/rustc-dev-guide/src/git.md | 18 +- src/doc/rustc-dev-guide/src/hir.md | 4 +- src/doc/rustc-dev-guide/src/identifiers.md | 2 +- .../src/implementing_new_features.md | 2 +- .../src/llvm-coverage-instrumentation.md | 17 +- src/doc/rustc-dev-guide/src/mir/debugging.md | 4 +- src/doc/rustc-dev-guide/src/mir/visitor.md | 6 +- src/doc/rustc-dev-guide/src/overview.md | 2 +- src/doc/rustc-dev-guide/src/profiling/with_perf.md | 11 +- .../src/rustc-driver-getting-diagnostics.md | 30 +- .../src/rustc-driver-interacting-with-the-ast.md | 41 +- src/doc/rustc-dev-guide/src/rustc-driver.md | 2 +- .../rustc-dev-guide/src/solve/canonicalization.md | 84 ++ src/doc/rustc-dev-guide/src/solve/coinduction.md | 250 +++++ src/doc/rustc-dev-guide/src/solve/the-solver.md | 17 + src/doc/rustc-dev-guide/src/solve/trait-solving.md | 114 +++ src/doc/rustc-dev-guide/src/tests/compiletest.md | 5 + src/doc/rustc-dev-guide/src/tests/running.md | 21 - src/doc/rustc-dev-guide/src/traits/resolution.md | 4 +- src/doc/rustc-dev-guide/src/ty.md | 80 +- src/doc/rustc-dev-guide/src/type-checking.md | 10 +- src/doc/rustc-dev-guide/src/type-inference.md | 12 +- src/doc/rustc/book.toml | 1 + src/doc/rustc/src/codegen-options/index.md | 66 +- src/doc/rustc/src/json.md | 1 - src/doc/rustc/src/platform-support.md | 6 +- src/doc/rustc/src/platform-support/fuchsia.md | 98 +- src/doc/rustc/src/platform-support/nto-qnx.md | 175 +++- src/doc/rustdoc/src/how-to-read-rustdoc.md | 3 + src/doc/style-guide/src/cargo.md | 3 +- src/doc/style-guide/src/statements.md | 78 ++ .../compiler-flags/export-executable-symbols.md | 43 + .../src/compiler-flags/instrument-xray.md | 39 + .../unstable-book/src/compiler-flags/sanitizer.md | 20 + .../src/compiler-flags/tiny-const-eval-limit.md | 6 + .../src/library-features/char-error-internals.md | 5 - .../src/library-features/int-error-internals.md | 5 - src/etc/pre-push.sh | 2 +- src/etc/vscode_settings.json | 28 + src/librustdoc/Cargo.toml | 2 - src/librustdoc/clean/auto_trait.rs | 6 +- src/librustdoc/clean/blanket_impl.rs | 2 +- src/librustdoc/clean/cfg.rs | 8 +- src/librustdoc/clean/cfg/tests.rs | 5 +- src/librustdoc/clean/inline.rs | 64 +- src/librustdoc/clean/mod.rs | 476 +++++---- src/librustdoc/clean/render_macro_matchers.rs | 3 +- src/librustdoc/clean/types.rs | 248 +---- src/librustdoc/clean/types/tests.rs | 1 + src/librustdoc/clean/utils.rs | 23 +- src/librustdoc/config.rs | 2 +- src/librustdoc/core.rs | 80 +- src/librustdoc/doctest.rs | 117 +-- src/librustdoc/formats/cache.rs | 41 +- src/librustdoc/formats/item_type.rs | 3 +- src/librustdoc/html/format.rs | 98 +- src/librustdoc/html/highlight.rs | 30 +- src/librustdoc/html/layout.rs | 1 - src/librustdoc/html/length_limit.rs | 4 +- src/librustdoc/html/length_limit/tests.rs | 2 +- src/librustdoc/html/markdown.rs | 209 ++-- src/librustdoc/html/markdown/tests.rs | 2 +- src/librustdoc/html/render/context.rs | 43 +- src/librustdoc/html/render/mod.rs | 166 +-- src/librustdoc/html/render/print_item.rs | 672 ++++++------- src/librustdoc/html/render/search_index.rs | 11 +- src/librustdoc/html/render/write_shared.rs | 5 +- src/librustdoc/html/sources.rs | 3 +- src/librustdoc/html/static/.eslintrc.js | 1 - src/librustdoc/html/static/css/rustdoc.css | 148 +-- src/librustdoc/html/static/css/settings.css | 30 +- src/librustdoc/html/static/css/themes/ayu.css | 77 +- src/librustdoc/html/static/css/themes/dark.css | 26 +- src/librustdoc/html/static/css/themes/light.css | 26 +- .../html/static/fonts/SourceSerif4-Bold.ttf.woff2 | Bin 81320 -> 81540 bytes .../html/static/fonts/SourceSerif4-It.ttf.woff2 | Bin 59860 -> 59716 bytes .../html/static/fonts/SourceSerif4-LICENSE.md | 2 +- .../static/fonts/SourceSerif4-Regular.ttf.woff2 | Bin 76180 -> 76260 bytes src/librustdoc/html/static/images/down-arrow.svg | 1 - src/librustdoc/html/static/images/toggle-minus.svg | 1 - src/librustdoc/html/static/images/toggle-plus.svg | 1 - src/librustdoc/html/static/images/wheel.svg | 2 +- src/librustdoc/html/static/js/main.js | 125 +-- src/librustdoc/html/static/js/search.js | 355 ++++--- src/librustdoc/html/static/js/settings.js | 55 +- src/librustdoc/html/static/js/source-script.js | 3 +- src/librustdoc/html/static/js/storage.js | 113 +-- src/librustdoc/html/static_files.rs | 3 - src/librustdoc/html/templates/page.html | 3 +- src/librustdoc/html/templates/print_item.html | 2 +- src/librustdoc/json/conversions.rs | 99 +- src/librustdoc/json/import_finder.rs | 10 +- src/librustdoc/json/mod.rs | 34 +- src/librustdoc/lib.rs | 40 +- src/librustdoc/lint.rs | 6 +- src/librustdoc/markdown.rs | 9 +- src/librustdoc/passes/calculate_doc_coverage.rs | 8 +- src/librustdoc/passes/check_doc_test_visibility.rs | 38 +- src/librustdoc/passes/collect_intra_doc_links.rs | 313 ++---- .../passes/collect_intra_doc_links/early.rs | 407 -------- src/librustdoc/passes/collect_trait_impls.rs | 91 +- .../passes/lint/check_code_block_syntax.rs | 11 +- src/librustdoc/passes/lint/html_tags.rs | 10 +- src/librustdoc/passes/mod.rs | 3 +- src/librustdoc/passes/propagate_doc_cfg.rs | 17 +- src/librustdoc/passes/strip_hidden.rs | 117 ++- src/librustdoc/passes/stripper.rs | 32 +- src/librustdoc/scrape_examples.rs | 2 +- src/librustdoc/visit_ast.rs | 275 +++-- src/librustdoc/visit_lib.rs | 9 +- src/stage0.json | 580 +++++------ src/tools/build_helper/src/git.rs | 72 +- src/tools/bump-stage0/src/main.rs | 2 +- src/tools/clippy/.github/workflows/clippy_bors.yml | 5 + src/tools/clippy/CHANGELOG.md | 207 +++- src/tools/clippy/Cargo.toml | 3 +- src/tools/clippy/README.md | 57 +- src/tools/clippy/book/src/README.md | 2 +- src/tools/clippy/book/src/SUMMARY.md | 1 + src/tools/clippy/book/src/configuration.md | 16 +- .../clippy/book/src/development/adding_lints.md | 7 +- .../src/development/infrastructure/backport.md | 1 + .../book/src/development/infrastructure/book.md | 14 +- .../development/infrastructure/changelog_update.md | 16 +- .../book/src/development/infrastructure/sync.md | 3 +- src/tools/clippy/book/src/lint_configuration.md | 553 ++++++++++ src/tools/clippy/clippy_dev/Cargo.toml | 2 +- src/tools/clippy/clippy_dev/src/main.rs | 127 +-- src/tools/clippy/clippy_dev/src/new_lint.rs | 9 +- src/tools/clippy/clippy_dev/src/update_lints.rs | 6 +- src/tools/clippy/clippy_lints/Cargo.toml | 4 +- .../clippy_lints/src/bool_assert_comparison.rs | 90 +- src/tools/clippy/clippy_lints/src/booleans.rs | 5 +- src/tools/clippy/clippy_lints/src/box_default.rs | 3 +- .../clippy_lints/src/casts/as_ptr_cast_mut.rs | 2 +- .../src/casts/cast_possible_truncation.rs | 32 +- .../clippy_lints/src/casts/cast_ptr_alignment.rs | 2 +- src/tools/clippy/clippy_lints/src/casts/mod.rs | 20 +- .../clippy_lints/src/cognitive_complexity.rs | 6 +- src/tools/clippy/clippy_lints/src/copy_iterator.rs | 2 +- .../clippy/clippy_lints/src/declared_lints.rs | 9 + src/tools/clippy/clippy_lints/src/default.rs | 2 +- .../clippy_lints/src/default_numeric_fallback.rs | 6 +- src/tools/clippy/clippy_lints/src/dereference.rs | 105 +- .../clippy/clippy_lints/src/derivable_impls.rs | 2 +- src/tools/clippy/clippy_lints/src/derive.rs | 11 +- src/tools/clippy/clippy_lints/src/doc.rs | 45 +- src/tools/clippy/clippy_lints/src/empty_enum.rs | 2 +- src/tools/clippy/clippy_lints/src/entry.rs | 12 +- src/tools/clippy/clippy_lints/src/enum_clike.rs | 2 +- src/tools/clippy/clippy_lints/src/enum_variants.rs | 4 +- src/tools/clippy/clippy_lints/src/escape.rs | 12 +- src/tools/clippy/clippy_lints/src/eta_reduction.rs | 8 +- .../clippy/clippy_lints/src/excessive_bools.rs | 6 +- .../clippy/clippy_lints/src/exhaustive_items.rs | 3 +- .../src/extra_unused_type_parameters.rs | 229 +++++ src/tools/clippy/clippy_lints/src/format_args.rs | 11 +- .../clippy_lints/src/from_raw_with_void_ptr.rs | 2 +- .../src/functions/impl_trait_in_params.rs | 50 + .../clippy_lints/src/functions/misnamed_getters.rs | 11 +- src/tools/clippy/clippy_lints/src/functions/mod.rs | 37 +- .../clippy/clippy_lints/src/functions/must_use.rs | 22 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 6 +- .../clippy/clippy_lints/src/functions/result.rs | 2 +- .../clippy/clippy_lints/src/future_not_send.rs | 9 +- .../clippy/clippy_lints/src/implicit_return.rs | 3 +- .../clippy_lints/src/implicit_saturating_sub.rs | 4 +- .../src/inconsistent_struct_constructor.rs | 4 +- .../clippy/clippy_lints/src/indexing_slicing.rs | 2 +- src/tools/clippy/clippy_lints/src/inherent_impl.rs | 3 +- .../clippy/clippy_lints/src/inherent_to_string.rs | 4 +- .../clippy/clippy_lints/src/instant_subtraction.rs | 2 +- .../src/iter_not_returning_iterator.rs | 4 +- .../clippy/clippy_lints/src/large_const_arrays.rs | 2 +- .../clippy/clippy_lints/src/large_enum_variant.rs | 2 +- .../clippy/clippy_lints/src/large_stack_arrays.rs | 2 +- src/tools/clippy/clippy_lints/src/len_zero.rs | 23 +- .../clippy/clippy_lints/src/let_underscore.rs | 54 +- src/tools/clippy/clippy_lints/src/lib.rs | 20 +- src/tools/clippy/clippy_lints/src/lifetimes.rs | 268 +++-- .../clippy_lints/src/literal_representation.rs | 12 +- .../clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- src/tools/clippy/clippy_lints/src/loops/mod.rs | 3 +- .../clippy_lints/src/loops/needless_range_loop.rs | 6 +- .../clippy/clippy_lints/src/loops/never_loop.rs | 61 +- src/tools/clippy/clippy_lints/src/manual_assert.rs | 97 +- .../clippy/clippy_lints/src/manual_async_fn.rs | 5 +- .../clippy_lints/src/manual_is_ascii_check.rs | 2 +- .../clippy/clippy_lints/src/manual_let_else.rs | 153 ++- .../clippy_lints/src/manual_non_exhaustive.rs | 5 +- src/tools/clippy/clippy_lints/src/map_unit_fn.rs | 2 +- .../clippy_lints/src/matches/match_wild_enum.rs | 13 +- .../src/matches/rest_pat_in_fully_bound_struct.rs | 2 +- .../src/matches/significant_drop_in_scrutinee.rs | 2 +- .../clippy_lints/src/methods/bytes_count_to_len.rs | 2 +- .../clippy/clippy_lints/src/methods/bytes_nth.rs | 42 +- .../case_sensitive_file_extension_comparisons.rs | 2 +- .../src/methods/collapsible_str_replace.rs | 2 +- .../clippy_lints/src/methods/expect_fun_call.rs | 8 +- .../clippy/clippy_lints/src/methods/expect_used.rs | 4 +- .../clippy/clippy_lints/src/methods/get_first.rs | 2 +- .../clippy_lints/src/methods/implicit_clone.rs | 4 +- .../clippy_lints/src/methods/manual_ok_or.rs | 2 +- .../clippy/clippy_lints/src/methods/map_clone.rs | 2 +- .../clippy_lints/src/methods/map_err_ignore.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/mod.rs | 44 +- .../clippy_lints/src/methods/mut_mutex_lock.rs | 2 +- .../clippy_lints/src/methods/needless_collect.rs | 6 +- .../clippy_lints/src/methods/open_options.rs | 2 +- .../src/methods/path_buf_push_overwrite.rs | 2 +- .../clippy_lints/src/methods/search_is_some.rs | 3 +- .../src/methods/single_char_pattern.rs | 2 +- .../src/methods/stable_sort_primitive.rs | 2 +- .../src/methods/string_extend_chars.rs | 3 +- .../src/methods/suspicious_command_arg_space.rs | 39 + .../clippy_lints/src/methods/suspicious_map.rs | 6 +- .../clippy_lints/src/methods/suspicious_splitn.rs | 2 +- .../src/methods/suspicious_to_owned.rs | 22 +- .../src/methods/unnecessary_sort_by.rs | 2 +- .../src/methods/unnecessary_to_owned.rs | 13 +- .../clippy/clippy_lints/src/methods/unwrap_used.rs | 4 +- src/tools/clippy/clippy_lints/src/methods/utils.rs | 4 +- .../clippy_lints/src/methods/vec_resize_to_zero.rs | 2 +- src/tools/clippy/clippy_lints/src/misc.rs | 7 +- .../clippy_lints/src/missing_const_for_fn.rs | 9 +- src/tools/clippy/clippy_lints/src/missing_doc.rs | 32 +- .../clippy_lints/src/missing_trait_methods.rs | 2 +- src/tools/clippy/clippy_lints/src/module_style.rs | 2 +- .../src/multiple_unsafe_ops_per_block.rs | 186 ++++ src/tools/clippy/clippy_lints/src/mut_key.rs | 17 +- src/tools/clippy/clippy_lints/src/mut_reference.rs | 2 +- .../clippy_lints/src/needless_pass_by_value.rs | 13 +- .../clippy/clippy_lints/src/new_without_default.rs | 14 +- .../clippy_lints/src/no_mangle_with_rust_abi.rs | 65 ++ .../clippy/clippy_lints/src/non_copy_const.rs | 2 +- .../clippy_lints/src/only_used_in_recursion.rs | 4 +- .../src/operators/arithmetic_side_effects.rs | 36 +- .../clippy/clippy_lints/src/operators/cmp_owned.rs | 6 +- .../src/operators/numeric_arithmetic.rs | 2 +- .../clippy/clippy_lints/src/option_if_let_else.rs | 10 +- .../clippy/clippy_lints/src/panic_in_result_fn.rs | 9 +- .../clippy_lints/src/pass_by_ref_or_value.rs | 9 +- .../clippy_lints/src/pattern_type_mismatch.rs | 7 +- src/tools/clippy/clippy_lints/src/ptr.rs | 13 +- .../clippy/clippy_lints/src/question_mark_used.rs | 52 + .../clippy/clippy_lints/src/redundant_clone.rs | 9 +- .../clippy/clippy_lints/src/redundant_slicing.rs | 4 +- src/tools/clippy/clippy_lints/src/regex.rs | 89 +- .../clippy_lints/src/return_self_not_must_use.rs | 18 +- src/tools/clippy/clippy_lints/src/returns.rs | 142 +-- .../clippy/clippy_lints/src/same_name_method.rs | 2 +- .../clippy_lints/src/self_named_constructors.rs | 4 +- .../clippy/clippy_lints/src/semicolon_block.rs | 4 +- .../src/significant_drop_tightening.rs | 423 ++++++++ src/tools/clippy/clippy_lints/src/strings.rs | 4 +- .../clippy_lints/src/suspicious_xor_used_as_pow.rs | 4 +- src/tools/clippy/clippy_lints/src/swap.rs | 107 +- .../clippy_lints/src/trailing_empty_array.rs | 5 +- src/tools/clippy/clippy_lints/src/transmute/mod.rs | 35 +- .../src/transmute/transmute_int_to_non_zero.rs | 61 ++ .../src/transmute/transmute_ptr_to_ref.rs | 2 +- .../src/transmute/transmute_ref_to_ref.rs | 3 +- .../src/transmute/transmute_undefined_repr.rs | 2 +- .../transmutes_expressible_as_ptr_casts.rs | 58 +- .../src/transmute/useless_transmute.rs | 2 +- .../clippy/clippy_lints/src/transmute/utils.rs | 26 +- src/tools/clippy/clippy_lints/src/types/mod.rs | 35 +- .../clippy_lints/src/types/redundant_allocation.rs | 2 +- src/tools/clippy/clippy_lints/src/types/vec_box.rs | 2 +- .../clippy_lints/src/undocumented_unsafe_blocks.rs | 12 + .../clippy_lints/src/unit_return_expecting_ord.rs | 2 +- .../clippy_lints/src/unit_types/let_unit_value.rs | 2 +- .../src/unnecessary_owned_empty_strings.rs | 3 +- .../clippy/clippy_lints/src/unnecessary_wraps.rs | 26 +- .../clippy_lints/src/unnested_or_patterns.rs | 50 +- src/tools/clippy/clippy_lints/src/unused_async.rs | 7 +- .../clippy/clippy_lints/src/unused_io_amount.rs | 2 +- src/tools/clippy/clippy_lints/src/unwrap.rs | 3 +- .../clippy/clippy_lints/src/unwrap_in_result.rs | 4 +- src/tools/clippy/clippy_lints/src/use_self.rs | 8 +- .../clippy/clippy_lints/src/useless_conversion.rs | 2 +- src/tools/clippy/clippy_lints/src/utils/author.rs | 2 +- src/tools/clippy/clippy_lints/src/utils/conf.rs | 26 +- .../clippy/clippy_lints/src/utils/dump_hir.rs | 13 + .../internal_lints/interning_defined_symbol.rs | 2 +- .../utils/internal_lints/lint_without_lint_pass.rs | 15 +- .../src/utils/internal_lints/metadata_collector.rs | 79 +- .../src/utils/internal_lints/msrv_attr_impl.rs | 1 + .../utils/internal_lints/unnecessary_def_path.rs | 8 +- .../clippy_lints/src/zero_sized_map_values.rs | 2 +- src/tools/clippy/clippy_utils/Cargo.toml | 2 +- src/tools/clippy/clippy_utils/src/ast_utils.rs | 3 +- src/tools/clippy/clippy_utils/src/consts.rs | 10 +- src/tools/clippy/clippy_utils/src/eager_or_lazy.rs | 8 +- src/tools/clippy/clippy_utils/src/higher.rs | 13 +- src/tools/clippy/clippy_utils/src/hir_utils.rs | 4 +- src/tools/clippy/clippy_utils/src/lib.rs | 30 +- src/tools/clippy/clippy_utils/src/macros.rs | 76 +- .../clippy_utils/src/mir/possible_borrower.rs | 6 +- .../clippy/clippy_utils/src/numeric_literal.rs | 2 +- src/tools/clippy/clippy_utils/src/paths.rs | 1 + .../clippy_utils/src/qualify_min_const_fn.rs | 7 +- src/tools/clippy/clippy_utils/src/sugg.rs | 22 +- src/tools/clippy/clippy_utils/src/ty.rs | 56 +- src/tools/clippy/clippy_utils/src/visitors.rs | 10 +- src/tools/clippy/declare_clippy_lint/Cargo.toml | 2 +- src/tools/clippy/lintcheck/Cargo.toml | 4 +- src/tools/clippy/lintcheck/src/config.rs | 2 +- src/tools/clippy/lintcheck/src/main.rs | 12 +- src/tools/clippy/rust-toolchain | 2 +- src/tools/clippy/src/driver.rs | 3 +- src/tools/clippy/src/main.rs | 2 +- .../clippy/tests/ui-internal/custom_ice_message.rs | 3 +- .../tests/ui-internal/custom_ice_message.stderr | 4 +- .../tests/ui-toml/expect_used/expect_used.rs | 12 + .../ui-toml/pub_crate_missing_docs/clippy.toml | 1 + .../pub_crate_missing_doc.rs | 59 ++ .../pub_crate_missing_doc.stderr | 52 + .../toml_unknown_key/conf_unknown_key.stderr | 1 + .../tests/ui-toml/unwrap_used/unwrap_used.rs | 6 + .../tests/ui-toml/unwrap_used/unwrap_used.stderr | 10 +- .../clippy/tests/ui/arithmetic_side_effects.rs | 27 + .../clippy/tests/ui/arithmetic_side_effects.stderr | 198 ++-- src/tools/clippy/tests/ui/auxiliary/macro_rules.rs | 19 + .../clippy/tests/ui/bool_assert_comparison.fixed | 171 ++++ .../clippy/tests/ui/bool_assert_comparison.rs | 53 +- .../clippy/tests/ui/bool_assert_comparison.stderr | 353 ++++++- src/tools/clippy/tests/ui/box_default.fixed | 18 + src/tools/clippy/tests/ui/box_default.rs | 18 + src/tools/clippy/tests/ui/box_default.stderr | 14 +- src/tools/clippy/tests/ui/bytes_nth.fixed | 6 +- src/tools/clippy/tests/ui/bytes_nth.rs | 2 +- src/tools/clippy/tests/ui/bytes_nth.stderr | 10 +- src/tools/clippy/tests/ui/cast.rs | 1 + src/tools/clippy/tests/ui/cast.stderr | 163 ++- src/tools/clippy/tests/ui/cast_size.stderr | 53 + src/tools/clippy/tests/ui/crashes/ice-2774.stderr | 5 + .../clippy/tests/ui/crashes/ice-rust-107877.rs | 17 + .../crashes/needless_lifetimes_impl_trait.stderr | 5 + .../crashes/needless_pass_by_value-w-late-bound.rs | 9 + .../needless_pass_by_value-w-late-bound.stderr | 15 + src/tools/clippy/tests/ui/doc/doc-fixable.fixed | 2 +- src/tools/clippy/tests/ui/doc/doc-fixable.rs | 4 +- src/tools/clippy/tests/ui/doc/doc-fixable.stderr | 24 +- src/tools/clippy/tests/ui/entry.fixed | 14 + src/tools/clippy/tests/ui/entry.rs | 14 + .../clippy/tests/ui/explicit_auto_deref.fixed | 14 + src/tools/clippy/tests/ui/explicit_auto_deref.rs | 14 + .../tests/ui/extra_unused_type_parameters.rs | 110 ++ .../tests/ui/extra_unused_type_parameters.stderr | 75 ++ src/tools/clippy/tests/ui/format.fixed | 6 + src/tools/clippy/tests/ui/format.rs | 6 + src/tools/clippy/tests/ui/format.stderr | 30 +- src/tools/clippy/tests/ui/impl_trait_in_params.rs | 17 + .../clippy/tests/ui/impl_trait_in_params.stderr | 25 + src/tools/clippy/tests/ui/large_digit_groups.fixed | 4 +- .../clippy/tests/ui/large_digit_groups.stderr | 20 +- src/tools/clippy/tests/ui/len_without_is_empty.rs | 46 + .../clippy/tests/ui/let_underscore_untyped.rs | 54 + .../clippy/tests/ui/let_underscore_untyped.stderr | 51 + src/tools/clippy/tests/ui/literals.stderr | 10 +- .../tests/ui/manual_assert.edition2018.fixed | 35 +- .../tests/ui/manual_assert.edition2018.stderr | 67 +- src/tools/clippy/tests/ui/manual_let_else.rs | 11 + src/tools/clippy/tests/ui/manual_let_else_match.rs | 29 +- .../clippy/tests/ui/manual_let_else_match.stderr | 15 +- .../clippy/tests/ui/map_flatten_fixable.fixed | 1 + src/tools/clippy/tests/ui/map_flatten_fixable.rs | 1 + .../clippy/tests/ui/map_flatten_fixable.stderr | 18 +- .../ui/match_wildcard_for_single_variants.fixed | 2 +- .../ui/match_wildcard_for_single_variants.stderr | 8 +- src/tools/clippy/tests/ui/methods.rs | 1 + src/tools/clippy/tests/ui/methods.stderr | 4 +- src/tools/clippy/tests/ui/missing_doc_impl.stderr | 4 +- .../clippy/tests/ui/module_name_repetitions.stderr | 20 +- .../tests/ui/multiple_unsafe_ops_per_block.rs | 119 +++ .../tests/ui/multiple_unsafe_ops_per_block.stderr | 129 +++ .../clippy/tests/ui/must_use_candidates.fixed | 2 +- src/tools/clippy/tests/ui/must_use_candidates.rs | 2 +- src/tools/clippy/tests/ui/needless_lifetimes.fixed | 548 ++++++++++ src/tools/clippy/tests/ui/needless_lifetimes.rs | 52 +- .../clippy/tests/ui/needless_lifetimes.stderr | 391 ++++++-- .../clippy/tests/ui/needless_range_loop.stderr | 8 +- src/tools/clippy/tests/ui/needless_return.fixed | 20 + src/tools/clippy/tests/ui/needless_return.rs | 20 + src/tools/clippy/tests/ui/needless_return.stderr | 126 ++- src/tools/clippy/tests/ui/never_loop.rs | 45 + src/tools/clippy/tests/ui/never_loop.stderr | 15 +- src/tools/clippy/tests/ui/new_without_default.rs | 7 +- .../clippy/tests/ui/new_without_default.stderr | 14 +- .../clippy/tests/ui/no_mangle_with_rust_abi.fixed | 48 + .../clippy/tests/ui/no_mangle_with_rust_abi.rs | 48 + .../clippy/tests/ui/no_mangle_with_rust_abi.stderr | 45 + src/tools/clippy/tests/ui/question_mark_used.rs | 15 + .../clippy/tests/ui/question_mark_used.stderr | 11 + .../clippy/tests/ui/redundant_field_names.fixed | 2 +- src/tools/clippy/tests/ui/redundant_field_names.rs | 2 +- src/tools/clippy/tests/ui/regex.rs | 4 + src/tools/clippy/tests/ui/regex.stderr | 66 +- .../tests/ui/seek_to_start_instead_of_rewind.fixed | 4 +- .../tests/ui/seek_to_start_instead_of_rewind.rs | 4 +- .../tests/ui/significant_drop_tightening.fixed | 84 ++ .../clippy/tests/ui/significant_drop_tightening.rs | 80 ++ .../tests/ui/significant_drop_tightening.stderr | 94 ++ .../tests/ui/suspicious_command_arg_space.rs | 10 + .../tests/ui/suspicious_command_arg_space.stderr | 25 + .../clippy/tests/ui/suspicious_to_owned.stderr | 43 +- src/tools/clippy/tests/ui/swap.fixed | 24 +- src/tools/clippy/tests/ui/swap.rs | 29 +- src/tools/clippy/tests/ui/swap.stderr | 65 +- src/tools/clippy/tests/ui/track-diagnostics.stderr | 2 +- .../clippy/tests/ui/transmute_int_to_non_zero.rs | 41 + .../tests/ui/transmute_int_to_non_zero.stderr | 64 ++ .../ui/transmutes_expressible_as_ptr_casts.fixed | 2 + .../ui/transmutes_expressible_as_ptr_casts.rs | 2 + .../ui/transmutes_expressible_as_ptr_casts.stderr | 10 +- .../clippy/tests/ui/type_repetition_in_bounds.rs | 1 + .../tests/ui/type_repetition_in_bounds.stderr | 8 +- .../clippy/tests/ui/uninlined_format_args.fixed | 4 + src/tools/clippy/tests/ui/uninlined_format_args.rs | 4 + .../clippy/tests/ui/unnecessary_safety_comment.rs | 17 + src/tools/clippy/tests/ui/unreadable_literal.fixed | 2 +- .../clippy/tests/ui/unreadable_literal.stderr | 10 +- src/tools/clippy/tests/ui/unused_io_amount.rs | 8 + src/tools/clippy/tests/ui/unused_io_amount.stderr | 44 +- .../clippy/tests/ui/wildcard_enum_match_arm.fixed | 2 +- .../clippy/tests/ui/wildcard_enum_match_arm.stderr | 4 +- src/tools/compiletest/Cargo.toml | 5 +- src/tools/compiletest/src/common.rs | 6 + src/tools/compiletest/src/errors.rs | 8 +- src/tools/compiletest/src/header.rs | 87 +- src/tools/compiletest/src/main.rs | 57 +- src/tools/compiletest/src/runtest.rs | 87 +- src/tools/compiletest/src/util.rs | 37 +- src/tools/error_index_generator/Cargo.toml | 1 + src/tools/error_index_generator/main.rs | 58 +- src/tools/expand-yaml-anchors/src/main.rs | 2 +- src/tools/jsondoclint/src/validator.rs | 28 +- src/tools/publish_toolstate.py | 61 -- src/tools/rust-analyzer/Cargo.lock | 430 ++++---- src/tools/rust-analyzer/Cargo.toml | 44 + src/tools/rust-analyzer/crates/base-db/Cargo.toml | 23 +- .../rust-analyzer/crates/base-db/src/fixture.rs | 86 +- .../rust-analyzer/crates/base-db/src/input.rs | 36 +- src/tools/rust-analyzer/crates/base-db/src/lib.rs | 1 + src/tools/rust-analyzer/crates/cfg/Cargo.toml | 21 +- src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs | 4 +- src/tools/rust-analyzer/crates/flycheck/Cargo.toml | 17 +- src/tools/rust-analyzer/crates/hir-def/Cargo.toml | 37 +- src/tools/rust-analyzer/crates/hir-def/src/adt.rs | 9 +- src/tools/rust-analyzer/crates/hir-def/src/attr.rs | 360 +------ src/tools/rust-analyzer/crates/hir-def/src/body.rs | 148 ++- .../rust-analyzer/crates/hir-def/src/body/lower.rs | 12 +- .../crates/hir-def/src/body/pretty.rs | 4 +- .../rust-analyzer/crates/hir-def/src/body/tests.rs | 13 + .../crates/hir-def/src/child_by_source.rs | 2 +- src/tools/rust-analyzer/crates/hir-def/src/data.rs | 4 +- src/tools/rust-analyzer/crates/hir-def/src/db.rs | 8 +- src/tools/rust-analyzer/crates/hir-def/src/expr.rs | 2 +- .../rust-analyzer/crates/hir-def/src/find_path.rs | 8 +- .../rust-analyzer/crates/hir-def/src/generics.rs | 2 +- .../rust-analyzer/crates/hir-def/src/intern.rs | 227 ----- .../rust-analyzer/crates/hir-def/src/item_scope.rs | 6 +- .../rust-analyzer/crates/hir-def/src/item_tree.rs | 20 +- .../crates/hir-def/src/item_tree/lower.rs | 33 +- .../crates/hir-def/src/item_tree/pretty.rs | 1 - src/tools/rust-analyzer/crates/hir-def/src/keys.rs | 3 +- .../rust-analyzer/crates/hir-def/src/lang_item.rs | 298 +++++- .../rust-analyzer/crates/hir-def/src/layout.rs | 1 + src/tools/rust-analyzer/crates/hir-def/src/lib.rs | 49 +- .../crates/hir-def/src/macro_expansion_tests.rs | 12 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 37 +- .../src/macro_expansion_tests/mbe/regression.rs | 21 +- .../src/macro_expansion_tests/proc_macros.rs | 7 +- .../crates/hir-def/src/nameres/attr_resolution.rs | 3 +- .../crates/hir-def/src/nameres/collector.rs | 52 +- .../crates/hir-def/src/nameres/diagnostics.rs | 11 +- .../crates/hir-def/src/nameres/mod_resolution.rs | 2 +- .../crates/hir-def/src/nameres/proc_macro.rs | 2 +- .../crates/hir-def/src/nameres/tests.rs | 8 +- .../crates/hir-def/src/nameres/tests/macros.rs | 6 +- src/tools/rust-analyzer/crates/hir-def/src/path.rs | 64 +- .../rust-analyzer/crates/hir-def/src/path/lower.rs | 87 +- .../rust-analyzer/crates/hir-def/src/pretty.rs | 4 +- .../rust-analyzer/crates/hir-def/src/resolver.rs | 2 +- .../rust-analyzer/crates/hir-def/src/type_ref.rs | 10 +- .../rust-analyzer/crates/hir-expand/Cargo.toml | 28 +- .../rust-analyzer/crates/hir-expand/src/attrs.rs | 349 +++++++ .../crates/hir-expand/src/builtin_attr_macro.rs | 6 +- .../crates/hir-expand/src/builtin_derive_macro.rs | 10 +- .../crates/hir-expand/src/builtin_fn_macro.rs | 66 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 25 +- .../rust-analyzer/crates/hir-expand/src/eager.rs | 6 +- .../rust-analyzer/crates/hir-expand/src/fixup.rs | 18 +- .../rust-analyzer/crates/hir-expand/src/hygiene.rs | 4 +- .../rust-analyzer/crates/hir-expand/src/lib.rs | 42 +- .../rust-analyzer/crates/hir-expand/src/name.rs | 18 +- .../crates/hir-expand/src/proc_macro.rs | 24 +- .../rust-analyzer/crates/hir-expand/src/quote.rs | 112 ++- src/tools/rust-analyzer/crates/hir-ty/Cargo.toml | 33 +- .../rust-analyzer/crates/hir-ty/src/autoderef.rs | 9 +- .../rust-analyzer/crates/hir-ty/src/builder.rs | 17 +- .../rust-analyzer/crates/hir-ty/src/chalk_db.rs | 83 +- .../rust-analyzer/crates/hir-ty/src/chalk_ext.rs | 36 +- src/tools/rust-analyzer/crates/hir-ty/src/db.rs | 2 +- .../hir-ty/src/diagnostics/decl_check/case_conv.rs | 2 + .../crates/hir-ty/src/diagnostics/expr.rs | 41 +- .../rust-analyzer/crates/hir-ty/src/display.rs | 236 +++-- src/tools/rust-analyzer/crates/hir-ty/src/infer.rs | 122 +-- .../crates/hir-ty/src/infer/closure.rs | 4 +- .../crates/hir-ty/src/infer/coerce.rs | 15 +- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 378 ++++--- .../rust-analyzer/crates/hir-ty/src/infer/path.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer/unify.rs | 25 +- .../rust-analyzer/crates/hir-ty/src/interner.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/lang_items.rs | 11 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 53 +- .../rust-analyzer/crates/hir-ty/src/layout/adt.rs | 4 +- .../crates/hir-ty/src/layout/target.rs | 40 +- .../crates/hir-ty/src/layout/tests.rs | 115 ++- src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 81 +- src/tools/rust-analyzer/crates/hir-ty/src/lower.rs | 145 ++- .../crates/hir-ty/src/method_resolution.rs | 112 ++- .../crates/hir-ty/src/tests/method_resolution.rs | 16 +- .../crates/hir-ty/src/tests/simple.rs | 83 ++ .../crates/hir-ty/src/tests/traits.rs | 337 +++---- .../rust-analyzer/crates/hir-ty/src/traits.rs | 18 +- src/tools/rust-analyzer/crates/hir-ty/src/utils.rs | 185 ++-- src/tools/rust-analyzer/crates/hir-ty/src/walk.rs | 147 --- src/tools/rust-analyzer/crates/hir/Cargo.toml | 31 +- src/tools/rust-analyzer/crates/hir/src/display.rs | 7 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 186 +++- .../rust-analyzer/crates/hir/src/semantics.rs | 16 +- .../crates/hir/src/semantics/source_to_def.rs | 3 +- .../crates/hir/src/source_analyzer.rs | 48 +- .../rust-analyzer/crates/ide-assists/Cargo.toml | 29 +- .../crates/ide-assists/src/handlers/add_braces.rs | 155 +++ .../src/handlers/add_missing_impl_members.rs | 92 +- .../src/handlers/add_missing_match_arms.rs | 201 +++- .../src/handlers/convert_comment_block.rs | 4 +- .../src/handlers/convert_match_to_let_else.rs | 87 +- .../convert_named_struct_to_tuple_struct.rs | 5 +- .../convert_tuple_struct_to_named_struct.rs | 5 +- .../src/handlers/desugar_doc_comment.rs | 312 ++++++ .../ide-assists/src/handlers/extract_type_alias.rs | 38 +- .../src/handlers/generate_default_from_new.rs | 6 +- .../src/handlers/generate_delegate_methods.rs | 2 +- .../ide-assists/src/handlers/generate_function.rs | 901 ++++++++++++++++- .../ide-assists/src/handlers/generate_getter.rs | 107 +- .../ide-assists/src/handlers/inline_macro.rs | 26 +- .../ide-assists/src/handlers/merge_imports.rs | 2 +- .../ide-assists/src/handlers/move_const_to_impl.rs | 44 +- .../crates/ide-assists/src/handlers/raw_string.rs | 23 +- .../ide-assists/src/handlers/reorder_fields.rs | 5 +- .../ide-assists/src/handlers/replace_arith_op.rs | 2 +- .../handlers/replace_derive_with_manual_impl.rs | 2 +- .../src/handlers/replace_if_let_with_match.rs | 183 +++- .../ide-assists/src/handlers/unmerge_match_arm.rs | 3 +- .../ide-assists/src/handlers/unwrap_block.rs | 162 ++- .../rust-analyzer/crates/ide-assists/src/lib.rs | 4 + .../rust-analyzer/crates/ide-assists/src/tests.rs | 42 +- .../crates/ide-assists/src/tests/generated.rs | 40 + .../rust-analyzer/crates/ide-assists/src/utils.rs | 38 +- .../ide-assists/src/utils/gen_trait_fn_body.rs | 36 +- .../rust-analyzer/crates/ide-completion/Cargo.toml | 30 +- .../src/completions/item_list/trait_impl.rs | 2 +- .../ide-completion/src/completions/keyword.rs | 3 + .../ide-completion/src/completions/postfix.rs | 163 ++- .../crates/ide-completion/src/config.rs | 1 + .../crates/ide-completion/src/context.rs | 37 +- .../crates/ide-completion/src/context/analysis.rs | 175 ++-- .../crates/ide-completion/src/item.rs | 104 +- .../rust-analyzer/crates/ide-completion/src/lib.rs | 18 +- .../crates/ide-completion/src/render.rs | 29 +- .../crates/ide-completion/src/render/literal.rs | 2 +- .../ide-completion/src/render/union_literal.rs | 2 +- .../crates/ide-completion/src/tests.rs | 46 +- .../crates/ide-completion/src/tests/expression.rs | 252 +++++ .../crates/ide-completion/src/tests/flyimport.rs | 6 +- .../crates/ide-completion/src/tests/item_list.rs | 51 + .../crates/ide-completion/src/tests/proc_macros.rs | 114 ++- .../crates/ide-completion/src/tests/special.rs | 89 +- src/tools/rust-analyzer/crates/ide-db/Cargo.toml | 38 +- .../crates/ide-db/src/active_parameter.rs | 80 +- src/tools/rust-analyzer/crates/ide-db/src/defs.rs | 2 +- .../rust-analyzer/crates/ide-db/src/line_index.rs | 199 ++-- .../crates/ide-db/src/path_transform.rs | 24 +- .../rust-analyzer/crates/ide-db/src/rename.rs | 76 +- .../rust-analyzer/crates/ide-db/src/search.rs | 183 ++-- .../crates/ide-db/src/symbol_index.rs | 8 +- .../crates/ide-diagnostics/Cargo.toml | 28 +- .../src/handlers/json_is_not_rust.rs | 122 ++- .../src/handlers/private_assoc_item.rs | 5 +- .../ide-diagnostics/src/handlers/unlinked_file.rs | 230 ++++- .../src/handlers/unresolved_proc_macro.rs | 5 +- src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml | 25 +- src/tools/rust-analyzer/crates/ide/Cargo.toml | 39 +- .../rust-analyzer/crates/ide/src/file_structure.rs | 29 +- .../crates/ide/src/goto_declaration.rs | 1 + .../crates/ide/src/goto_definition.rs | 64 ++ .../crates/ide/src/highlight_related.rs | 2 - src/tools/rust-analyzer/crates/ide/src/hover.rs | 181 ++-- .../rust-analyzer/crates/ide/src/hover/render.rs | 163 +-- .../rust-analyzer/crates/ide/src/hover/tests.rs | 248 +++-- .../rust-analyzer/crates/ide/src/inlay_hints.rs | 191 ++-- .../crates/ide/src/inlay_hints/adjustment.rs | 135 ++- .../crates/ide/src/inlay_hints/bind_pat.rs | 137 ++- .../crates/ide/src/inlay_hints/binding_mode.rs | 55 +- .../crates/ide/src/inlay_hints/chaining.rs | 323 +++--- .../crates/ide/src/inlay_hints/closing_brace.rs | 14 +- .../crates/ide/src/inlay_hints/closure_ret.rs | 7 +- .../crates/ide/src/inlay_hints/discriminant.rs | 108 +- .../crates/ide/src/inlay_hints/fn_lifetime_fn.rs | 11 +- .../crates/ide/src/inlay_hints/implicit_static.rs | 5 +- .../crates/ide/src/inlay_hints/param_name.rs | 11 +- .../rust-analyzer/crates/ide/src/join_lines.rs | 6 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 7 +- .../crates/ide/src/markdown_remove.rs | 140 ++- .../rust-analyzer/crates/ide/src/references.rs | 395 ++++++++ src/tools/rust-analyzer/crates/ide/src/rename.rs | 202 +++- .../crates/ide/src/shuffle_crate_graph.rs | 22 +- .../rust-analyzer/crates/ide/src/signature_help.rs | 299 ++++-- .../rust-analyzer/crates/ide/src/static_index.rs | 7 +- .../crates/ide/src/syntax_highlighting.rs | 9 +- .../crates/ide/src/syntax_highlighting/tests.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/typing.rs | 57 +- src/tools/rust-analyzer/crates/intern/Cargo.toml | 20 + src/tools/rust-analyzer/crates/intern/src/lib.rs | 216 ++++ src/tools/rust-analyzer/crates/limit/Cargo.toml | 8 +- src/tools/rust-analyzer/crates/mbe/Cargo.toml | 21 +- .../rust-analyzer/crates/mbe/src/benchmark.rs | 30 +- src/tools/rust-analyzer/crates/mbe/src/expander.rs | 7 +- .../crates/mbe/src/expander/matcher.rs | 41 +- .../crates/mbe/src/expander/transcriber.rs | 74 +- src/tools/rust-analyzer/crates/mbe/src/lib.rs | 55 +- src/tools/rust-analyzer/crates/mbe/src/parser.rs | 22 +- .../rust-analyzer/crates/mbe/src/syntax_bridge.rs | 166 ++- .../crates/mbe/src/syntax_bridge/tests.rs | 4 +- .../crates/mbe/src/to_parser_input.rs | 36 +- src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs | 27 +- src/tools/rust-analyzer/crates/parser/Cargo.toml | 17 +- src/tools/rust-analyzer/crates/parser/src/event.rs | 13 +- .../rust-analyzer/crates/parser/src/grammar.rs | 30 + .../crates/parser/src/grammar/attributes.rs | 2 + .../crates/parser/src/grammar/expressions.rs | 137 ++- .../crates/parser/src/grammar/expressions/atom.rs | 38 +- .../crates/parser/src/grammar/generic_args.rs | 32 +- .../crates/parser/src/grammar/generic_params.rs | 29 +- .../crates/parser/src/grammar/items/adt.rs | 25 +- .../crates/parser/src/grammar/params.rs | 14 +- .../crates/parser/src/grammar/paths.rs | 11 +- .../crates/parser/src/grammar/types.rs | 3 +- .../rust-analyzer/crates/parser/src/lexed_str.rs | 5 + src/tools/rust-analyzer/crates/parser/src/lib.rs | 4 + .../rust-analyzer/crates/parser/src/output.rs | 61 +- .../rust-analyzer/crates/parser/src/parser.rs | 29 + .../rust-analyzer/crates/parser/src/shortcuts.rs | 53 + src/tools/rust-analyzer/crates/parser/src/tests.rs | 6 + .../crates/parser/src/tests/prefix_entries.rs | 4 + .../crates/parser/src/tests/top_entries.rs | 2 +- .../err/0009_broken_struct_type_parameter.rast | 3 +- .../test_data/parser/err/0013_invalid_type.rast | 38 +- .../test_data/parser/err/0022_bad_exprs.rast | 18 +- .../parser/err/0024_many_type_parens.rast | 211 ++-- .../parser/test_data/parser/err/0025_nope.rast | 7 +- .../test_data/parser/err/0042_weird_blocks.rast | 2 +- .../test_data/parser/err/0048_double_fish.rast | 19 +- .../inline/err/0002_misplaced_label_err.rast | 2 +- .../parser/inline/err/0015_arg_list_recovery.rast | 77 ++ .../parser/inline/err/0015_arg_list_recovery.rs | 5 + .../inline/err/0015_missing_fn_param_type.rast | 2 +- .../parser/inline/ok/0011_field_expr.rast | 33 + .../test_data/parser/inline/ok/0011_field_expr.rs | 2 + .../parser/inline/ok/0107_method_call_expr.rast | 43 + .../parser/inline/ok/0107_method_call_expr.rs | 2 + .../parser/inline/ok/0137_await_expr.rast | 35 + .../test_data/parser/inline/ok/0137_await_expr.rs | 2 + .../parser/inline/ok/0205_const_closure.rast | 42 + .../parser/inline/ok/0205_const_closure.rs | 1 + src/tools/rust-analyzer/crates/paths/Cargo.toml | 8 +- .../rust-analyzer/crates/proc-macro-api/Cargo.toml | 23 +- .../rust-analyzer/crates/proc-macro-api/src/lib.rs | 17 +- .../rust-analyzer/crates/proc-macro-api/src/msg.rs | 23 +- .../crates/proc-macro-api/src/msg/flat.rs | 43 +- .../crates/proc-macro-api/src/process.rs | 54 +- .../crates/proc-macro-api/src/version.rs | 13 +- .../crates/proc-macro-srv-cli/Cargo.toml | 10 +- .../rust-analyzer/crates/proc-macro-srv/Cargo.toml | 20 +- .../crates/proc-macro-srv/src/abis/abi_1_58/mod.rs | 104 -- .../src/abis/abi_1_58/proc_macro/bridge/buffer.rs | 143 --- .../src/abis/abi_1_58/proc_macro/bridge/client.rs | 485 --------- .../src/abis/abi_1_58/proc_macro/bridge/closure.rs | 24 - .../src/abis/abi_1_58/proc_macro/bridge/handle.rs | 70 -- .../src/abis/abi_1_58/proc_macro/bridge/mod.rs | 429 -------- .../src/abis/abi_1_58/proc_macro/bridge/rpc.rs | 305 ------ .../abis/abi_1_58/proc_macro/bridge/scoped_cell.rs | 81 -- .../src/abis/abi_1_58/proc_macro/bridge/server.rs | 352 ------- .../src/abis/abi_1_58/proc_macro/diagnostic.rs | 166 --- .../src/abis/abi_1_58/proc_macro/mod.rs | 1056 -------------------- .../src/abis/abi_1_58/proc_macro/quote.rs | 140 --- .../proc-macro-srv/src/abis/abi_1_58/ra_server.rs | 822 --------------- .../crates/proc-macro-srv/src/abis/abi_1_63/mod.rs | 1 + .../src/abis/abi_1_63/proc_macro/mod.rs | 24 +- .../proc-macro-srv/src/abis/abi_1_63/ra_server.rs | 119 +-- .../proc-macro-srv/src/abis/abi_sysroot/mod.rs | 2 +- .../src/abis/abi_sysroot/ra_server.rs | 65 +- .../src/abis/abi_sysroot/ra_server/token_stream.rs | 28 +- .../crates/proc-macro-srv/src/abis/mod.rs | 11 +- .../rust-analyzer/crates/proc-macro-srv/src/cli.rs | 3 + .../crates/proc-macro-srv/src/dylib.rs | 2 + .../rust-analyzer/crates/proc-macro-srv/src/lib.rs | 2 + .../crates/proc-macro-srv/src/tests/mod.rs | 26 +- .../crates/proc-macro-test/Cargo.toml | 15 +- .../crates/proc-macro-test/imp/Cargo.toml | 1 - src/tools/rust-analyzer/crates/profile/Cargo.toml | 10 +- src/tools/rust-analyzer/crates/profile/src/lib.rs | 2 +- .../rust-analyzer/crates/project-model/Cargo.toml | 21 +- .../crates/project-model/src/cargo_workspace.rs | 1 + .../rust-analyzer/crates/project-model/src/lib.rs | 2 +- .../crates/project-model/src/project_json.rs | 44 + .../crates/project-model/src/sysroot.rs | 34 +- .../crates/project-model/src/target_data_layout.rs | 13 +- .../crates/project-model/src/tests.rs | 128 ++- .../crates/project-model/src/workspace.rs | 230 +++-- .../rust-analyzer/crates/rust-analyzer/Cargo.toml | 58 +- .../crates/rust-analyzer/src/bin/main.rs | 37 +- .../rust-analyzer/crates/rust-analyzer/src/caps.rs | 25 +- .../crates/rust-analyzer/src/cli/analysis_stats.rs | 22 +- .../crates/rust-analyzer/src/cli/diagnostics.rs | 4 +- .../crates/rust-analyzer/src/cli/load_cargo.rs | 33 +- .../crates/rust-analyzer/src/cli/lsif.rs | 8 +- .../crates/rust-analyzer/src/cli/scip.rs | 7 +- .../crates/rust-analyzer/src/cli/ssr.rs | 6 +- .../crates/rust-analyzer/src/config.rs | 70 +- .../rust-analyzer/src/config/patch_old_style.rs | 42 +- .../rust-analyzer/src/diagnostics/to_proto.rs | 6 +- .../crates/rust-analyzer/src/from_proto.rs | 13 +- .../crates/rust-analyzer/src/global_state.rs | 82 +- .../crates/rust-analyzer/src/handlers.rs | 66 +- .../rust-analyzer/src/integrated_benchmarks.rs | 8 +- .../crates/rust-analyzer/src/line_index.rs | 5 +- .../crates/rust-analyzer/src/lsp_ext.rs | 41 +- .../crates/rust-analyzer/src/lsp_utils.rs | 88 +- .../crates/rust-analyzer/src/main_loop.rs | 61 +- .../crates/rust-analyzer/src/reload.rs | 46 +- .../crates/rust-analyzer/src/to_proto.rs | 277 ++--- .../crates/rust-analyzer/tests/slow-tests/main.rs | 4 +- .../rust-analyzer/tests/slow-tests/support.rs | 2 + .../crates/rust-analyzer/tests/slow-tests/tidy.rs | 27 - .../rust-analyzer/crates/sourcegen/Cargo.toml | 8 +- src/tools/rust-analyzer/crates/stdx/Cargo.toml | 8 +- src/tools/rust-analyzer/crates/stdx/src/lib.rs | 15 +- src/tools/rust-analyzer/crates/stdx/src/macros.rs | 9 + src/tools/rust-analyzer/crates/stdx/src/rand.rs | 21 + src/tools/rust-analyzer/crates/syntax/Cargo.toml | 26 +- .../rust-analyzer/crates/syntax/fuzz/Cargo.toml | 7 +- src/tools/rust-analyzer/crates/syntax/rust.ungram | 2 +- src/tools/rust-analyzer/crates/syntax/src/ast.rs | 30 + .../crates/syntax/src/ast/generated/nodes.rs | 1 + .../rust-analyzer/crates/syntax/src/ast/make.rs | 21 +- src/tools/rust-analyzer/crates/syntax/src/lib.rs | 2 +- .../rust-analyzer/crates/syntax/src/syntax_node.rs | 1 + src/tools/rust-analyzer/crates/syntax/src/utils.rs | 7 +- .../rust-analyzer/crates/test-utils/Cargo.toml | 12 +- .../crates/test-utils/src/minicore.rs | 44 +- .../rust-analyzer/crates/text-edit/Cargo.toml | 8 +- .../rust-analyzer/crates/toolchain/Cargo.toml | 8 +- src/tools/rust-analyzer/crates/tt/Cargo.toml | 10 +- src/tools/rust-analyzer/crates/tt/src/buffer.rs | 105 +- src/tools/rust-analyzer/crates/tt/src/lib.rs | 228 +++-- .../rust-analyzer/crates/vfs-notify/Cargo.toml | 12 +- src/tools/rust-analyzer/crates/vfs/Cargo.toml | 12 +- src/tools/rust-analyzer/crates/vfs/src/lib.rs | 3 +- src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs | 17 +- src/tools/rust-analyzer/docs/dev/architecture.md | 2 +- src/tools/rust-analyzer/docs/dev/lsp-extensions.md | 2 +- .../rust-analyzer/docs/user/generated_config.adoc | 18 +- src/tools/rust-analyzer/docs/user/manual.adoc | 119 ++- src/tools/rust-analyzer/lib/lsp-server/Cargo.toml | 2 +- .../rust-analyzer/lib/lsp-server/src/stdio.rs | 5 +- src/tools/rust-analyzer/xtask/Cargo.toml | 1 + src/tools/rust-analyzer/xtask/src/dist.rs | 9 +- src/tools/rust-analyzer/xtask/src/publish.rs | 2 +- .../rust-analyzer/xtask/src/release/changelog.rs | 4 +- src/tools/rust-installer/src/generator.rs | 16 +- src/tools/rustdoc-js/tester.js | 14 +- src/tools/rustfmt/.github/workflows/check_diff.yml | 33 + .../rustfmt/.github/workflows/integration.yml | 4 - src/tools/rustfmt/CHANGELOG.md | 28 +- src/tools/rustfmt/Cargo.lock | 4 +- src/tools/rustfmt/Cargo.toml | 4 +- src/tools/rustfmt/Configurations.md | 185 +++- src/tools/rustfmt/Processes.md | 2 +- src/tools/rustfmt/ci/build_and_test.bat | 1 + src/tools/rustfmt/ci/build_and_test.sh | 1 + src/tools/rustfmt/ci/check_diff.sh | 199 ++++ src/tools/rustfmt/ci/integration.sh | 18 +- src/tools/rustfmt/config_proc_macro/Cargo.lock | 2 +- src/tools/rustfmt/config_proc_macro/Cargo.toml | 2 +- src/tools/rustfmt/config_proc_macro/src/attrs.rs | 27 +- .../rustfmt/config_proc_macro/src/item_enum.rs | 40 +- src/tools/rustfmt/config_proc_macro/src/lib.rs | 13 + src/tools/rustfmt/config_proc_macro/tests/smoke.rs | 1 + src/tools/rustfmt/rust-toolchain | 4 +- src/tools/rustfmt/src/attr.rs | 4 +- src/tools/rustfmt/src/bin/main.rs | 2 +- src/tools/rustfmt/src/cargo-fmt/main.rs | 12 +- src/tools/rustfmt/src/cargo-fmt/test/mod.rs | 4 +- src/tools/rustfmt/src/chains.rs | 124 ++- src/tools/rustfmt/src/closures.rs | 3 +- src/tools/rustfmt/src/config/config_type.rs | 122 ++- src/tools/rustfmt/src/config/macro_names.rs | 118 +++ src/tools/rustfmt/src/config/mod.rs | 149 ++- src/tools/rustfmt/src/expr.rs | 14 +- src/tools/rustfmt/src/imports.rs | 4 +- src/tools/rustfmt/src/items.rs | 10 +- src/tools/rustfmt/src/lib.rs | 1 + src/tools/rustfmt/src/lists.rs | 16 +- src/tools/rustfmt/src/macros.rs | 26 +- src/tools/rustfmt/src/modules.rs | 13 +- src/tools/rustfmt/src/parse/parser.rs | 3 +- src/tools/rustfmt/src/parse/session.rs | 9 +- src/tools/rustfmt/src/skip.rs | 79 +- .../rustfmt/src/test/configuration_snippet.rs | 7 +- src/tools/rustfmt/src/test/mod.rs | 6 +- src/tools/rustfmt/src/types.rs | 44 +- src/tools/rustfmt/src/utils.rs | 6 +- src/tools/rustfmt/src/visitor.rs | 13 +- src/tools/rustfmt/tests/cargo-fmt/main.rs | 29 +- .../tests/cargo-fmt/source/issue_3164/Cargo.toml | 8 + .../tests/cargo-fmt/source/issue_3164/src/main.rs | 13 + src/tools/rustfmt/tests/config/small_tabs.toml | 2 +- .../issue-5198/lib/c/d/explanation.txt | 2 +- .../mod-resolver/issue-5198/lib/explanation.txt | 2 +- src/tools/rustfmt/tests/rustfmt/main.rs | 21 +- .../rustfmt/tests/source/cfg_if/detect/arch/x86.rs | 2 +- src/tools/rustfmt/tests/source/comments_unicode.rs | 140 +++ .../source/configs/fn_args_layout/compressed.rs | 16 - .../tests/source/configs/fn_args_layout/tall.rs | 16 - .../source/configs/fn_args_layout/vertical.rs | 16 - .../source/configs/fn_params_layout/compressed.rs | 16 + .../tests/source/configs/fn_params_layout/tall.rs | 16 + .../source/configs/fn_params_layout/vertical.rs | 16 + src/tools/rustfmt/tests/source/enum.rs | 2 +- src/tools/rustfmt/tests/source/fn-custom-7.rs | 2 +- src/tools/rustfmt/tests/source/fn-custom.rs | 2 +- .../tests/source/fn_args_layout-vertical.rs | 2 +- .../source/issue-3987/format_macro_bodies_true.rs | 26 + src/tools/rustfmt/tests/source/issue-4643.rs | 23 + src/tools/rustfmt/tests/source/issue-4689/one.rs | 149 +++ src/tools/rustfmt/tests/source/issue-4689/two.rs | 149 +++ src/tools/rustfmt/tests/source/issue_1306.rs | 29 + src/tools/rustfmt/tests/source/issue_3245.rs | 4 + src/tools/rustfmt/tests/source/issue_3561.rs | 6 + .../tests/source/skip_macro_invocations/all.rs | 11 + .../source/skip_macro_invocations/all_and_name.rs | 11 + .../tests/source/skip_macro_invocations/empty.rs | 11 + .../tests/source/skip_macro_invocations/name.rs | 11 + .../source/skip_macro_invocations/name_unknown.rs | 6 + .../tests/source/skip_macro_invocations/names.rs | 16 + .../path_qualified_invocation_mismatch.rs | 6 + .../skip_macro_invocations/path_qualified_match.rs | 6 + .../path_qualified_name_mismatch.rs | 6 + .../skip_macro_invocations/use_alias_examples.rs | 32 + src/tools/rustfmt/tests/source/tuple.rs | 2 +- ...omments_should_not_imply_format_doc_comments.rs | 2 +- .../rustfmt/tests/target/cfg_if/detect/arch/x86.rs | 2 +- src/tools/rustfmt/tests/target/comments_unicode.rs | 140 +++ .../target/configs/fn_args_layout/compressed.rs | 22 - .../tests/target/configs/fn_args_layout/tall.rs | 32 - .../target/configs/fn_args_layout/vertical.rs | 42 - .../target/configs/fn_params_layout/compressed.rs | 22 + .../tests/target/configs/fn_params_layout/tall.rs | 32 + .../target/configs/fn_params_layout/vertical.rs | 42 + src/tools/rustfmt/tests/target/enum.rs | 2 +- src/tools/rustfmt/tests/target/fn-custom-7.rs | 2 +- src/tools/rustfmt/tests/target/fn-custom.rs | 2 +- .../tests/target/fn_args_layout-vertical.rs | 2 +- .../issue-2534/format_macro_matchers_false.rs | 6 + .../issue-2534/format_macro_matchers_true.rs | 6 + .../target/issue-3987/format_macro_bodies_false.rs | 26 + .../target/issue-3987/format_macro_bodies_true.rs | 21 + src/tools/rustfmt/tests/target/issue-4643.rs | 19 + src/tools/rustfmt/tests/target/issue-4689/one.rs | 150 +++ src/tools/rustfmt/tests/target/issue-4689/two.rs | 152 +++ .../rustfmt/tests/target/issue-4791/issue_4928.rs | 2 +- src/tools/rustfmt/tests/target/issue-5358.rs | 4 + src/tools/rustfmt/tests/target/issue_1306.rs | 33 + src/tools/rustfmt/tests/target/issue_3033.rs | 2 + src/tools/rustfmt/tests/target/issue_3245.rs | 4 + src/tools/rustfmt/tests/target/issue_3561.rs | 7 + src/tools/rustfmt/tests/target/issue_4350.rs | 13 + src/tools/rustfmt/tests/target/issue_5668.rs | 8 + .../tests/target/skip_macro_invocations/all.rs | 11 + .../target/skip_macro_invocations/all_and_name.rs | 11 + .../tests/target/skip_macro_invocations/empty.rs | 11 + .../tests/target/skip_macro_invocations/name.rs | 11 + .../target/skip_macro_invocations/name_unknown.rs | 6 + .../tests/target/skip_macro_invocations/names.rs | 16 + .../path_qualified_invocation_mismatch.rs | 6 + .../skip_macro_invocations/path_qualified_match.rs | 6 + .../path_qualified_name_mismatch.rs | 6 + .../skip_macro_invocations/use_alias_examples.rs | 32 + src/tools/rustfmt/tests/target/tuple.rs | 2 +- ...omments_should_not_imply_format_doc_comments.rs | 2 +- src/tools/tidy/src/deps.rs | 9 +- src/tools/tidy/src/error_codes.rs | 26 +- src/tools/tidy/src/ui_tests.rs | 4 +- src/tools/x/Cargo.toml | 2 +- src/tools/x/src/main.rs | 74 +- src/version | 2 +- 1054 files changed, 29262 insertions(+), 17029 deletions(-) create mode 100644 src/bootstrap/setup/tests.rs delete mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-llvm-13-stage1/Dockerfile delete mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-llvm-13/Dockerfile create mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-llvm-14-stage1/Dockerfile create mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile create mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/Dockerfile delete mode 100755 src/ci/pgo.sh create mode 100644 src/ci/stage-build.py create mode 100644 src/doc/rustc-dev-guide/CITATION.cff create mode 100644 src/doc/rustc-dev-guide/examples/README create mode 100644 src/doc/rustc-dev-guide/src/solve/canonicalization.md create mode 100644 src/doc/rustc-dev-guide/src/solve/coinduction.md create mode 100644 src/doc/rustc-dev-guide/src/solve/the-solver.md create mode 100644 src/doc/rustc-dev-guide/src/solve/trait-solving.md create mode 100644 src/doc/unstable-book/src/compiler-flags/export-executable-symbols.md create mode 100644 src/doc/unstable-book/src/compiler-flags/instrument-xray.md create mode 100644 src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md delete mode 100644 src/doc/unstable-book/src/library-features/char-error-internals.md delete mode 100644 src/doc/unstable-book/src/library-features/int-error-internals.md create mode 100644 src/etc/vscode_settings.json delete mode 100644 src/librustdoc/html/static/images/down-arrow.svg delete mode 100644 src/librustdoc/html/static/images/toggle-minus.svg delete mode 100644 src/librustdoc/html/static/images/toggle-plus.svg delete mode 100644 src/librustdoc/passes/collect_intra_doc_links/early.rs create mode 100644 src/tools/clippy/book/src/lint_configuration.md create mode 100644 src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs create mode 100644 src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs create mode 100644 src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs create mode 100644 src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs create mode 100644 src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs create mode 100644 src/tools/clippy/clippy_lints/src/question_mark_used.rs create mode 100644 src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs create mode 100644 src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs create mode 100644 src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/clippy.toml create mode 100644 src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs create mode 100644 src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr create mode 100644 src/tools/clippy/tests/ui/bool_assert_comparison.fixed create mode 100644 src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs create mode 100644 src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.rs create mode 100644 src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr create mode 100644 src/tools/clippy/tests/ui/extra_unused_type_parameters.rs create mode 100644 src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr create mode 100644 src/tools/clippy/tests/ui/impl_trait_in_params.rs create mode 100644 src/tools/clippy/tests/ui/impl_trait_in_params.stderr create mode 100644 src/tools/clippy/tests/ui/let_underscore_untyped.rs create mode 100644 src/tools/clippy/tests/ui/let_underscore_untyped.stderr create mode 100644 src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs create mode 100644 src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr create mode 100644 src/tools/clippy/tests/ui/needless_lifetimes.fixed create mode 100644 src/tools/clippy/tests/ui/no_mangle_with_rust_abi.fixed create mode 100644 src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs create mode 100644 src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr create mode 100644 src/tools/clippy/tests/ui/question_mark_used.rs create mode 100644 src/tools/clippy/tests/ui/question_mark_used.stderr create mode 100644 src/tools/clippy/tests/ui/significant_drop_tightening.fixed create mode 100644 src/tools/clippy/tests/ui/significant_drop_tightening.rs create mode 100644 src/tools/clippy/tests/ui/significant_drop_tightening.stderr create mode 100644 src/tools/clippy/tests/ui/suspicious_command_arg_space.rs create mode 100644 src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr create mode 100644 src/tools/clippy/tests/ui/transmute_int_to_non_zero.rs create mode 100644 src/tools/clippy/tests/ui/transmute_int_to_non_zero.stderr delete mode 100644 src/tools/rust-analyzer/crates/hir-def/src/intern.rs create mode 100644 src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/walk.rs create mode 100644 src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs create mode 100644 src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs create mode 100644 src/tools/rust-analyzer/crates/intern/Cargo.toml create mode 100644 src/tools/rust-analyzer/crates/intern/src/lib.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0205_const_closure.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0205_const_closure.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/mod.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/bridge/buffer.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/bridge/client.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/bridge/closure.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/bridge/handle.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/bridge/mod.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/bridge/rpc.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/bridge/scoped_cell.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/bridge/server.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/diagnostic.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/mod.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/quote.rs delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_58/ra_server.rs create mode 100644 src/tools/rust-analyzer/crates/stdx/src/rand.rs create mode 100644 src/tools/rustfmt/.github/workflows/check_diff.yml create mode 100755 src/tools/rustfmt/ci/check_diff.sh create mode 100644 src/tools/rustfmt/src/config/macro_names.rs create mode 100644 src/tools/rustfmt/tests/cargo-fmt/source/issue_3164/Cargo.toml create mode 100644 src/tools/rustfmt/tests/cargo-fmt/source/issue_3164/src/main.rs create mode 100644 src/tools/rustfmt/tests/source/comments_unicode.rs delete mode 100644 src/tools/rustfmt/tests/source/configs/fn_args_layout/compressed.rs delete mode 100644 src/tools/rustfmt/tests/source/configs/fn_args_layout/tall.rs delete mode 100644 src/tools/rustfmt/tests/source/configs/fn_args_layout/vertical.rs create mode 100644 src/tools/rustfmt/tests/source/configs/fn_params_layout/compressed.rs create mode 100644 src/tools/rustfmt/tests/source/configs/fn_params_layout/tall.rs create mode 100644 src/tools/rustfmt/tests/source/configs/fn_params_layout/vertical.rs create mode 100644 src/tools/rustfmt/tests/source/issue-3987/format_macro_bodies_true.rs create mode 100644 src/tools/rustfmt/tests/source/issue-4643.rs create mode 100644 src/tools/rustfmt/tests/source/issue-4689/one.rs create mode 100644 src/tools/rustfmt/tests/source/issue-4689/two.rs create mode 100644 src/tools/rustfmt/tests/source/issue_1306.rs create mode 100644 src/tools/rustfmt/tests/source/issue_3245.rs create mode 100644 src/tools/rustfmt/tests/source/issue_3561.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/all.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/all_and_name.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/empty.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/name.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/name_unknown.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/names.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/path_qualified_invocation_mismatch.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/path_qualified_match.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/path_qualified_name_mismatch.rs create mode 100644 src/tools/rustfmt/tests/source/skip_macro_invocations/use_alias_examples.rs create mode 100644 src/tools/rustfmt/tests/target/comments_unicode.rs delete mode 100644 src/tools/rustfmt/tests/target/configs/fn_args_layout/compressed.rs delete mode 100644 src/tools/rustfmt/tests/target/configs/fn_args_layout/tall.rs delete mode 100644 src/tools/rustfmt/tests/target/configs/fn_args_layout/vertical.rs create mode 100644 src/tools/rustfmt/tests/target/configs/fn_params_layout/compressed.rs create mode 100644 src/tools/rustfmt/tests/target/configs/fn_params_layout/tall.rs create mode 100644 src/tools/rustfmt/tests/target/configs/fn_params_layout/vertical.rs create mode 100644 src/tools/rustfmt/tests/target/issue-2534/format_macro_matchers_false.rs create mode 100644 src/tools/rustfmt/tests/target/issue-2534/format_macro_matchers_true.rs create mode 100644 src/tools/rustfmt/tests/target/issue-3987/format_macro_bodies_false.rs create mode 100644 src/tools/rustfmt/tests/target/issue-3987/format_macro_bodies_true.rs create mode 100644 src/tools/rustfmt/tests/target/issue-4643.rs create mode 100644 src/tools/rustfmt/tests/target/issue-4689/one.rs create mode 100644 src/tools/rustfmt/tests/target/issue-4689/two.rs create mode 100644 src/tools/rustfmt/tests/target/issue-5358.rs create mode 100644 src/tools/rustfmt/tests/target/issue_1306.rs create mode 100644 src/tools/rustfmt/tests/target/issue_3033.rs create mode 100644 src/tools/rustfmt/tests/target/issue_3245.rs create mode 100644 src/tools/rustfmt/tests/target/issue_3561.rs create mode 100644 src/tools/rustfmt/tests/target/issue_4350.rs create mode 100644 src/tools/rustfmt/tests/target/issue_5668.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/all.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/all_and_name.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/empty.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/name.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/name_unknown.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/names.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/path_qualified_invocation_mismatch.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/path_qualified_match.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/path_qualified_name_mismatch.rs create mode 100644 src/tools/rustfmt/tests/target/skip_macro_invocations/use_alias_examples.rs (limited to 'src') diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 4a0ba5925..e861d520c 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -50,6 +50,7 @@ dependencies = [ "opener", "pretty_assertions", "serde", + "serde_derive", "serde_json", "sha2", "sysinfo", @@ -564,9 +565,6 @@ name = "serde" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" -dependencies = [ - "serde_derive", -] [[package]] name = "serde_derive" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 22ceeca94..663987f11 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -39,7 +39,10 @@ cc = "1.0.69" libc = "0.2" hex = "0.4" object = { version = "0.29.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } -serde = { version = "1.0.8", features = ["derive"] } +serde = "1.0.137" +# Directly use serde_derive rather than through the derive feature of serde to allow building both +# in parallel and to allow serde_json and toml to start building as soon as serde has been built. +serde_derive = "1.0.137" serde_json = "1.0.2" sha2 = "0.10" tar = "0.4" diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 8dce9e79e..71eee8968 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -4,105 +4,31 @@ This is an in-progress README which is targeted at helping to explain how Rust is bootstrapped and in general, some of the technical details of the build system. -## Using rustbuild +Note that this README only covers internal information, not how to use the tool. +Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further information. -The rustbuild build system has a primary entry point, a top level `x.py` script: +[bootstrapping-dev-guide]: https://rustc-dev-guide.rust-lang.org/building/bootstrapping.html -```sh -$ python ./x.py build -``` - -Note that if you're on Unix, you should be able to execute the script directly: - -```sh -$ ./x.py build -``` - -The script accepts commands, flags, and arguments to determine what to do: - -* `build` - a general purpose command for compiling code. Alone, `build` will - bootstrap the entire compiler, and otherwise, arguments passed indicate what to - build. For example: - - ``` - # build the whole compiler - ./x.py build --stage 2 - - # build the stage1 compiler - ./x.py build - - # build stage0 libstd - ./x.py build --stage 0 library/std - - # build a particular crate in stage0 - ./x.py build --stage 0 library/test - ``` - - If files that would normally be rebuilt from stage 0 are dirty, the rebuild can be - overridden using `--keep-stage 0`. Using `--keep-stage n` will skip all steps - that belong to stage n or earlier: - - ``` - # build stage 1, keeping old build products for stage 0 - ./x.py build --keep-stage 0 - ``` - -* `test` - a command for executing unit tests. Like the `build` command, this - will execute the entire test suite by default, and otherwise, it can be used to - select which test suite is run: - - ``` - # run all unit tests - ./x.py test - - # execute tool tests - ./x.py test tidy - - # execute the UI test suite - ./x.py test tests/ui - - # execute only some tests in the UI test suite - ./x.py test tests/ui --test-args substring-of-test-name - - # execute tests in the standard library in stage0 - ./x.py test --stage 0 library/std - - # execute tests in the core and standard library in stage0, - # without running doc tests (thus avoid depending on building the compiler) - ./x.py test --stage 0 --no-doc library/core library/std +## Introduction - # execute all doc tests - ./x.py test src/doc - ``` +The build system defers most of the complicated logic managing invocations +of rustc and rustdoc to Cargo itself. However, moving through various stages +and copying artifacts is still necessary for it to do. Each time rustbuild +is invoked, it will iterate through the list of predefined steps and execute +each serially in turn if it matches the paths passed or is a default rule. +For each step rustbuild relies on the step internally being incremental and +parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded +to appropriate test harnesses and such. -* `doc` - a command for building documentation. Like above, can take arguments - for what to document. - -## Configuring rustbuild - -rustbuild offers a TOML-based configuration system with a `config.toml` -file. An example of this configuration can be found at `config.toml.example`, -and the configuration file can also be passed as `--config path/to/config.toml` -if the build system is being invoked manually (via the python script). - -You can generate a config.toml using `./configure` options if you want to automate creating the file without having to edit it. - -Finally, rustbuild makes use of the [cc-rs crate] which has [its own -method][env-vars] of configuring C compilers and C flags via environment -variables. - -[cc-rs crate]: https://github.com/alexcrichton/cc-rs -[env-vars]: https://github.com/alexcrichton/cc-rs#external-configuration-via-environment-variables - -## Build stages +## Build phases The rustbuild build system goes through a few phases to actually build the compiler. What actually happens when you invoke rustbuild is: -1. The entry point script, `x.py` is run. This script is - responsible for downloading the stage0 compiler/Cargo binaries, and it then - compiles the build system itself (this folder). Finally, it then invokes the - actual `bootstrap` binary build system. +1. The entry point script(`x` for unix like systems, `x.ps1` for windows systems, + `x.py` cross-platform) is run. This script is responsible for downloading the stage0 + compiler/Cargo binaries, and it then compiles the build system itself (this folder). + Finally, it then invokes the actual `bootstrap` binary build system. 2. In Rust, `bootstrap` will slurp up all configuration, perform a number of sanity checks (whether compilers exist, for example), and then start building the stage0 artifacts. @@ -115,24 +41,6 @@ compiler. What actually happens when you invoke rustbuild is: The goal of each stage is to (a) leverage Cargo as much as possible and failing that (b) leverage Rust as much as possible! -## Incremental builds - -You can configure rustbuild to use incremental compilation with the -`--incremental` flag: - -```sh -$ ./x.py build --incremental -``` - -The `--incremental` flag will store incremental compilation artifacts -in `build//stage0-incremental`. Note that we only use incremental -compilation for the stage0 -> stage1 compilation -- this is because -the stage1 compiler is changing, and we don't try to cache and reuse -incremental artifacts across different versions of the compiler. - -You can always drop the `--incremental` to build as normal (but you -will still be using the local nightly as your bootstrap). - ## Directory Layout This build system houses all output under the `build` directory, which looks @@ -236,63 +144,31 @@ build/ # system will link (using hard links) output from stageN-{std,rustc} into # each of these directories. # - # In theory, there is no extra build output in these directories. + # In theory these are working rustc sysroot directories, meaning there is + # no extra build output in these directories. stage1/ stage2/ stage3/ ``` -## Cargo projects - -The current build is unfortunately not quite as simple as `cargo build` in a -directory, but rather the compiler is split into three different Cargo projects: - -* `library/std` - the standard library -* `library/test` - testing support, depends on libstd -* `compiler/rustc` - the actual compiler itself - -Each "project" has a corresponding Cargo.lock file with all dependencies, and -this means that building the compiler involves running Cargo three times. The -structure here serves two goals: - -1. Facilitating dependencies coming from crates.io. These dependencies don't - depend on `std`, so libstd is a separate project compiled ahead of time - before the actual compiler builds. -2. Splitting "host artifacts" from "target artifacts". That is, when building - code for an arbitrary target, you don't need the entire compiler, but you'll - end up needing libraries like libtest that depend on std but also want to use - crates.io dependencies. Hence, libtest is split out as its own project that - is sequenced after `std` but before `rustc`. This project is built for all - targets. - -There is some loss in build parallelism here because libtest can be compiled in -parallel with a number of rustc artifacts, but in theory, the loss isn't too bad! - -## Build tools - -We've actually got quite a few tools that we use in the compiler's build system -and for testing. To organize these, each tool is a project in `src/tools` with a -corresponding `Cargo.toml`. All tools are compiled with Cargo (currently having -independent `Cargo.lock` files) and do not currently explicitly depend on the -compiler or standard library. Compiling each tool is sequenced after the -appropriate libstd/libtest/librustc compile above. - ## Extending rustbuild -So, you'd like to add a feature to the rustbuild build system or just fix a bug. -Great! One of the major motivational factors for moving away from `make` is that -Rust is in theory much easier to read, modify, and write. If you find anything -excessively confusing, please open an issue on this, and we'll try to get it -documented or simplified, pronto. +When you use the bootstrap system, you'll call it through the entry point script +(`x`, `x.ps1`, or `x.py`). However, most of the code lives in `src/bootstrap`. +`bootstrap` has a difficult problem: it is written in Rust, but yet it is run +before the Rust compiler is built! To work around this, there are two components +of bootstrap: the main one written in rust, and `bootstrap.py`. `bootstrap.py` +is what gets run by entry point script. It takes care of downloading the `stage0` +compiler, which will then build the bootstrap binary written in Rust. -First up, you'll probably want to read over the documentation above, as that'll -give you a high level overview of what rustbuild is doing. You also probably -want to play around a bit yourself by just getting it up and running before you -dive too much into the actual build system itself. +Because there are two separate codebases behind `x.py`, they need to +be kept in sync. In particular, both `bootstrap.py` and the bootstrap binary +parse `config.toml` and read the same command line arguments. `bootstrap.py` +keeps these in sync by setting various environment variables, and the +programs sometimes have to add arguments that are explicitly ignored, to be +read by the other. -After that, each module in rustbuild should have enough documentation to keep -you up and running. Some general areas that you may be interested in modifying -are: +Some general areas that you may be interested in modifying are: * Adding a new build tool? Take a look at `bootstrap/tool.rs` for examples of other tools. @@ -320,8 +196,9 @@ A 'major change' includes Changes that do not affect contributors to the compiler or users building rustc from source don't need an update to `VERSION`. -If you have any questions, feel free to reach out on the `#t-infra` channel in -the [Rust Zulip server][rust-zulip] or ask on internals.rust-lang.org. When -you encounter bugs, please file issues on the rust-lang/rust issue tracker. +If you have any questions, feel free to reach out on the `#t-infra/bootstrap` channel +at [Rust Bootstrap Zulip server][rust-bootstrap-zulip]. When you encounter bugs, +please file issues on the [Rust issue tracker][rust-issue-tracker]. -[rust-zulip]: https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra +[rust-bootstrap-zulip]: https://rust-lang.zulipchat.com/#narrow/stream/t-infra.2Fbootstrap +[rust-issue-tracker]: https://github.com/rust-lang/rust/issues diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index be69f819c..3856bb64f 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -16,12 +16,17 @@ fn main() { let mut build_lock; let _build_lock_guard; if cfg!(any(unix, windows)) { - build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(config.out.join("lock")))); + let path = config.out.join("lock"); + build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(&path))); _build_lock_guard = match build_lock.try_write() { Ok(lock) => lock, err => { - println!("warning: build directory locked, waiting for lock"); drop(err); + if let Some(pid) = get_lock_owner(&path) { + println!("warning: build directory locked by process {pid}, waiting for lock"); + } else { + println!("warning: build directory locked, waiting for lock"); + } t!(build_lock.write()) } }; @@ -98,3 +103,30 @@ fn check_version(config: &Config) -> Option { Some(msg) } + +/// Get the PID of the process which took the write lock by +/// parsing `/proc/locks`. +#[cfg(target_os = "linux")] +fn get_lock_owner(f: &std::path::Path) -> Option { + use std::fs::File; + use std::io::{BufRead, BufReader}; + use std::os::unix::fs::MetadataExt; + + let lock_inode = std::fs::metadata(f).ok()?.ino(); + let lockfile = File::open("/proc/locks").ok()?; + BufReader::new(lockfile).lines().find_map(|line| { + // pid--vvvvvv vvvvvvv--- inode + // 21: FLOCK ADVISORY WRITE 359238 08:02:3719774 0 EOF + let line = line.ok()?; + let parts = line.split_whitespace().collect::>(); + let (pid, inode) = (parts[4].parse::().ok()?, &parts[5]); + let inode = inode.rsplit_once(':')?.1.parse::().ok()?; + if inode == lock_inode { Some(pid) } else { None } + }) +} + +#[cfg(not(target_os = "linux"))] +fn get_lock_owner(_: &std::path::Path) -> Option { + // FIXME: Implement on other OS's + None +} diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 9cf43fc7a..013d1ab52 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -2,7 +2,6 @@ from __future__ import absolute_import, division, print_function import argparse import contextlib import datetime -import distutils.version import hashlib import json import os @@ -13,17 +12,17 @@ import sys import tarfile import tempfile -from time import time, sleep +from time import time -def support_xz(): - try: - with tempfile.NamedTemporaryFile(delete=False) as temp_file: - temp_path = temp_file.name - with tarfile.open(temp_path, "w:xz"): - pass - return True - except tarfile.CompressionError: - return False +try: + import lzma +except ImportError: + lzma = None + +if sys.platform == 'win32': + EXE_SUFFIX = ".exe" +else: + EXE_SUFFIX = "" def get(base, url, path, checksums, verbose=False): with tempfile.NamedTemporaryFile(delete=False) as temp_file: @@ -61,7 +60,7 @@ def get(base, url, path, checksums, verbose=False): def download(path, url, probably_big, verbose): - for _ in range(0, 4): + for _ in range(4): try: _download(path, url, probably_big, verbose, True) return @@ -88,14 +87,16 @@ def _download(path, url, probably_big, verbose, exception): # If curl is not present on Win32, we should not sys.exit # but raise `CalledProcessError` or `OSError` instead require(["curl", "--version"], exception=platform_is_win32) - run(["curl", option, - "-L", # Follow redirect. - "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds - "--connect-timeout", "30", # timeout if cannot connect within 30 seconds - "--retry", "3", "-Sf", "-o", path, url], - verbose=verbose, - exception=True, # Will raise RuntimeError on failure - ) + with open(path, "wb") as outfile: + run(["curl", option, + "-L", # Follow redirect. + "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds + "--connect-timeout", "30", # timeout if cannot connect within 30 seconds + "--retry", "3", "-Sf", url], + stdout=outfile, #Implements cli redirect operator '>' + verbose=verbose, + exception=True, # Will raise RuntimeError on failure + ) except (subprocess.CalledProcessError, OSError, RuntimeError): # see http://serverfault.com/questions/301128/how-to-download if platform_is_win32: @@ -395,17 +396,18 @@ class RustBuild(object): def __init__(self): self.checksums_sha256 = {} self.stage0_compiler = None - self._download_url = '' + self.download_url = '' self.build = '' self.build_dir = '' self.clean = False self.config_toml = '' self.rust_root = '' - self.use_locked_deps = '' - self.use_vendored_sources = '' + self.use_locked_deps = False + self.use_vendored_sources = False self.verbose = False self.git_version = None self.nix_deps_dir = None + self._should_fix_bins_and_dylibs = None def download_toolchain(self): """Fetch the build system for Rust, written in Rust @@ -426,7 +428,7 @@ class RustBuild(object): self.program_out_of_date(self.rustc_stamp(), key)): if os.path.exists(bin_root): shutil.rmtree(bin_root) - tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' + tarball_suffix = '.tar.gz' if lzma is None else '.tar.xz' filename = "rust-std-{}-{}{}".format( rustc_channel, self.build, tarball_suffix) pattern = "rust-std-{}".format(self.build) @@ -437,15 +439,17 @@ class RustBuild(object): filename = "cargo-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) self._download_component_helper(filename, "cargo", tarball_suffix) - self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root)) - - self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root)) - self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root)) - self.fix_bin_or_dylib("{}/libexec/rust-analyzer-proc-macro-srv".format(bin_root)) - lib_dir = "{}/lib".format(bin_root) - for lib in os.listdir(lib_dir): - if lib.endswith(".so"): - self.fix_bin_or_dylib(os.path.join(lib_dir, lib)) + if self.should_fix_bins_and_dylibs(): + self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root)) + + self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root)) + self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root)) + self.fix_bin_or_dylib("{}/libexec/rust-analyzer-proc-macro-srv".format(bin_root)) + lib_dir = "{}/lib".format(bin_root) + for lib in os.listdir(lib_dir): + if lib.endswith(".so"): + self.fix_bin_or_dylib(os.path.join(lib_dir, lib)) + with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(key) @@ -458,60 +462,73 @@ class RustBuild(object): if not os.path.exists(rustc_cache): os.makedirs(rustc_cache) - base = self._download_url - url = "dist/{}".format(key) tarball = os.path.join(rustc_cache, filename) if not os.path.exists(tarball): get( - base, - "{}/{}".format(url, filename), + self.download_url, + "dist/{}/{}".format(key, filename), tarball, self.checksums_sha256, verbose=self.verbose, ) unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) - def fix_bin_or_dylib(self, fname): - """Modifies the interpreter section of 'fname' to fix the dynamic linker, - or the RPATH section, to fix the dynamic library search path - - This method is only required on NixOS and uses the PatchELF utility to - change the interpreter/RPATH of ELF executables. - - Please see https://nixos.org/patchelf.html for more information + def should_fix_bins_and_dylibs(self): + """Whether or not `fix_bin_or_dylib` needs to be run; can only be True + on NixOS. """ - default_encoding = sys.getdefaultencoding() - try: - ostype = subprocess.check_output( - ['uname', '-s']).strip().decode(default_encoding) - except subprocess.CalledProcessError: - return - except OSError as reason: - if getattr(reason, 'winerror', None) is not None: - return - raise reason + if self._should_fix_bins_and_dylibs is not None: + return self._should_fix_bins_and_dylibs - if ostype != "Linux": - return + def get_answer(): + default_encoding = sys.getdefaultencoding() + try: + ostype = subprocess.check_output( + ['uname', '-s']).strip().decode(default_encoding) + except subprocess.CalledProcessError: + return False + except OSError as reason: + if getattr(reason, 'winerror', None) is not None: + return False + raise reason + + if ostype != "Linux": + return False + + # If the user has asked binaries to be patched for Nix, then + # don't check for NixOS or `/lib`. + if self.get_toml("patch-binaries-for-nix", "build") == "true": + return True - # If the user has asked binaries to be patched for Nix, then - # don't check for NixOS or `/lib`, just continue to the patching. - if self.get_toml('patch-binaries-for-nix', 'build') != 'true': # Use `/etc/os-release` instead of `/etc/NIXOS`. # The latter one does not exist on NixOS when using tmpfs as root. try: with open("/etc/os-release", "r") as f: - if not any(l.strip() in ["ID=nixos", "ID='nixos'", 'ID="nixos"'] for l in f): - return + if not any(l.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"') for l in f): + return False except FileNotFoundError: - return + return False if os.path.exists("/lib"): - return + return False + + return True + + answer = self._should_fix_bins_and_dylibs = get_answer() + if answer: + print("info: You seem to be using Nix.") + return answer - # At this point we're pretty sure the user is running NixOS or - # using Nix - nix_os_msg = "info: you seem to be using Nix. Attempting to patch" - print(nix_os_msg, fname) + def fix_bin_or_dylib(self, fname): + """Modifies the interpreter section of 'fname' to fix the dynamic linker, + or the RPATH section, to fix the dynamic library search path + + This method is only required on NixOS and uses the PatchELF utility to + change the interpreter/RPATH of ELF executables. + + Please see https://nixos.org/patchelf.html for more information + """ + assert self._should_fix_bins_and_dylibs is True + print("attempting to patch", fname) # Only build `.nix-deps` once. nix_deps_dir = self.nix_deps_dir @@ -666,8 +683,7 @@ class RustBuild(object): config = self.get_toml(program) if config: return os.path.expanduser(config) - return os.path.join(self.bin_root(), "bin", "{}{}".format( - program, self.exe_suffix())) + return os.path.join(self.bin_root(), "bin", "{}{}".format(program, EXE_SUFFIX)) @staticmethod def get_string(line): @@ -692,13 +708,6 @@ class RustBuild(object): return line[start + 1:end] return None - @staticmethod - def exe_suffix(): - """Return a suffix for executables""" - if sys.platform == 'win32': - return '.exe' - return '' - def bootstrap_binary(self): """Return the path of the bootstrap binary @@ -710,9 +719,9 @@ class RustBuild(object): """ return os.path.join(self.build_dir, "bootstrap", "debug", "bootstrap") - def build_bootstrap(self, color): + def build_bootstrap(self, color, verbose_count): """Build bootstrap""" - print("Building rustbuild") + print("Building bootstrap") build_dir = os.path.join(self.build_dir, "bootstrap") if self.clean and os.path.exists(build_dir): shutil.rmtree(build_dir) @@ -757,7 +766,6 @@ class RustBuild(object): if target_linker is not None: env["RUSTFLAGS"] += " -C linker=" + target_linker 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" @@ -768,8 +776,7 @@ class RustBuild(object): self.cargo())) args = [self.cargo(), "build", "--manifest-path", os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")] - for _ in range(0, self.verbose): - args.append("--verbose") + args.extend("--verbose" for _ in range(verbose_count)) if self.use_locked_deps: args.append("--locked") if self.use_vendored_sources: @@ -777,6 +784,8 @@ class RustBuild(object): if self.get_toml("metrics", "build"): args.append("--features") args.append("build-metrics") + if self.json_output: + args.append("--message-format=json") if color == "always": args.append("--color=always") elif color == "never": @@ -792,16 +801,7 @@ class RustBuild(object): so use `self.build` where possible. """ config = self.get_toml('build') - if config: - return config - return default_build_triple(self.verbose) - - def set_dist_environment(self, url): - """Set download URL for normal environment""" - if 'RUSTUP_DIST_SERVER' in os.environ: - self._download_url = os.environ['RUSTUP_DIST_SERVER'] - else: - self._download_url = url + return config or default_build_triple(self.verbose) def check_vendored_status(self): """Check that vendoring is configured properly""" @@ -834,32 +834,28 @@ class RustBuild(object): if os.path.exists(cargo_dir): shutil.rmtree(cargo_dir) -def bootstrap(help_triggered): - """Configure, fetch, build and run the initial bootstrap""" - - # If the user is asking for help, let them know that the whole download-and-build - # process has to happen before anything is printed out. - if help_triggered: - print("info: Downloading and building bootstrap before processing --help") - print(" command. See src/bootstrap/README.md for help with common") - print(" commands.") - - parser = argparse.ArgumentParser(description='Build rust') +def parse_args(): + """Parse the command line arguments that the python script needs.""" + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument('-h', '--help', action='store_true') parser.add_argument('--config') parser.add_argument('--build-dir') parser.add_argument('--build') parser.add_argument('--color', choices=['always', 'never', 'auto']) parser.add_argument('--clean', action='store_true') + parser.add_argument('--json-output', action='store_true') parser.add_argument('-v', '--verbose', action='count', default=0) - args = [a for a in sys.argv if a != '-h' and a != '--help'] - args, _ = parser.parse_known_args(args) + return parser.parse_known_args(sys.argv)[0] +def bootstrap(args): + """Configure, fetch, build and run the initial bootstrap""" # Configure initial bootstrap build = RustBuild() build.rust_root = os.path.abspath(os.path.join(__file__, '../../..')) - build.verbose = args.verbose + build.verbose = args.verbose != 0 build.clean = args.clean + build.json_output = args.json_output # Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`, # then `config.toml` in the root directory. @@ -886,12 +882,12 @@ def bootstrap(help_triggered): with open(include_path) as included_toml: build.config_toml += os.linesep + included_toml.read() - config_verbose = build.get_toml('verbose', 'build') - if config_verbose is not None: - build.verbose = max(build.verbose, int(config_verbose)) + verbose_count = args.verbose + config_verbose_count = build.get_toml('verbose', 'build') + if config_verbose_count is not None: + verbose_count = max(args.verbose, int(config_verbose_count)) build.use_vendored_sources = build.get_toml('vendor', 'build') == 'true' - build.use_locked_deps = build.get_toml('locked-deps', 'build') == 'true' build.check_vendored_status() @@ -903,8 +899,7 @@ def bootstrap(help_triggered): data = json.load(f) build.checksums_sha256 = data["checksums_sha256"] build.stage0_compiler = Stage0Toolchain(data["compiler"]) - - build.set_dist_environment(data["config"]["dist_server"]) + build.download_url = os.getenv("RUSTUP_DIST_SERVER") or data["config"]["dist_server"] build.build = args.build or build.build_triple() @@ -914,7 +909,7 @@ def bootstrap(help_triggered): # Fetch/build the bootstrap build.download_toolchain() sys.stdout.flush() - build.build_bootstrap(args.color) + build.build_bootstrap(args.color, verbose_count) sys.stdout.flush() # Run the bootstrap @@ -932,25 +927,34 @@ def main(): # x.py help ... if len(sys.argv) > 1 and sys.argv[1] == 'help': - sys.argv = [sys.argv[0], '-h'] + sys.argv[2:] + sys.argv[1] = '-h' + + args = parse_args() + help_triggered = args.help or len(sys.argv) == 1 - help_triggered = ( - '-h' in sys.argv) or ('--help' in sys.argv) or (len(sys.argv) == 1) + # If the user is asking for help, let them know that the whole download-and-build + # process has to happen before anything is printed out. + if help_triggered: + print( + "info: Downloading and building bootstrap before processing --help command.\n" + " See src/bootstrap/README.md for help with common commands." + ) + + exit_code = 0 + success_word = "successfully" try: - bootstrap(help_triggered) - if not help_triggered: - print("Build completed successfully in {}".format( - format_build_time(time() - start_time))) + bootstrap(args) except (SystemExit, KeyboardInterrupt) as error: if hasattr(error, 'code') and isinstance(error.code, int): exit_code = error.code else: exit_code = 1 print(error) - if not help_triggered: - print("Build completed unsuccessfully in {}".format( - format_build_time(time() - start_time))) - sys.exit(exit_code) + success_word = "unsuccessfully" + + if not help_triggered: + print("Build completed", success_word, "in", format_build_time(time() - start_time)) + sys.exit(exit_code) if __name__ == '__main__': diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index b4fc1d4f2..b33fc02f4 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -780,7 +780,6 @@ impl<'a> Builder<'a> { install::Clippy, install::Miri, install::LlvmTools, - install::Analysis, install::Src, install::Rustc ), @@ -793,7 +792,7 @@ impl<'a> Builder<'a> { run::CollectLicenseMetadata, run::GenerateCopyright, ), - Kind::Setup => describe!(setup::Profile), + Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode), Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), // special-cased in Build::build() Kind::Format => vec![], @@ -1802,16 +1801,6 @@ impl<'a> Builder<'a> { } } - if mode == Mode::Std && self.config.extended && compiler.is_final_stage(self) { - rustflags.arg("-Zsave-analysis"); - cargo.env( - "RUST_SAVE_ANALYSIS_CONFIG", - "{\"output_file\": null,\"full_docs\": false,\ - \"pub_only\": true,\"reachable_only\": false,\ - \"distro_crate\": true,\"signatures\": false,\"borrow_data\": false}", - ); - } - // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc // when compiling the standard library, since this might be linked into the final outputs // produced by rustc. Since this mitigation is only available on Windows, only enable it @@ -1926,6 +1915,13 @@ impl<'a> Builder<'a> { } } + if matches!(mode, Mode::Std) { + if let Some(mir_opt_level) = self.config.rust_validate_mir_opts { + rustflags.arg("-Zvalidate-mir"); + rustflags.arg(&format!("-Zmir-opt-level={}", mir_opt_level)); + } + } + Cargo { command: cargo, rustflags, rustdocflags, allow_features } } diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index d5fcd1075..3574f1118 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -557,6 +557,7 @@ mod dist { rustfix_coverage: false, pass: None, run: None, + only_modified: false, }; let build = Build::new(config); @@ -627,6 +628,7 @@ mod dist { rustfix_coverage: false, pass: None, run: None, + only_modified: false, }; // Make sure rustfmt binary not being found isn't an error. config.channel = "beta".to_string(); diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 4b8a58e87..cd1966713 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -58,9 +58,10 @@ fn args(builder: &Builder<'_>) -> Vec { clippy_lint_warn.iter().for_each(|v| clippy_lint_levels.push(format!("-W{}", v))); clippy_lint_forbid.iter().for_each(|v| clippy_lint_levels.push(format!("-F{}", v))); args.extend(clippy_lint_levels); + args.extend(builder.config.free_args.clone()); args } else { - vec![] + builder.config.free_args.clone() } } @@ -99,11 +100,20 @@ impl Step for Std { cargo_subcommand(builder.kind), ); std_cargo(builder, target, compiler.stage, &mut cargo); + if matches!(builder.config.cmd, Subcommand::Fix { .. }) { + // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot. + cargo.arg("--lib"); + } - builder.info(&format!( - "Checking stage{} library artifacts ({} -> {})", - builder.top_stage, &compiler.host, target - )); + let msg = if compiler.host == target { + format!("Checking stage{} library artifacts ({target})", builder.top_stage) + } else { + format!( + "Checking stage{} library artifacts ({} -> {})", + builder.top_stage, &compiler.host, target + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -157,10 +167,18 @@ impl Step for Std { cargo.arg("-p").arg(krate.name); } - builder.info(&format!( - "Checking stage{} library test/bench/example targets ({} -> {})", - builder.top_stage, &compiler.host, target - )); + let msg = if compiler.host == target { + format!( + "Checking stage{} library test/bench/example targets ({target})", + builder.top_stage + ) + } else { + format!( + "Checking stage{} library test/bench/example targets ({} -> {})", + builder.top_stage, &compiler.host, target + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -234,10 +252,15 @@ impl Step for Rustc { cargo.arg("-p").arg(krate.name); } - builder.info(&format!( - "Checking stage{} compiler artifacts ({} -> {})", - builder.top_stage, &compiler.host, target - )); + let msg = if compiler.host == target { + format!("Checking stage{} compiler artifacts ({target})", builder.top_stage) + } else { + format!( + "Checking stage{} compiler artifacts ({} -> {})", + builder.top_stage, &compiler.host, target + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -294,10 +317,15 @@ impl Step for CodegenBackend { .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend))); rustc_cargo_env(builder, &mut cargo, target); - builder.info(&format!( - "Checking stage{} {} artifacts ({} -> {})", - builder.top_stage, backend, &compiler.host.triple, target.triple - )); + let msg = if compiler.host == target { + format!("Checking stage{} {} artifacts ({target})", builder.top_stage, backend) + } else { + format!( + "Checking stage{} {} library ({} -> {})", + builder.top_stage, backend, &compiler.host.triple, target.triple + ) + }; + builder.info(&msg); run_cargo( builder, @@ -357,10 +385,15 @@ impl Step for RustAnalyzer { cargo.arg("--benches"); } - builder.info(&format!( - "Checking stage{} {} artifacts ({} -> {})", - compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple - )); + let msg = if compiler.host == target { + format!("Checking stage{} {} artifacts ({target})", compiler.stage, "rust-analyzer") + } else { + format!( + "Checking stage{} {} artifacts ({} -> {})", + compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -427,14 +460,18 @@ macro_rules! tool_check_step { // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]` // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776 cargo.rustflag("-Zunstable-options"); - - builder.info(&format!( - "Checking stage{} {} artifacts ({} -> {})", - builder.top_stage, - stringify!($name).to_lowercase(), - &compiler.host.triple, - target.triple - )); + let msg = if compiler.host == target { + format!("Checking stage{} {} artifacts ({target})", builder.top_stage, stringify!($name).to_lowercase()) + } else { + format!( + "Checking stage{} {} artifacts ({} -> {})", + builder.top_stage, + stringify!($name).to_lowercase(), + &compiler.host.triple, + target.triple + ) + }; + builder.info(&msg); run_cargo( builder, cargo, diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index 468efc111..7ebd0a8f2 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -62,6 +62,7 @@ macro_rules! clean_crate_tree { let target = compiler.host; let mut cargo = builder.bare_cargo(compiler, $mode, target, "clean"); for krate in &*self.crates { + cargo.arg("-p"); cargo.arg(krate); } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 68d1db016..8b80dfc0f 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -16,7 +16,7 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::str; -use serde::Deserialize; +use serde_derive::Deserialize; use crate::builder::crate_description; use crate::builder::Cargo; @@ -111,10 +111,18 @@ impl Step for Std { let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); if compiler_to_use != compiler { builder.ensure(Std::new(compiler_to_use, target)); - builder.info(&format!( - "Uplifting stage1 library ({} -> {})", - compiler_to_use.host, target - )); + let msg = if compiler_to_use.host == target { + format!( + "Uplifting library (stage{} -> stage{})", + compiler_to_use.stage, compiler.stage + ) + } else { + format!( + "Uplifting library (stage{}:{} -> stage{}:{})", + compiler_to_use.stage, compiler_to_use.host, compiler.stage, target + ) + }; + builder.info(&msg); // Even if we're not building std this stage, the new sysroot must // still contain the third party objects needed by various targets. @@ -134,13 +142,23 @@ impl Step for Std { cargo.arg("-p").arg(krate); } - builder.info(&format!( - "Building{} stage{} library artifacts ({} -> {})", - crate_description(&self.crates), - compiler.stage, - &compiler.host, - target, - )); + let msg = if compiler.host == target { + format!( + "Building{} stage{} library artifacts ({}) ", + crate_description(&self.crates), + compiler.stage, + compiler.host + ) + } else { + format!( + "Building{} stage{} library artifacts ({} -> {})", + crate_description(&self.crates), + compiler.stage, + compiler.host, + target, + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -379,6 +397,9 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car if stage >= 1 { cargo.rustflag("-Cembed-bitcode=yes"); } + if builder.config.rust_lto == RustcLto::Off { + cargo.rustflag("-Clto=off"); + } // By default, rustc does not include unwind tables unless they are required // for a particular target. They are not required by RISC-V targets, but @@ -435,10 +456,6 @@ impl Step for StdLink { let compiler = self.compiler; let target_compiler = self.target_compiler; let target = self.target; - builder.info(&format!( - "Copying stage{} library from stage{} ({} -> {} / {})", - target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target - )); let libdir = builder.sysroot_libdir(target_compiler, target); let hostdir = builder.sysroot_libdir(target_compiler, compiler.host); add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); @@ -464,7 +481,12 @@ fn copy_sanitizers( let dst = libdir.join(&runtime.name); builder.copy(&runtime.path, &dst); - if target == "x86_64-apple-darwin" || target == "aarch64-apple-darwin" { + if target == "x86_64-apple-darwin" + || target == "aarch64-apple-darwin" + || target == "aarch64-apple-ios" + || target == "aarch64-apple-ios-sim" + || target == "x86_64-apple-ios" + { // Update the library’s install name to reflect that it has been renamed. apple_darwin_update_library_name(&dst, &format!("@rpath/{}", &runtime.name)); // Upon renaming the install name, the code signature of the file will invalidate, @@ -641,8 +663,22 @@ impl Step for Rustc { let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); if compiler_to_use != compiler { builder.ensure(Rustc::new(compiler_to_use, target)); - builder - .info(&format!("Uplifting stage1 rustc ({} -> {})", builder.config.build, target)); + let msg = if compiler_to_use.host == target { + format!( + "Uplifting rustc (stage{} -> stage{})", + compiler_to_use.stage, + compiler.stage + 1 + ) + } else { + format!( + "Uplifting rustc (stage{}:{} -> stage{}:{})", + compiler_to_use.stage, + compiler_to_use.host, + compiler.stage + 1, + target + ) + }; + builder.info(&msg); builder.ensure(RustcLink::from_rustc(self, compiler_to_use)); return; } @@ -722,6 +758,13 @@ impl Step for Rustc { cargo.rustflag("-Cembed-bitcode=yes"); } RustcLto::ThinLocal => { /* Do nothing, this is the default */ } + RustcLto::Off => { + cargo.rustflag("-Clto=off"); + } + } + } else { + if builder.config.rust_lto == RustcLto::Off { + cargo.rustflag("-Clto=off"); } } @@ -729,13 +772,24 @@ impl Step for Rustc { cargo.arg("-p").arg(krate); } - builder.info(&format!( - "Building{} stage{} compiler artifacts ({} -> {})", - crate_description(&self.crates), - compiler.stage, - &compiler.host, - target, - )); + let msg = if compiler.host == target { + format!( + "Building{} compiler artifacts (stage{} -> stage{})", + crate_description(&self.crates), + compiler.stage, + compiler.stage + 1 + ) + } else { + format!( + "Building{} compiler artifacts (stage{}:{} -> stage{}:{})", + crate_description(&self.crates), + compiler.stage, + compiler.host, + compiler.stage + 1, + target, + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -919,10 +973,6 @@ impl Step for RustcLink { let compiler = self.compiler; let target_compiler = self.target_compiler; let target = self.target; - builder.info(&format!( - "Copying stage{} rustc from stage{} ({} -> {} / {})", - target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target - )); add_to_sysroot( builder, &builder.sysroot_libdir(target_compiler, target), @@ -996,10 +1046,15 @@ impl Step for CodegenBackend { let tmp_stamp = out_dir.join(".tmp.stamp"); - builder.info(&format!( - "Building stage{} codegen backend {} ({} -> {})", - compiler.stage, backend, &compiler.host, target - )); + let msg = if compiler.host == target { + format!("Building stage{} codegen backend {}", compiler.stage, backend) + } else { + format!( + "Building stage{} codegen backend {} ({} -> {})", + compiler.stage, backend, compiler.host, target + ) + }; + builder.info(&msg); let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false); if builder.config.dry_run() { return; @@ -1305,7 +1360,12 @@ impl Step for Assemble { let stage = target_compiler.stage; let host = target_compiler.host; - builder.info(&format!("Assembling stage{} compiler ({})", stage, host)); + let msg = if build_compiler.host == host { + format!("Assembling stage{} compiler", stage) + } else { + format!("Assembling stage{} compiler ({})", stage, host) + }; + builder.info(&msg); // Link in all dylibs to the libdir let stamp = librustc_stamp(builder, build_compiler, target_compiler.host); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index b41d60d51..05e742549 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -25,6 +25,7 @@ use crate::flags::{Color, Flags}; use crate::util::{exe, output, t}; use once_cell::sync::OnceCell; use serde::{Deserialize, Deserializer}; +use serde_derive::Deserialize; macro_rules! check_ci_llvm { ($name:expr) => { @@ -65,6 +66,7 @@ pub struct Config { pub verbose: usize, pub submodules: Option, pub compiler_docs: bool, + pub library_docs_private_items: bool, pub docs_minification: bool, pub docs: bool, pub locked_deps: bool, @@ -96,6 +98,10 @@ pub struct Config { pub cmd: Subcommand, pub incremental: bool, pub dry_run: DryRun, + /// Arguments appearing after `--` to be forwarded to tools, + /// e.g. `--fix-broken` or test arguments. + pub free_args: Vec, + /// `None` if we shouldn't download CI compiler artifacts, or the commit to download if we should. #[cfg(not(test))] download_rustc_commit: Option, @@ -168,6 +174,7 @@ pub struct Config { pub rust_profile_use: Option, pub rust_profile_generate: Option, pub rust_lto: RustcLto, + pub rust_validate_mir_opts: Option, pub llvm_profile_use: Option, pub llvm_profile_generate: bool, pub llvm_libunwind_default: Option, @@ -332,8 +339,9 @@ impl SplitDebuginfo { } /// LTO mode used for compiling rustc itself. -#[derive(Default, Clone)] +#[derive(Default, Clone, PartialEq)] pub enum RustcLto { + Off, #[default] ThinLocal, Thin, @@ -348,6 +356,7 @@ impl std::str::FromStr for RustcLto { "thin-local" => Ok(RustcLto::ThinLocal), "thin" => Ok(RustcLto::Thin), "fat" => Ok(RustcLto::Fat), + "off" => Ok(RustcLto::Off), _ => Err(format!("Invalid value for rustc LTO: {}", s)), } } @@ -606,6 +615,7 @@ define_config! { rustfmt: Option = "rustfmt", docs: Option = "docs", compiler_docs: Option = "compiler-docs", + library_docs_private_items: Option = "library-docs-private-items", docs_minification: Option = "docs-minification", submodules: Option = "submodules", gdb: Option = "gdb", @@ -762,6 +772,7 @@ define_config! { // ignored; this is set from an env var set by bootstrap.py download_rustc: Option = "download-rustc", lto: Option = "lto", + validate_mir_opts: Option = "validate-mir-opts", } } @@ -862,6 +873,7 @@ impl Config { config.keep_stage = flags.keep_stage; config.keep_stage_std = flags.keep_stage_std; config.color = flags.color; + config.free_args = flags.free_args.clone().unwrap_or_default(); if let Some(value) = flags.deny_warnings { config.deny_warnings = value; } @@ -965,6 +977,9 @@ impl Config { config.changelog_seen = toml.changelog_seen; let build = toml.build.unwrap_or_default(); + if let Some(file_build) = build.build { + config.build = TargetSelection::from_user(&file_build); + }; set(&mut config.out, flags.build_dir.or_else(|| build.build_dir.map(PathBuf::from))); // NOTE: Bootstrap spawns various commands with different working directories. @@ -1015,6 +1030,7 @@ impl Config { config.submodules = build.submodules; set(&mut config.low_priority, build.low_priority); set(&mut config.compiler_docs, build.compiler_docs); + set(&mut config.library_docs_private_items, build.library_docs_private_items); set(&mut config.docs_minification, build.docs_minification); set(&mut config.docs, build.docs); set(&mut config.locked_deps, build.locked_deps); @@ -1136,6 +1152,7 @@ impl Config { .as_deref() .map(|value| RustcLto::from_str(value).unwrap()) .unwrap_or_default(); + config.rust_validate_mir_opts = rust.validate_mir_opts; } else { config.rust_profile_use = flags.rust_profile_use; config.rust_profile_generate = flags.rust_profile_generate; @@ -1302,15 +1319,6 @@ impl Config { } else { RustfmtState::Unavailable }; - } else { - // If using a system toolchain for bootstrapping, see if that has rustfmt available. - let host = config.build; - let rustfmt_path = config.initial_rustc.with_file_name(exe("rustfmt", host)); - let bin_root = config.out.join(host.triple).join("stage0"); - if !rustfmt_path.starts_with(&bin_root) { - // Using a system-provided toolchain; we shouldn't download rustfmt. - *config.initial_rustfmt.borrow_mut() = RustfmtState::SystemToolchain(rustfmt_path); - } } // Now that we've reached the end of our configuration, infer the diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs index c30c91317..5a105007f 100644 --- a/src/bootstrap/config/tests.rs +++ b/src/bootstrap/config/tests.rs @@ -11,6 +11,11 @@ fn parse(config: &str) -> Config { #[test] fn download_ci_llvm() { + if crate::native::is_ci_llvm_modified(&parse("")) { + eprintln!("Detected LLVM as non-available: running in CI and modified LLVM in this change"); + return; + } + let parse_llvm = |s| parse(s).llvm_from_ci; let if_available = parse_llvm("llvm.download-ci-llvm = \"if-available\""); @@ -19,6 +24,13 @@ fn download_ci_llvm() { assert_eq!(parse_llvm(""), if_available); assert_eq!(parse_llvm("rust.channel = \"dev\""), if_available); assert!(!parse_llvm("rust.channel = \"stable\"")); + assert!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\"")); + assert!(parse_llvm( + "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-available\"" + )); + assert!(!parse_llvm( + "llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-available\"" + )); } // FIXME: add test for detecting `src` and `out` diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 0af329e70..ab3d08292 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -379,8 +379,14 @@ cur_section = None sections[None] = [] section_order = [None] targets = {} +top_level_keys = [] for line in open(rust_dir + '/config.toml.example').read().split("\n"): + if cur_section == None: + if line.count('=') == 1: + top_level_key = line.split('=')[0] + top_level_key = top_level_key.strip(' #') + top_level_keys.append(top_level_key) if line.startswith('['): cur_section = line[1:-1] if cur_section.startswith('target'): @@ -436,6 +442,8 @@ def to_toml(value): return value else: return "'" + value + "'" + elif isinstance(value, dict): + return "{" + ", ".join(map(lambda a: "{} = {}".format(to_toml(a[0]), to_toml(a[1])), value.items())) + "}" else: raise RuntimeError('no toml') @@ -459,12 +467,22 @@ def configure_section(lines, config): raise RuntimeError("failed to find config line for {}".format(key)) -for section_key in config: - section_config = config[section_key] - if section_key not in sections: - raise RuntimeError("config key {} not in sections".format(section_key)) +def configure_top_level_key(lines, top_level_key, value): + for i, line in enumerate(lines): + if line.startswith('#' + top_level_key + ' = ') or line.startswith(top_level_key + ' = '): + lines[i] = "{} = {}".format(top_level_key, value) + return - if section_key == 'target': + raise RuntimeError("failed to find config line for {}".format(top_level_key)) + + +for section_key, section_config in config.items(): + if section_key not in sections and section_key not in top_level_keys: + raise RuntimeError("config key {} not in sections or top_level_keys".format(section_key)) + if section_key in top_level_keys: + configure_top_level_key(sections[None], section_key, section_config) + + elif section_key == 'target': for target in section_config: configure_section(targets[target], section_config[target]) else: diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml index 2f4ccb825..b98b13119 100644 --- a/src/bootstrap/defaults/config.compiler.toml +++ b/src/bootstrap/defaults/config.compiler.toml @@ -12,6 +12,8 @@ debug-logging = true incremental = true # Print backtrace on internal compiler errors during bootstrap backtrace-on-ice = true +# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown. +lto = "off" [llvm] # Will download LLVM from CI if available on your platform. diff --git a/src/bootstrap/defaults/config.library.toml b/src/bootstrap/defaults/config.library.toml index 7bc054d3a..f362c4111 100644 --- a/src/bootstrap/defaults/config.library.toml +++ b/src/bootstrap/defaults/config.library.toml @@ -8,6 +8,8 @@ bench-stage = 0 [rust] # This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. incremental = true +# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown. +lto = "off" [llvm] # Will download LLVM from CI if available on your platform. diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 6594b23c5..9b2b54961 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -12,6 +12,7 @@ use std::collections::HashSet; use std::env; use std::ffi::OsStr; use std::fs; +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; @@ -392,19 +393,29 @@ impl Step for Rustc { t!(fs::create_dir_all(image.join("bin"))); builder.cp_r(&src.join("bin"), &image.join("bin")); - builder.install(&builder.rustdoc(compiler), &image.join("bin"), 0o755); + if builder + .config + .tools + .as_ref() + .map_or(true, |tools| tools.iter().any(|tool| tool == "rustdoc")) + { + let rustdoc = builder.rustdoc(compiler); + builder.install(&rustdoc, &image.join("bin"), 0o755); + } - let ra_proc_macro_srv = builder - .ensure(tool::RustAnalyzerProcMacroSrv { + if let Some(ra_proc_macro_srv) = builder.ensure_if_default( + tool::RustAnalyzerProcMacroSrv { compiler: builder.compiler_for( compiler.stage, builder.config.build, compiler.host, ), target: compiler.host, - }) - .expect("rust-analyzer-proc-macro-server always builds"); - builder.install(&ra_proc_macro_srv, &image.join("libexec"), 0o755); + }, + builder.kind, + ) { + builder.install(&ra_proc_macro_srv, &image.join("libexec"), 0o755); + } let libdir_relative = builder.libdir_relative(compiler); @@ -743,7 +754,7 @@ impl Step for Analysis { }); } - /// Creates a tarball of save-analysis metadata, if available. + /// Creates a tarball of (degenerate) save-analysis metadata, if available. fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; @@ -751,7 +762,6 @@ impl Step for Analysis { return None; } - builder.ensure(compile::Std::new(compiler, target)); let src = builder .stage_out(compiler, Mode::Std) .join(target.triple) @@ -759,6 +769,13 @@ impl Step for Analysis { .join("deps") .join("save-analysis"); + // Write a file indicating that this component has been removed. + t!(std::fs::create_dir_all(&src)); + let mut removed = src.clone(); + removed.push("removed.json"); + let mut f = t!(std::fs::File::create(removed)); + t!(write!(f, r#"{{ "warning": "The `rust-analysis` component has been removed." }}"#)); + let mut tarball = Tarball::new(builder, "rust-analysis", &target.triple); tarball.include_target_in_component_name(true); tarball.add_dir(src, format!("lib/rustlib/{}/analysis", target.triple)); diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 9bad9046e..cc80763ef 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -62,6 +62,7 @@ macro_rules! book { target: self.target, name: INTERNER.intern_str($book_name), src: INTERNER.intern_path(builder.src.join($path)), + parent: Some(self), }) } } @@ -119,18 +120,20 @@ impl Step for UnstableBook { target: self.target, name: INTERNER.intern_str("unstable-book"), src: INTERNER.intern_path(builder.md_doc_out(self.target).join("unstable-book")), + parent: Some(self), }) } } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -struct RustbookSrc { +struct RustbookSrc { target: TargetSelection, name: Interned, src: Interned, + parent: Option

, } -impl Step for RustbookSrc { +impl Step for RustbookSrc

{ type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -152,13 +155,18 @@ impl Step for RustbookSrc { let index = out.join("index.html"); let rustbook = builder.tool_exe(Tool::Rustbook); let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); - if builder.config.dry_run() || up_to_date(&src, &index) && up_to_date(&rustbook, &index) { - return; + + if !builder.config.dry_run() && !(up_to_date(&src, &index) || up_to_date(&rustbook, &index)) + { + builder.info(&format!("Rustbook ({}) - {}", target, name)); + let _ = fs::remove_dir_all(&out); + + builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out)); } - builder.info(&format!("Rustbook ({}) - {}", target, name)); - let _ = fs::remove_dir_all(&out); - builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out)); + if self.parent.is_some() { + builder.maybe_open_in_browser::

(index) + } } } @@ -205,6 +213,7 @@ impl Step for TheBook { target, name: INTERNER.intern_str("book"), src: INTERNER.intern_path(builder.src.join(&relative_path)), + parent: Some(self), }); // building older edition redirects @@ -213,6 +222,9 @@ impl Step for TheBook { target, name: INTERNER.intern_string(format!("book/{}", edition)), src: INTERNER.intern_path(builder.src.join(&relative_path).join(edition)), + // There should only be one book that is marked as the parent for each target, so + // treat the other editions as not having a parent. + parent: Option::::None, }); } @@ -228,10 +240,6 @@ impl Step for TheBook { invoke_rustdoc(builder, compiler, &shared_assets, target, path); } - - let out = builder.doc_out(target); - let index = out.join("book").join("index.html"); - builder.maybe_open_in_browser::(index); } } @@ -597,6 +605,9 @@ fn doc_std( .arg("--resource-suffix") .arg(&builder.version) .args(extra_args); + if builder.config.library_docs_private_items { + cargo.arg("--document-private-items").arg("--document-hidden-items"); + } builder.run(&mut cargo.into()); }; @@ -1029,10 +1040,7 @@ impl Step for RustcBook { target: self.target, name: INTERNER.intern_str("rustc"), src: INTERNER.intern_path(out_base), + parent: Some(self), }); - - let out = builder.doc_out(self.target); - let index = out.join("rustc").join("index.html"); - builder.maybe_open_in_browser::(index); } } diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index 6ae283f32..d1e2149d3 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -18,6 +18,8 @@ use crate::{ Config, }; +static SHOULD_FIX_BINS_AND_DYLIBS: OnceCell = OnceCell::new(); + /// Generic helpers that are useful anywhere in bootstrap. impl Config { pub fn is_verbose(&self) -> bool { @@ -70,53 +72,61 @@ impl Config { check_run(cmd, self.is_verbose()) } - /// Modifies the interpreter section of 'fname' to fix the dynamic linker, - /// or the RPATH section, to fix the dynamic library search path - /// - /// This is only required on NixOS and uses the PatchELF utility to - /// change the interpreter/RPATH of ELF executables. - /// - /// Please see https://nixos.org/patchelf.html for more information - fn fix_bin_or_dylib(&self, fname: &Path) { - // FIXME: cache NixOS detection? - match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() { - Err(_) => return, - Ok(output) if !output.status.success() => return, - Ok(output) => { - let mut s = output.stdout; - if s.last() == Some(&b'\n') { - s.pop(); - } - if s != b"Linux" { - return; + /// Whether or not `fix_bin_or_dylib` needs to be run; can only be true + /// on NixOS + fn should_fix_bins_and_dylibs(&self) -> bool { + let val = *SHOULD_FIX_BINS_AND_DYLIBS.get_or_init(|| { + match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() { + Err(_) => return false, + Ok(output) if !output.status.success() => return false, + Ok(output) => { + let mut os_name = output.stdout; + if os_name.last() == Some(&b'\n') { + os_name.pop(); + } + if os_name != b"Linux" { + return false; + } } } - } - // If the user has asked binaries to be patched for Nix, then - // don't check for NixOS or `/lib`, just continue to the patching. - // NOTE: this intentionally comes after the Linux check: - // - patchelf only works with ELF files, so no need to run it on Mac or Windows - // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc. - if !self.patch_binaries_for_nix { + // If the user has asked binaries to be patched for Nix, then + // don't check for NixOS or `/lib`. + // NOTE: this intentionally comes after the Linux check: + // - patchelf only works with ELF files, so no need to run it on Mac or Windows + // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc. + if self.patch_binaries_for_nix { + return true; + } + // Use `/etc/os-release` instead of `/etc/NIXOS`. // The latter one does not exist on NixOS when using tmpfs as root. - const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""]; - let os_release = match File::open("/etc/os-release") { - Err(e) if e.kind() == ErrorKind::NotFound => return, + let is_nixos = match File::open("/etc/os-release") { + Err(e) if e.kind() == ErrorKind::NotFound => false, Err(e) => panic!("failed to access /etc/os-release: {}", e), - Ok(f) => f, + Ok(os_release) => BufReader::new(os_release).lines().any(|l| { + let l = l.expect("reading /etc/os-release"); + matches!(l.trim(), "ID=nixos" | "ID='nixos'" | "ID=\"nixos\"") + }), }; - if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) { - return; - } - if Path::new("/lib").exists() { - return; - } + is_nixos && !Path::new("/lib").exists() + }); + if val { + println!("info: You seem to be using Nix."); } + val + } - // At this point we're pretty sure the user is running NixOS or using Nix - println!("info: you seem to be using Nix. Attempting to patch {}", fname.display()); + /// Modifies the interpreter section of 'fname' to fix the dynamic linker, + /// or the RPATH section, to fix the dynamic library search path + /// + /// This is only required on NixOS and uses the PatchELF utility to + /// change the interpreter/RPATH of ELF executables. + /// + /// Please see https://nixos.org/patchelf.html for more information + fn fix_bin_or_dylib(&self, fname: &Path) { + assert_eq!(SHOULD_FIX_BINS_AND_DYLIBS.get(), Some(&true)); + println!("attempting to patch {}", fname.display()); // Only build `.nix-deps` once. static NIX_DEPS_DIR: OnceCell = OnceCell::new(); @@ -163,8 +173,7 @@ impl Config { // appear to have this (even when `../lib` is redundant). // NOTE: there are only two paths here, delimited by a `:` let mut entries = OsString::from("$ORIGIN/../lib:"); - entries.push(t!(fs::canonicalize(nix_deps_dir))); - entries.push("/lib"); + entries.push(t!(fs::canonicalize(nix_deps_dir)).join("lib")); entries }; patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]); @@ -211,10 +220,10 @@ impl Config { "--retry", "3", "-Sf", - "-o", ]); - curl.arg(tempfile); curl.arg(url); + let f = File::create(tempfile).unwrap(); + curl.stdout(Stdio::from(f)); if !self.check_run(&mut curl) { if self.build.contains("windows-msvc") { println!("Fallback to PowerShell"); @@ -318,18 +327,39 @@ impl Config { let channel = format!("{version}-{date}"); let host = self.build; - let rustfmt_path = self.initial_rustc.with_file_name(exe("rustfmt", host)); - let bin_root = self.out.join(host.triple).join("stage0"); + let bin_root = self.out.join(host.triple).join("rustfmt"); + let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { return Some(rustfmt_path); } - let filename = format!("rustfmt-{version}-{build}.tar.xz", build = host.triple); - self.download_component(DownloadSource::Dist, filename, "rustfmt-preview", &date, "stage0"); - - self.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt")); - self.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt")); + self.download_component( + DownloadSource::Dist, + format!("rustfmt-{version}-{build}.tar.xz", build = host.triple), + "rustfmt-preview", + &date, + "rustfmt", + ); + self.download_component( + DownloadSource::Dist, + format!("rustc-{version}-{build}.tar.xz", build = host.triple), + "rustc", + &date, + "rustfmt", + ); + + if self.should_fix_bins_and_dylibs() { + self.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt")); + self.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt")); + let lib_dir = bin_root.join("lib"); + for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { + let lib = t!(lib); + if lib.path().extension() == Some(OsStr::new("so")) { + self.fix_bin_or_dylib(&lib.path()); + } + } + } self.create(&rustfmt_stamp, &channel); Some(rustfmt_path) @@ -358,16 +388,21 @@ impl Config { let filename = format!("rust-src-{version}.tar.xz"); self.download_ci_component(filename, "rust-src", commit); - self.fix_bin_or_dylib(&bin_root.join("bin").join("rustc")); - self.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc")); - self.fix_bin_or_dylib(&bin_root.join("libexec").join("rust-analyzer-proc-macro-srv")); - let lib_dir = bin_root.join("lib"); - for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { - let lib = t!(lib); - if lib.path().extension() == Some(OsStr::new("so")) { - self.fix_bin_or_dylib(&lib.path()); + if self.should_fix_bins_and_dylibs() { + self.fix_bin_or_dylib(&bin_root.join("bin").join("rustc")); + self.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc")); + self.fix_bin_or_dylib( + &bin_root.join("libexec").join("rust-analyzer-proc-macro-srv"), + ); + let lib_dir = bin_root.join("lib"); + for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { + let lib = t!(lib); + if lib.path().extension() == Some(OsStr::new("so")) { + self.fix_bin_or_dylib(&lib.path()); + } } } + t!(fs::write(rustc_stamp, commit)); } } @@ -459,8 +494,10 @@ impl Config { let key = format!("{}{}", llvm_sha, self.llvm_assertions); if program_out_of_date(&llvm_stamp, &key) && !self.dry_run() { self.download_ci_llvm(&llvm_sha); - for entry in t!(fs::read_dir(llvm_root.join("bin"))) { - self.fix_bin_or_dylib(&t!(entry).path()); + if self.should_fix_bins_and_dylibs() { + for entry in t!(fs::read_dir(llvm_root.join("bin"))) { + self.fix_bin_or_dylib(&t!(entry).path()); + } } // Update the timestamp of llvm-config to force rustc_llvm to be @@ -475,13 +512,16 @@ impl Config { let llvm_config = llvm_root.join("bin").join(exe("llvm-config", self.build)); t!(filetime::set_file_times(&llvm_config, now, now)); - let llvm_lib = llvm_root.join("lib"); - for entry in t!(fs::read_dir(&llvm_lib)) { - let lib = t!(entry).path(); - if lib.extension().map_or(false, |ext| ext == "so") { - self.fix_bin_or_dylib(&lib); + if self.should_fix_bins_and_dylibs() { + let llvm_lib = llvm_root.join("lib"); + for entry in t!(fs::read_dir(&llvm_lib)) { + let lib = t!(entry).path(); + if lib.extension().map_or(false, |ext| ext == "so") { + self.fix_bin_or_dylib(&lib); + } } } + t!(fs::write(llvm_stamp, key)); } } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 52c3dc0bf..9d1504c34 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -80,6 +80,10 @@ pub struct Flags { pub llvm_profile_generate: bool, pub llvm_bolt_profile_generate: bool, pub llvm_bolt_profile_use: Option, + + /// Arguments appearing after `--` to be forwarded to tools, + /// e.g. `--fix-broken` or test arguments. + pub free_args: Option>, } #[derive(Debug)] @@ -124,6 +128,7 @@ pub enum Subcommand { fail_fast: bool, doc_tests: DocTests, rustfix_coverage: bool, + only_modified: bool, }, Bench { paths: Vec, @@ -156,6 +161,12 @@ impl Default for Subcommand { impl Flags { pub fn parse(args: &[String]) -> Flags { + let (args, free_args) = if let Some(pos) = args.iter().position(|s| s == "--") { + let (args, free) = args.split_at(pos); + (args, Some(free[1..].to_vec())) + } else { + (args, None) + }; let mut subcommand_help = String::from( "\ Usage: x.py [options] [...] @@ -301,6 +312,7 @@ To learn more about a subcommand, run `./x.py -h`", opts.optflag("", "doc", "only run doc tests"); opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests"); opts.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged"); + opts.optflag("", "only-modified", "only run tests that result has been changed"); opts.optopt( "", "compare-mode", @@ -542,7 +554,8 @@ Arguments: Kind::Setup => { subcommand_help.push_str(&format!( "\n -x.py setup creates a `config.toml` which changes the defaults for x.py itself. +x.py setup creates a `config.toml` which changes the defaults for x.py itself, +as well as setting up a git pre-push hook, VS code config and toolchain link. Arguments: This subcommand accepts a 'profile' to use for builds. For example: @@ -552,7 +565,13 @@ Arguments: The profile is optional and you will be prompted interactively if it is not given. The following profiles are available: -{}", +{} + + To only set up the git hook, VS code or toolchain link, you may use + ./x.py setup hook + ./x.py setup vscode + ./x.py setup link +", Profile::all_for_help(" ").trim_end() )); } @@ -598,6 +617,7 @@ Arguments: rustc_args: matches.opt_strs("rustc-args"), fail_fast: !matches.opt_present("no-fail-fast"), rustfix_coverage: matches.opt_present("rustfix-coverage"), + only_modified: matches.opt_present("only-modified"), doc_tests: if matches.opt_present("doc") { DocTests::Only } else if matches.opt_present("no-doc") { @@ -625,7 +645,7 @@ Arguments: } Kind::Setup => { let profile = if paths.len() > 1 { - eprintln!("\nerror: At most one profile can be passed to setup\n"); + eprintln!("\nerror: At most one option can be passed to setup\n"); usage(1, &opts, verbose, &subcommand_help) } else if let Some(path) = paths.pop() { let profile_string = t!(path.into_os_string().into_string().map_err( @@ -706,6 +726,7 @@ Arguments: llvm_profile_generate: matches.opt_present("llvm-profile-generate"), llvm_bolt_profile_generate: matches.opt_present("llvm-bolt-profile-generate"), llvm_bolt_profile_use: matches.opt_str("llvm-bolt-profile-use"), + free_args, } } } @@ -777,6 +798,13 @@ impl Subcommand { } } + pub fn only_modified(&self) -> bool { + match *self { + Subcommand::Test { only_modified, .. } => only_modified, + _ => false, + } + } + pub fn force_rerun(&self) -> bool { match *self { Subcommand::Test { force_rerun, .. } => force_rerun, diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index bfc57a85c..6d5753e8a 100644 --- a/src/bootstrap/format.rs +++ b/src/bootstrap/format.rs @@ -1,8 +1,8 @@ //! Runs rustfmt on the repository. use crate::builder::Builder; -use crate::util::{output, output_result, program_out_of_date, t}; -use build_helper::git::updated_master_branch; +use crate::util::{output, program_out_of_date, t}; +use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; use std::collections::VecDeque; use std::path::{Path, PathBuf}; @@ -80,26 +80,14 @@ fn update_rustfmt_version(build: &Builder<'_>) { /// /// Returns `None` if all files should be formatted. fn get_modified_rs_files(build: &Builder<'_>) -> Result>, String> { - let Ok(updated_master) = updated_master_branch(Some(&build.config.src)) else { return Ok(None); }; - if !verify_rustfmt_version(build) { return Ok(None); } - let merge_base = - output_result(build.config.git().arg("merge-base").arg(&updated_master).arg("HEAD"))?; - Ok(Some( - output_result( - build.config.git().arg("diff-index").arg("--name-only").arg(merge_base.trim()), - )? - .lines() - .map(|s| s.trim().to_owned()) - .filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs")) - .collect(), - )) + get_git_modified_files(Some(&build.config.src), &vec!["rs"]) } -#[derive(serde::Deserialize)] +#[derive(serde_derive::Deserialize)] struct RustfmtConfig { ignore: Vec, } @@ -205,10 +193,46 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { let (tx, rx): (SyncSender, _) = std::sync::mpsc::sync_channel(128); let walker = match paths.get(0) { Some(first) => { - let mut walker = WalkBuilder::new(first); + let find_shortcut_candidates = |p: &PathBuf| { + let mut candidates = Vec::new(); + for candidate in WalkBuilder::new(src.clone()).max_depth(Some(3)).build() { + if let Ok(entry) = candidate { + if let Some(dir_name) = p.file_name() { + if entry.path().is_dir() && entry.file_name() == dir_name { + candidates.push(entry.into_path()); + } + } + } + } + candidates + }; + + // Only try to look for shortcut candidates for single component paths like + // `std` and not for e.g. relative paths like `../library/std`. + let should_look_for_shortcut_dir = |p: &PathBuf| p.components().count() == 1; + + let mut walker = if should_look_for_shortcut_dir(first) { + if let [single_candidate] = &find_shortcut_candidates(first)[..] { + WalkBuilder::new(single_candidate) + } else { + WalkBuilder::new(first) + } + } else { + WalkBuilder::new(src.join(first)) + }; + for path in &paths[1..] { - walker.add(path); + if should_look_for_shortcut_dir(path) { + if let [single_candidate] = &find_shortcut_candidates(path)[..] { + walker.add(single_candidate); + } else { + walker.add(path); + } + } else { + walker.add(src.join(path)); + } } + walker } None => WalkBuilder::new(src.clone()), diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 1815a0973..ac3843c33 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -243,18 +243,6 @@ install!((self, builder, _config), ); } }; - Analysis, alias = "analysis", Self::should_build(_config), only_hosts: false, { - // `expect` should be safe, only None with host != build, but this - // only uses the `build` compiler - let tarball = builder.ensure(dist::Analysis { - // Find the actual compiler (handling the full bootstrap option) which - // produced the save-analysis data because that data isn't copied - // through the sysroot uplifting. - compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target), - target: self.target - }).expect("missing analysis"); - install_sh(builder, "analysis", self.compiler.stage, Some(self.target), &tarball); - }; Rustc, path = "compiler/rustc", true, only_hosts: true, { let tarball = builder.ensure(dist::Rustc { compiler: builder.compiler(builder.top_stage, self.target), diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 0474ab344..950f3b151 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -11,93 +11,6 @@ //! crates.io and Cargo. //! * A standard interface to build across all platforms, including MSVC //! -//! ## Architecture -//! -//! The build system defers most of the complicated logic managing invocations -//! of rustc and rustdoc to Cargo itself. However, moving through various stages -//! and copying artifacts is still necessary for it to do. Each time rustbuild -//! is invoked, it will iterate through the list of predefined steps and execute -//! each serially in turn if it matches the paths passed or is a default rule. -//! For each step rustbuild relies on the step internally being incremental and -//! parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded -//! to appropriate test harnesses and such. -//! -//! Most of the "meaty" steps that matter are backed by Cargo, which does indeed -//! have its own parallelism and incremental management. Later steps, like -//! tests, aren't incremental and simply run the entire suite currently. -//! However, compiletest itself tries to avoid running tests when the artifacts -//! that are involved (mainly the compiler) haven't changed. -//! -//! When you execute `x.py build`, the steps executed are: -//! -//! * First, the python script is run. This will automatically download the -//! stage0 rustc and cargo according to `src/stage0.json`, or use the cached -//! versions if they're available. These are then used to compile rustbuild -//! itself (using Cargo). Finally, control is then transferred to rustbuild. -//! -//! * Rustbuild takes over, performs sanity checks, probes the environment, -//! reads configuration, and starts executing steps as it reads the command -//! line arguments (paths) or going through the default rules. -//! -//! The build output will be something like the following: -//! -//! Building stage0 std artifacts -//! Copying stage0 std -//! Building stage0 test artifacts -//! Copying stage0 test -//! Building stage0 compiler artifacts -//! Copying stage0 rustc -//! Assembling stage1 compiler -//! Building stage1 std artifacts -//! Copying stage1 std -//! Building stage1 test artifacts -//! Copying stage1 test -//! Building stage1 compiler artifacts -//! Copying stage1 rustc -//! Assembling stage2 compiler -//! Uplifting stage1 std -//! Uplifting stage1 test -//! Uplifting stage1 rustc -//! -//! Let's disect that a little: -//! -//! ## Building stage0 {std,test,compiler} artifacts -//! -//! These steps use the provided (downloaded, usually) compiler to compile the -//! local Rust source into libraries we can use. -//! -//! ## Copying stage0 {std,test,rustc} -//! -//! This copies the build output from Cargo into -//! `build/$HOST/stage0-sysroot/lib/rustlib/$ARCH/lib`. FIXME: this step's -//! documentation should be expanded -- the information already here may be -//! incorrect. -//! -//! ## Assembling stage1 compiler -//! -//! This copies the libraries we built in "building stage0 ... artifacts" into -//! the stage1 compiler's lib directory. These are the host libraries that the -//! compiler itself uses to run. These aren't actually used by artifacts the new -//! compiler generates. This step also copies the rustc and rustdoc binaries we -//! generated into build/$HOST/stage/bin. -//! -//! The stage1/bin/rustc is a fully functional compiler, but it doesn't yet have -//! any libraries to link built binaries or libraries to. The next 3 steps will -//! provide those libraries for it; they are mostly equivalent to constructing -//! the stage1/bin compiler so we don't go through them individually. -//! -//! ## Uplifting stage1 {std,test,rustc} -//! -//! This step copies the libraries from the stage1 compiler sysroot into the -//! stage2 compiler. This is done to avoid rebuilding the compiler; libraries -//! we'd build in this step should be identical (in function, if not necessarily -//! identical on disk) so there's no need to recompile the compiler again. Note -//! that if you want to, you can enable the full-bootstrap option to change this -//! behavior. -//! -//! Each step is driven by a separate Cargo project and rustbuild orchestrates -//! copying files between steps and otherwise preparing for Cargo to run. -//! //! ## Further information //! //! More documentation can be found in each respective module below, and you can @@ -110,7 +23,7 @@ use std::fs::{self, File}; use std::io; use std::io::ErrorKind; use std::path::{Path, PathBuf}; -use std::process::Command; +use std::process::{Command, Stdio}; use std::str; use build_helper::ci::CiEnv; @@ -203,7 +116,6 @@ const EXTRA_CHECK_CFGS: &[(Option, &'static str, Option<&[&'static str]>)] (None, "bootstrap", None), (Some(Mode::Rustc), "parallel_compiler", None), (Some(Mode::ToolRustc), "parallel_compiler", None), - (Some(Mode::ToolRustc), "emulate_second_only_system", None), (Some(Mode::Codegen), "parallel_compiler", None), (Some(Mode::Std), "stdarch_intel_sde", None), (Some(Mode::Std), "no_fp_fmt_parse", None), @@ -214,18 +126,9 @@ const EXTRA_CHECK_CFGS: &[(Option, &'static str, Option<&[&'static str]>)] (Some(Mode::Std), "backtrace_in_libstd", None), /* Extra values not defined in the built-in targets yet, but used in std */ (Some(Mode::Std), "target_env", Some(&["libnx"])), - (Some(Mode::Std), "target_os", Some(&["watchos"])), - ( - Some(Mode::Std), - "target_arch", - Some(&["asmjs", "spirv", "nvptx", "nvptx64", "le32", "xtensa"]), - ), + // (Some(Mode::Std), "target_os", Some(&[])), + (Some(Mode::Std), "target_arch", Some(&["asmjs", "spirv", "nvptx", "xtensa"])), /* Extra names used by dependencies */ - // FIXME: Used by rustfmt is their test but is invalid (neither cargo nor bootstrap ever set - // this config) should probably by removed or use a allow attribute. - (Some(Mode::ToolRustc), "release", None), - // FIXME: Used by stdarch in their test, should use a allow attribute instead. - (Some(Mode::Std), "dont_compile_me", None), // FIXME: Used by serde_json, but we should not be triggering on external dependencies. (Some(Mode::Rustc), "no_btreemap_remove_entry", None), (Some(Mode::ToolRustc), "no_btreemap_remove_entry", None), @@ -235,8 +138,12 @@ const EXTRA_CHECK_CFGS: &[(Option, &'static str, Option<&[&'static str]>)] // FIXME: Used by proc-macro2, but we should not be triggering on external dependencies. (Some(Mode::Rustc), "span_locations", None), (Some(Mode::ToolRustc), "span_locations", None), - // Can be passed in RUSTFLAGS to prevent direct syscalls in rustix. - (None, "rustix_use_libc", None), + // FIXME: Used by rustix, but we should not be triggering on external dependencies. + (Some(Mode::Rustc), "rustix_use_libc", None), + (Some(Mode::ToolRustc), "rustix_use_libc", None), + // FIXME: Used by filetime, but we should not be triggering on external dependencies. + (Some(Mode::Rustc), "emulate_second_only_system", None), + (Some(Mode::ToolRustc), "emulate_second_only_system", None), ]; /// A structure representing a Rust compiler. @@ -663,12 +570,32 @@ impl Build { // Try passing `--progress` to start, then run git again without if that fails. let update = |progress: bool| { - let mut git = Command::new("git"); + // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository, + // even though that has no relation to the upstream for the submodule. + let current_branch = { + let output = self + .config + .git() + .args(["symbolic-ref", "--short", "HEAD"]) + .stderr(Stdio::inherit()) + .output(); + let output = t!(output); + if output.status.success() { + Some(String::from_utf8(output.stdout).unwrap().trim().to_owned()) + } else { + None + } + }; + + let mut git = self.config.git(); + if let Some(branch) = current_branch { + git.arg("-c").arg(format!("branch.{branch}.remote=origin")); + } git.args(&["submodule", "update", "--init", "--recursive", "--depth=1"]); if progress { git.arg("--progress"); } - git.arg(relative_path).current_dir(&self.config.src); + git.arg(relative_path); git }; // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails. @@ -1432,6 +1359,14 @@ impl Build { return Vec::new(); } + if !stamp.exists() { + eprintln!( + "Error: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?", + stamp.display() + ); + crate::detail_exit(1); + } + let mut paths = Vec::new(); let contents = t!(fs::read(stamp), &stamp); // This is the method we use for extracting paths from the stamp file passed to us. See diff --git a/src/bootstrap/metadata.rs b/src/bootstrap/metadata.rs index e193e70a0..bba4d65e8 100644 --- a/src/bootstrap/metadata.rs +++ b/src/bootstrap/metadata.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use std::process::Command; -use serde::Deserialize; +use serde_derive::Deserialize; use crate::cache::INTERNER; use crate::util::output; diff --git a/src/bootstrap/metrics.rs b/src/bootstrap/metrics.rs index c823dc796..2e62c9507 100644 --- a/src/bootstrap/metrics.rs +++ b/src/bootstrap/metrics.rs @@ -7,7 +7,7 @@ use crate::builder::Step; use crate::util::t; use crate::Build; -use serde::{Deserialize, Serialize}; +use serde_derive::{Deserialize, Serialize}; use std::cell::RefCell; use std::fs::File; use std::io::BufWriter; diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index e0d1504c9..5987b641b 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -180,60 +180,60 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { // https://doc.rust-lang.org/rustc/platform-support.html#tier-1 let supported_platforms = [ // tier 1 - "aarch64-unknown-linux-gnu", - "i686-pc-windows-gnu", - "i686-pc-windows-msvc", - "i686-unknown-linux-gnu", - "x86_64-unknown-linux-gnu", - "x86_64-apple-darwin", - "x86_64-pc-windows-gnu", - "x86_64-pc-windows-msvc", + ("aarch64-unknown-linux-gnu", false), + ("i686-pc-windows-gnu", false), + ("i686-pc-windows-msvc", false), + ("i686-unknown-linux-gnu", false), + ("x86_64-unknown-linux-gnu", true), + ("x86_64-apple-darwin", true), + ("x86_64-pc-windows-gnu", true), + ("x86_64-pc-windows-msvc", true), // tier 2 with host tools - "aarch64-apple-darwin", - "aarch64-pc-windows-msvc", - "aarch64-unknown-linux-musl", - "arm-unknown-linux-gnueabi", - "arm-unknown-linux-gnueabihf", - "armv7-unknown-linux-gnueabihf", - "mips-unknown-linux-gnu", - "mips64-unknown-linux-gnuabi64", - "mips64el-unknown-linux-gnuabi64", - "mipsel-unknown-linux-gnu", - "powerpc-unknown-linux-gnu", - "powerpc64-unknown-linux-gnu", - "powerpc64le-unknown-linux-gnu", - "riscv64gc-unknown-linux-gnu", - "s390x-unknown-linux-gnu", - "x86_64-unknown-freebsd", - "x86_64-unknown-illumos", - "x86_64-unknown-linux-musl", - "x86_64-unknown-netbsd", + ("aarch64-apple-darwin", false), + ("aarch64-pc-windows-msvc", false), + ("aarch64-unknown-linux-musl", false), + ("arm-unknown-linux-gnueabi", false), + ("arm-unknown-linux-gnueabihf", false), + ("armv7-unknown-linux-gnueabihf", false), + ("mips-unknown-linux-gnu", false), + ("mips64-unknown-linux-gnuabi64", false), + ("mips64el-unknown-linux-gnuabi64", false), + ("mipsel-unknown-linux-gnu", false), + ("powerpc-unknown-linux-gnu", false), + ("powerpc64-unknown-linux-gnu", false), + ("powerpc64le-unknown-linux-gnu", false), + ("riscv64gc-unknown-linux-gnu", false), + ("s390x-unknown-linux-gnu", false), + ("x86_64-unknown-freebsd", false), + ("x86_64-unknown-illumos", false), + ("x86_64-unknown-linux-musl", false), + ("x86_64-unknown-netbsd", false), ]; - if !supported_platforms.contains(&&*config.build.triple) { - return false; + + if !supported_platforms.contains(&(&*config.build.triple, asserts)) { + if asserts == true || !supported_platforms.contains(&(&*config.build.triple, true)) { + return false; + } } - let triple = &*config.build.triple; - if (triple == "aarch64-unknown-linux-gnu" || triple.contains("i686")) && asserts { - // No alt builder for aarch64-unknown-linux-gnu today. + if is_ci_llvm_modified(config) { + eprintln!("Detected LLVM as non-available: running in CI and modified LLVM in this change"); return false; } - if CiEnv::is_ci() { + true +} + +/// Returns true if we're running in CI with modified LLVM (and thus can't download it) +pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool { + CiEnv::is_ci() && { // We assume we have access to git, so it's okay to unconditionally pass // `true` here. let llvm_sha = detect_llvm_sha(config, true); let head_sha = output(config.git().arg("rev-parse").arg("HEAD")); let head_sha = head_sha.trim(); - if llvm_sha == head_sha { - eprintln!( - "Detected LLVM as non-available: running in CI and modified LLVM in this change" - ); - return false; - } + llvm_sha == head_sha } - - true } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -486,7 +486,7 @@ impl Step for Llvm { cfg.define("LLVM_VERSION_SUFFIX", suffix); } - configure_cmake(builder, target, &mut cfg, true, ldflags); + configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); configure_llvm(builder, target, &mut cfg); for (key, val) in &builder.config.llvm_build_config { @@ -564,11 +564,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = output(cmd.arg("--version")); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { - if major >= 13 { + if major >= 14 { return; } } - panic!("\n\nbad LLVM version: {}, need >=13.0\n\n", version) + panic!("\n\nbad LLVM version: {}, need >=14.0\n\n", version) } fn configure_cmake( @@ -577,6 +577,7 @@ fn configure_cmake( cfg: &mut cmake::Config, use_compiler_launcher: bool, mut ldflags: LdFlags, + extra_compiler_flags: &[&str], ) { // Do not print installation messages for up-to-date files. // LLVM and LLD builds can produce a lot of those and hit CI limits on log size. @@ -717,6 +718,9 @@ fn configure_cmake( if builder.config.llvm_clang_cl.is_some() { cflags.push(&format!(" --target={}", target)); } + for flag in extra_compiler_flags { + cflags.push(&format!(" {}", flag)); + } cfg.define("CMAKE_C_FLAGS", cflags); let mut cxxflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::Cxx).join(" ").into(); if let Some(ref s) = builder.config.llvm_cxxflags { @@ -726,6 +730,9 @@ fn configure_cmake( if builder.config.llvm_clang_cl.is_some() { cxxflags.push(&format!(" --target={}", target)); } + for flag in extra_compiler_flags { + cxxflags.push(&format!(" {}", flag)); + } cfg.define("CMAKE_CXX_FLAGS", cxxflags); if let Some(ar) = builder.ar(target) { if ar.is_absolute() { @@ -867,7 +874,7 @@ impl Step for Lld { } } - configure_cmake(builder, target, &mut cfg, true, ldflags); + configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); configure_llvm(builder, target, &mut cfg); // Re-use the same flags as llvm to control the level of debug information @@ -1031,7 +1038,16 @@ impl Step for Sanitizers { // Unfortunately sccache currently lacks support to build them successfully. // Disable compiler launcher on Darwin targets to avoid potential issues. let use_compiler_launcher = !self.target.contains("apple-darwin"); - configure_cmake(builder, self.target, &mut cfg, use_compiler_launcher, LdFlags::default()); + let extra_compiler_flags: &[&str] = + if self.target.contains("apple") { &["-fembed-bitcode=off"] } else { &[] }; + configure_cmake( + builder, + self.target, + &mut cfg, + use_compiler_launcher, + LdFlags::default(), + extra_compiler_flags, + ); t!(fs::create_dir_all(&out_dir)); cfg.out_dir(out_dir); @@ -1087,12 +1103,15 @@ fn supported_sanitizers( match &*target.triple { "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), + "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan"]), + "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan"]), "aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-unknown-linux-gnu" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) } "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), + "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]), "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]), "x86_64-unknown-netbsd" => { common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"]) @@ -1105,6 +1124,12 @@ fn supported_sanitizers( "x86_64-unknown-linux-musl" => { common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) } + "s390x-unknown-linux-gnu" => { + common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"]) + } + "s390x-unknown-linux-musl" => { + common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"]) + } _ => Vec::new(), } } diff --git a/src/bootstrap/run.rs b/src/bootstrap/run.rs index e02808545..e14440f57 100644 --- a/src/bootstrap/run.rs +++ b/src/bootstrap/run.rs @@ -183,6 +183,7 @@ impl Step for Miri { // Forward arguments. miri.arg("--").arg("--target").arg(target.rustc_target_arg()); miri.args(builder.config.cmd.args()); + miri.args(&builder.config.free_args); // miri tests need to know about the stage sysroot miri.env("MIRI_SYSROOT", &miri_sysroot); diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 004601cb6..4480bce99 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -1,6 +1,7 @@ use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::Config; use crate::{t, VERSION}; +use sha2::Digest; use std::env::consts::EXE_SUFFIX; use std::fmt::Write as _; use std::fs::File; @@ -10,6 +11,9 @@ use std::process::Command; use std::str::FromStr; use std::{fmt, fs, io}; +#[cfg(test)] +mod tests; + #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum Profile { Compiler, @@ -17,8 +21,19 @@ pub enum Profile { Library, Tools, User, + None, } +/// A list of historical hashes of `src/etc/vscode_settings.json`. +/// New entries should be appended whenever this is updated so we can detect +/// outdated vs. user-modified settings files. +static SETTINGS_HASHES: &[&str] = &[ + "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", + "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", + "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", +]; +static VSCODE_SETTINGS: &str = include_str!("../etc/vscode_settings.json"); + impl Profile { fn include_path(&self, src_path: &Path) -> PathBuf { PathBuf::from(format!("{}/src/bootstrap/defaults/config.{}.toml", src_path.display(), self)) @@ -27,7 +42,7 @@ impl Profile { pub fn all() -> impl Iterator { use Profile::*; // N.B. these are ordered by how they are displayed, not alphabetically - [Library, Compiler, Codegen, Tools, User].iter().copied() + [Library, Compiler, Codegen, Tools, User, None].iter().copied() } pub fn purpose(&self) -> String { @@ -38,6 +53,7 @@ impl Profile { Codegen => "Contribute to the compiler, and also modify LLVM or codegen", Tools => "Contribute to tools which depend on the compiler, but do not modify it directly (e.g. rustdoc, clippy, miri)", User => "Install Rust from source", + None => "Do not modify `config.toml`" } .to_string() } @@ -57,6 +73,7 @@ impl Profile { Profile::Library => "library", Profile::Tools => "tools", Profile::User => "user", + Profile::None => "none", } } } @@ -73,6 +90,7 @@ impl FromStr for Profile { "tools" | "tool" | "rustdoc" | "clippy" | "miri" | "rustfmt" | "rls" => { Ok(Profile::Tools) } + "none" => Ok(Profile::None), _ => Err(format!("unknown profile: '{}'", s)), } } @@ -130,17 +148,8 @@ impl Step for Profile { } pub fn setup(config: &Config, profile: Profile) { - let stage_path = - ["build", config.build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string()); - - if !rustup_installed() && profile != Profile::User { - eprintln!("`rustup` is not installed; cannot link `stage1` toolchain"); - } else if stage_dir_exists(&stage_path[..]) && !config.dry_run() { - attempt_toolchain_link(&stage_path[..]); - } - - let suggestions = match profile { - Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..], + let suggestions: &[&str] = match profile { + Profile::Codegen | Profile::Compiler | Profile::None => &["check", "build", "test"], Profile::Tools => &[ "check", "build", @@ -153,10 +162,6 @@ pub fn setup(config: &Config, profile: Profile) { Profile::User => &["dist", "build"], }; - if !config.dry_run() { - t!(install_git_hook_maybe(&config)); - } - println!(); println!("To get started, try one of the following commands:"); @@ -175,6 +180,9 @@ pub fn setup(config: &Config, profile: Profile) { } fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) { + if profile == Profile::None { + return; + } if path.exists() { eprintln!(); eprintln!( @@ -202,6 +210,41 @@ fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) { println!("`x.py` will now use the configuration at {}", include_path.display()); } +/// Creates a toolchain link for stage1 using `rustup` +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct Link; +impl Step for Link { + type Output = (); + const DEFAULT: bool = true; + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("link") + } + fn make_run(run: RunConfig<'_>) { + if run.builder.config.dry_run() { + return; + } + if let [cmd] = &run.paths[..] { + if cmd.assert_single_path().path.as_path().as_os_str() == "link" { + run.builder.ensure(Link); + } + } + } + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + let stage_path = + ["build", config.build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string()); + + if !rustup_installed() { + eprintln!("`rustup` is not installed; cannot link `stage1` toolchain"); + } else if stage_dir_exists(&stage_path[..]) && !config.dry_run() { + attempt_toolchain_link(&stage_path[..]); + } + } +} + fn rustup_installed() -> bool { Command::new("rustup") .arg("--version") @@ -351,6 +394,63 @@ pub fn interactive_path() -> io::Result { Ok(template) } +#[derive(PartialEq)] +enum PromptResult { + Yes, // y/Y/yes + No, // n/N/no + Print, // p/P/print +} + +/// Prompt a user for a answer, looping until they enter an accepted input or nothing +fn prompt_user(prompt: &str) -> io::Result> { + let mut input = String::new(); + loop { + print!("{prompt} "); + io::stdout().flush()?; + input.clear(); + io::stdin().read_line(&mut input)?; + match input.trim().to_lowercase().as_str() { + "y" | "yes" => return Ok(Some(PromptResult::Yes)), + "n" | "no" => return Ok(Some(PromptResult::No)), + "p" | "print" => return Ok(Some(PromptResult::Print)), + "" => return Ok(None), + _ => { + eprintln!("error: unrecognized option '{}'", input.trim()); + eprintln!("note: press Ctrl+C to exit"); + } + }; + } +} + +/// Installs `src/etc/pre-push.sh` as a Git hook +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct Hook; + +impl Step for Hook { + type Output = (); + const DEFAULT: bool = true; + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("hook") + } + fn make_run(run: RunConfig<'_>) { + if run.builder.config.dry_run() { + return; + } + if let [cmd] = &run.paths[..] { + if cmd.assert_single_path().path.as_path().as_os_str() == "hook" { + run.builder.ensure(Hook); + } + } + } + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + t!(install_git_hook_maybe(&config)); + } +} + // install a git hook to automatically run tidy, if they want fn install_git_hook_maybe(config: &Config) -> io::Result<()> { let git = t!(config.git().args(&["rev-parse", "--git-common-dir"]).output().map(|output| { @@ -363,43 +463,127 @@ fn install_git_hook_maybe(config: &Config) -> io::Result<()> { return Ok(()); } - let mut input = String::new(); - println!(); println!( - "Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality. + "\nRust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality. If you'd like, x.py can install a git hook for you that will automatically run `test tidy` before pushing your code to ensure your code is up to par. If you decide later that this behavior is undesirable, simply delete the `pre-push` file from .git/hooks." ); - let should_install = loop { - print!("Would you like to install the git hook?: [y/N] "); - io::stdout().flush()?; - input.clear(); - io::stdin().read_line(&mut input)?; - break match input.trim().to_lowercase().as_str() { - "y" | "yes" => true, - "n" | "no" | "" => false, - _ => { - eprintln!("error: unrecognized option '{}'", input.trim()); - eprintln!("note: press Ctrl+C to exit"); - continue; - } - }; - }; - - if should_install { - let src = config.src.join("src").join("etc").join("pre-push.sh"); - match fs::hard_link(src, &dst) { - Err(e) => eprintln!( + if prompt_user("Would you like to install the git hook?: [y/N]")? != Some(PromptResult::Yes) { + println!("Ok, skipping installation!"); + return Ok(()); + } + let src = config.src.join("src").join("etc").join("pre-push.sh"); + match fs::hard_link(src, &dst) { + Err(e) => { + eprintln!( "error: could not create hook {}: do you already have the git hook installed?\n{}", dst.display(), e - ), - Ok(_) => println!("Linked `src/etc/pre-push.sh` to `.git/hooks/pre-push`"), + ); + return Err(e); + } + Ok(_) => println!("Linked `src/etc/pre-push.sh` to `.git/hooks/pre-push`"), + }; + Ok(()) +} + +/// Sets up or displays `src/etc/vscode_settings.json` +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct Vscode; + +impl Step for Vscode { + type Output = (); + const DEFAULT: bool = true; + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("vscode") + } + fn make_run(run: RunConfig<'_>) { + if run.builder.config.dry_run() { + return; + } + if let [cmd] = &run.paths[..] { + if cmd.assert_single_path().path.as_path().as_os_str() == "vscode" { + run.builder.ensure(Vscode); + } + } + } + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + t!(create_vscode_settings_maybe(&config)); + } +} + +/// Create a `.vscode/settings.json` file for rustc development, or just print it +fn create_vscode_settings_maybe(config: &Config) -> io::Result<()> { + let (current_hash, historical_hashes) = SETTINGS_HASHES.split_last().unwrap(); + let vscode_settings = config.src.join(".vscode").join("settings.json"); + // If None, no settings.json exists + // If Some(true), is a previous version of settings.json + // If Some(false), is not a previous version (i.e. user modified) + // If it's up to date we can just skip this + let mut mismatched_settings = None; + if let Ok(current) = fs::read_to_string(&vscode_settings) { + let mut hasher = sha2::Sha256::new(); + hasher.update(¤t); + let hash = hex::encode(hasher.finalize().as_slice()); + if hash == *current_hash { + return Ok(()); + } else if historical_hashes.contains(&hash.as_str()) { + mismatched_settings = Some(true); + } else { + mismatched_settings = Some(false); + } + } + println!( + "\nx.py can automatically install the recommended `.vscode/settings.json` file for rustc development" + ); + match mismatched_settings { + Some(true) => eprintln!( + "warning: existing `.vscode/settings.json` is out of date, x.py will update it" + ), + Some(false) => eprintln!( + "warning: existing `.vscode/settings.json` has been modified by user, x.py will back it up and replace it" + ), + _ => (), + } + let should_create = match prompt_user( + "Would you like to create/update `settings.json`, or only print suggested settings?: [y/p/N]", + )? { + Some(PromptResult::Yes) => true, + Some(PromptResult::Print) => false, + _ => { + println!("Ok, skipping settings!"); + return Ok(()); + } + }; + if should_create { + let path = config.src.join(".vscode"); + if !path.exists() { + fs::create_dir(&path)?; + } + let verb = match mismatched_settings { + // exists but outdated, we can replace this + Some(true) => "Updated", + // exists but user modified, back it up + Some(false) => { + // exists and is not current version or outdated, so back it up + let mut backup = vscode_settings.clone(); + backup.set_extension("bak"); + eprintln!("warning: copying `settings.json` to `settings.json.bak`"); + fs::copy(&vscode_settings, &backup)?; + "Updated" + } + _ => "Created", }; + fs::write(&vscode_settings, &VSCODE_SETTINGS)?; + println!("{verb} `.vscode/settings.json`"); } else { - println!("Ok, skipping installation!"); + println!("\n{VSCODE_SETTINGS}"); } Ok(()) } diff --git a/src/bootstrap/setup/tests.rs b/src/bootstrap/setup/tests.rs new file mode 100644 index 000000000..dcf9d18e6 --- /dev/null +++ b/src/bootstrap/setup/tests.rs @@ -0,0 +1,14 @@ +use super::{SETTINGS_HASHES, VSCODE_SETTINGS}; +use sha2::Digest; + +#[test] +fn check_matching_settings_hash() { + let mut hasher = sha2::Sha256::new(); + hasher.update(&VSCODE_SETTINGS); + let hash = hex::encode(hasher.finalize().as_slice()); + assert_eq!( + &hash, + SETTINGS_HASHES.last().unwrap(), + "Update `SETTINGS_HASHES` with the new hash of `src/etc/vscode_settings.json`" + ); +} diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 6078e39ac..b4f1506dc 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -435,6 +435,10 @@ impl Step for Rustfmt { &[], ); + if !builder.fail_fast { + cargo.arg("--no-fail-fast"); + } + let dir = testdir(builder, compiler.host); t!(fs::create_dir_all(&dir)); cargo.env("RUSTFMT_TEST_DIR", dir); @@ -615,6 +619,10 @@ impl Step for Miri { ); cargo.add_rustc_lib_path(builder, compiler); + if !builder.fail_fast { + cargo.arg("--no-fail-fast"); + } + // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", sysroot); @@ -746,6 +754,10 @@ impl Step for Clippy { &[], ); + if !builder.fail_fast { + cargo.arg("--no-fail-fast"); + } + cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir()); @@ -1114,9 +1126,6 @@ impl Step for Tidy { cmd.arg("--bless"); } - builder.info("tidy check"); - try_run(builder, &mut cmd); - if builder.config.channel == "dev" || builder.config.channel == "nightly" { builder.info("fmt check"); if builder.initial_rustfmt().is_none() { @@ -1134,6 +1143,11 @@ help: to skip test's attempt to check tidiness, pass `--exclude src/tools/tidy` } crate::format::format(&builder, !builder.config.cmd.bless(), &[]); } + + builder.info("tidy check"); + try_run(builder, &mut cmd); + + builder.ensure(ExpandYamlAnchors {}); } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1508,6 +1522,10 @@ note: if you're sure you want to do this, please open an issue as to why. In the if builder.config.rust_optimize_tests { cmd.arg("--optimize-tests"); } + if builder.config.cmd.only_modified() { + cmd.arg("--only-modified"); + } + let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] }; flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests)); flags.extend(builder.config.cmd.rustc_args().iter().map(|s| s.to_string())); @@ -1582,6 +1600,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the .collect(); test_args.append(&mut builder.config.cmd.test_args()); + test_args.extend(builder.config.free_args.iter().map(|s| s.as_str())); // On Windows, replace forward slashes in test-args by backslashes // so the correct filters are passed to libtest diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 9a2100c2f..3c9a154da 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -33,6 +33,44 @@ struct ToolBuild { allow_features: &'static str, } +fn tooling_output( + mode: Mode, + tool: &str, + build_stage: u32, + host: &TargetSelection, + target: &TargetSelection, +) -> String { + match mode { + // depends on compiler stage, different to host compiler + Mode::ToolRustc => { + if host == target { + format!("Building tool {} (stage{} -> stage{})", tool, build_stage, build_stage + 1) + } else { + format!( + "Building tool {} (stage{}:{} -> stage{}:{})", + tool, + build_stage, + host, + build_stage + 1, + target + ) + } + } + // doesn't depend on compiler, same as host compiler + Mode::ToolStd => { + if host == target { + format!("Building tool {} (stage{})", tool, build_stage) + } else { + format!( + "Building tool {} (stage{}:{} -> stage{}:{})", + tool, build_stage, host, build_stage, target + ) + } + } + _ => format!("Building tool {} (stage{})", tool, build_stage), + } +} + impl Step for ToolBuild { type Output = Option; @@ -74,8 +112,14 @@ impl Step for ToolBuild { if !self.allow_features.is_empty() { cargo.allow_features(self.allow_features); } - - builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target)); + let msg = tooling_output( + self.mode, + self.tool, + self.compiler.stage, + &self.compiler.host, + &self.target, + ); + builder.info(&msg); let mut duplicates = Vec::new(); let is_expected = compile::stream_cargo(builder, cargo, vec![], &mut |msg| { // Only care about big things like the RLS/Cargo for now @@ -551,7 +595,7 @@ impl Step for Rustdoc { features.push("jemalloc".to_string()); } - let cargo = prepare_tool_cargo( + let mut cargo = prepare_tool_cargo( builder, build_compiler, Mode::ToolRustc, @@ -562,10 +606,18 @@ impl Step for Rustdoc { features.as_slice(), ); - builder.info(&format!( - "Building rustdoc for stage{} ({})", - target_compiler.stage, target_compiler.host - )); + if builder.config.rustc_parallel { + cargo.rustflag("--cfg=parallel_compiler"); + } + + let msg = tooling_output( + Mode::ToolRustc, + "rustdoc", + build_compiler.stage, + &self.compiler.host, + &target, + ); + builder.info(&msg); builder.run(&mut cargo.into()); // Cargo adds a number of paths to the dylib search path on windows, which results in @@ -765,9 +817,15 @@ impl Step for RustAnalyzerProcMacroSrv { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let builder = run.builder; // Allow building `rust-analyzer-proc-macro-srv` both as part of the `rust-analyzer` and as a stand-alone tool. run.path("src/tools/rust-analyzer") .path("src/tools/rust-analyzer/crates/proc-macro-srv-cli") + .default_condition(builder.config.tools.as_ref().map_or(true, |tools| { + tools + .iter() + .any(|tool| tool == "rust-analyzer" || tool == "rust-analyzer-proc-macro-srv") + })) } fn make_run(run: RunConfig<'_>) { diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs index 1969e0b6f..7aab88a1a 100644 --- a/src/bootstrap/toolstate.rs +++ b/src/bootstrap/toolstate.rs @@ -1,6 +1,6 @@ use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::util::t; -use serde::{Deserialize, Serialize}; +use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; use std::env; use std::fmt; diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile index 43a449b3a..adb98d7eb 100644 --- a/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile @@ -28,5 +28,5 @@ ENV \ ENV HOSTS=s390x-unknown-linux-gnu -ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-profiler --disable-docs +ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-sanitizers --enable-profiler --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 6bdc88e18..5feba4e06 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -81,7 +81,7 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.jemalloc \ --set rust.use-lld=true \ --set rust.lto=thin -ENV SCRIPT ../src/ci/pgo.sh python3 ../x.py dist \ +ENV SCRIPT python3 ../src/ci/stage-build.py python3 ../x.py dist \ --host $HOSTS --target $HOSTS \ --include-default-paths \ build-manifest bootstrap diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 4cc5d9f8a..98bd90210 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -23,6 +23,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN curl -sL https://nodejs.org/dist/v16.9.0/node-v16.9.0-linux-x64.tar.xz | tar -xJ ENV PATH="/node-v16.9.0-linux-x64/bin:${PATH}" +ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" + # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. RUN npm install es-check@6.1.1 eslint@8.6.0 -g @@ -38,7 +40,7 @@ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ - python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu --all-targets && \ + python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py test --stage 0 core alloc std test proc_macro && \ diff --git a/src/ci/docker/host-x86_64/mingw-check/validate-toolstate.sh b/src/ci/docker/host-x86_64/mingw-check/validate-toolstate.sh index c6d728eb8..0b06f5e36 100755 --- a/src/ci/docker/host-x86_64/mingw-check/validate-toolstate.sh +++ b/src/ci/docker/host-x86_64/mingw-check/validate-toolstate.sh @@ -9,11 +9,5 @@ git clone --depth=1 https://github.com/rust-lang-nursery/rust-toolstate.git cd rust-toolstate python3 "../../src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" \ "$(git log --format=%s -n1 HEAD)" "" "" -# Only check maintainers if this build is supposed to publish toolstate. -# Builds that are not supposed to publish don't have the access token. -if [ -n "${TOOLSTATE_PUBLISH+is_set}" ]; then - TOOLSTATE_VALIDATE_MAINTAINERS_REPO=rust-lang/rust python3 \ - "../../src/tools/publish_toolstate.py" -fi cd .. rm -rf rust-toolstate diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-13-stage1/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-13-stage1/Dockerfile deleted file mode 100644 index bcbf58253..000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-13-stage1/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -FROM ubuntu:22.04 - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python2.7 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-13-tools \ - llvm-13-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - && rm -rf /var/lib/apt/lists/* - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-13 \ - --enable-llvm-link-shared \ - --set rust.thin-lto-import-instr-limit=10 - -ENV SCRIPT python2.7 ../x.py --stage 1 test --exclude src/tools/tidy && \ - # Run the `mir-opt` tests again but this time for a 32-bit target. - # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have - # both 32-bit and 64-bit outputs updated by the PR author, before - # the PR is approved and tested for merging. - # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, - # despite having different output on 32-bit vs 64-bit targets. - python2.7 ../x.py --stage 1 test tests/mir-opt \ - --host='' --target=i686-unknown-linux-gnu diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-13/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-13/Dockerfile deleted file mode 100644 index 9fc9e9cbf..000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-13/Dockerfile +++ /dev/null @@ -1,69 +0,0 @@ -FROM ubuntu:22.04 - -ARG DEBIAN_FRONTEND=noninteractive - -# NOTE: intentionally installs both python2 and python3 so we can test support for both. -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python2.7 \ - python3.9 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-13-tools \ - llvm-13-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - \ -# Install powershell so we can test x.ps1 on Linux - apt-transport-https software-properties-common && \ - curl -s "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb" > packages-microsoft-prod.deb && \ - dpkg -i packages-microsoft-prod.deb && \ - apt-get update && \ - apt-get install -y powershell \ - && rm -rf /var/lib/apt/lists/* - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-13 \ - --enable-llvm-link-shared \ - --set rust.thin-lto-import-instr-limit=10 - -# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux. -ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \ - # Run the `mir-opt` tests again but this time for a 32-bit target. - # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have - # both 32-bit and 64-bit outputs updated by the PR author, before - # the PR is approved and tested for merging. - # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, - # despite having different output on 32-bit vs 64-bit targets. - ../x --stage 2 test tests/mir-opt \ - --host='' --target=i686-unknown-linux-gnu && \ - # Run the UI test suite again, but in `--pass=check` mode - # - # This is intended to make sure that both `--pass=check` continues to - # work. - # - ../x.ps1 --stage 2 test tests/ui --pass=check \ - --host='' --target=i686-unknown-linux-gnu && \ - # Run tidy at the very end, after all the other tests. - python2.7 ../x.py --stage 2 test src/tools/tidy diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14-stage1/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14-stage1/Dockerfile new file mode 100644 index 000000000..b99a0886b --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14-stage1/Dockerfile @@ -0,0 +1,49 @@ +FROM ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + gcc-multilib \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-14-tools \ + llvm-14-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# We are disabling CI LLVM since this builder is intentionally using a host +# LLVM, rather than the typical src/llvm-project LLVM. +ENV NO_DOWNLOAD_CI_LLVM 1 + +# Using llvm-link-shared due to libffi issues -- see #34486 +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --llvm-root=/usr/lib/llvm-14 \ + --enable-llvm-link-shared \ + --set rust.thin-lto-import-instr-limit=10 + +ENV SCRIPT ../x.py --stage 1 test --exclude src/tools/tidy && \ + # Run the `mir-opt` tests again but this time for a 32-bit target. + # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have + # both 32-bit and 64-bit outputs updated by the PR author, before + # the PR is approved and tested for merging. + # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, + # despite having different output on 32-bit vs 64-bit targets. + ../x.py --stage 1 test tests/mir-opt \ + --host='' --target=i686-unknown-linux-gnu diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile new file mode 100644 index 000000000..db6032f87 --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile @@ -0,0 +1,67 @@ +FROM ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive + +# NOTE: intentionally installs both python2 and python3 so we can test support for both. +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + gcc-multilib \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python2.7 \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-14-tools \ + llvm-14-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs \ + && rm -rf /var/lib/apt/lists/* + +# Install powershell (universal package) so we can test x.ps1 on Linux +RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ + dpkg -i powershell.deb && \ + rm -f powershell.deb + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# We are disabling CI LLVM since this builder is intentionally using a host +# LLVM, rather than the typical src/llvm-project LLVM. +ENV NO_DOWNLOAD_CI_LLVM 1 + +# Using llvm-link-shared due to libffi issues -- see #34486 +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --llvm-root=/usr/lib/llvm-14 \ + --enable-llvm-link-shared \ + --set rust.thin-lto-import-instr-limit=10 + +# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux. +ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \ + # Run the `mir-opt` tests again but this time for a 32-bit target. + # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have + # both 32-bit and 64-bit outputs updated by the PR author, before + # the PR is approved and tested for merging. + # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, + # despite having different output on 32-bit vs 64-bit targets. + ../x --stage 2 test tests/mir-opt \ + --host='' --target=i686-unknown-linux-gnu && \ + # Run the UI test suite again, but in `--pass=check` mode + # + # This is intended to make sure that both `--pass=check` continues to + # work. + # + ../x.ps1 --stage 2 test tests/ui --pass=check \ + --host='' --target=i686-unknown-linux-gnu && \ + # Run tidy at the very end, after all the other tests. + python2.7 ../x.py --stage 2 test src/tools/tidy diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/Dockerfile new file mode 100644 index 000000000..5219247cc --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/Dockerfile @@ -0,0 +1,67 @@ +FROM ubuntu:22.10 + +ARG DEBIAN_FRONTEND=noninteractive + +# NOTE: intentionally installs both python2 and python3 so we can test support for both. +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + gcc-multilib \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python2.7 \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-15-tools \ + llvm-15-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs \ + && rm -rf /var/lib/apt/lists/* + +# Install powershell (universal package) so we can test x.ps1 on Linux +RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ + dpkg -i powershell.deb && \ + rm -f powershell.deb + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# We are disabling CI LLVM since this builder is intentionally using a host +# LLVM, rather than the typical src/llvm-project LLVM. +ENV NO_DOWNLOAD_CI_LLVM 1 + +# Using llvm-link-shared due to libffi issues -- see #34486 +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --llvm-root=/usr/lib/llvm-15 \ + --enable-llvm-link-shared \ + --set rust.thin-lto-import-instr-limit=10 + +# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux. +ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \ + # Run the `mir-opt` tests again but this time for a 32-bit target. + # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have + # both 32-bit and 64-bit outputs updated by the PR author, before + # the PR is approved and tested for merging. + # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, + # despite having different output on 32-bit vs 64-bit targets. + ../x --stage 2 test tests/mir-opt \ + --host='' --target=i686-unknown-linux-gnu && \ + # Run the UI test suite again, but in `--pass=check` mode + # + # This is intended to make sure that both `--pass=check` continues to + # work. + # + ../x.ps1 --stage 2 test tests/ui --pass=check \ + --host='' --target=i686-unknown-linux-gnu && \ + # Run tidy at the very end, after all the other tests. + python2.7 ../x.py --stage 2 test src/tools/tidy diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index c39e9c5fb..9c550b2d7 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.14.1 \ No newline at end of file +0.14.4 \ No newline at end of file diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py index c8d1ff9ae..e7d1d9781 100755 --- a/src/ci/docker/scripts/fuchsia-test-runner.py +++ b/src/ci/docker/scripts/fuchsia-test-runner.py @@ -9,6 +9,7 @@ https://doc.rust-lang.org/stable/rustc/platform-support/fuchsia.html#aarch64-unk import argparse from dataclasses import dataclass +import fcntl import glob import hashlib import json @@ -19,18 +20,18 @@ import shutil import signal import subprocess import sys -from typing import ClassVar, List +from typing import ClassVar, List, Optional @dataclass class TestEnvironment: rust_dir: str sdk_dir: str - target_arch: str - package_server_pid: int = None - emu_addr: str = None - libstd_name: str = None - libtest_name: str = None + target: str + package_server_pid: Optional[int] = None + emu_addr: Optional[str] = None + libstd_name: Optional[str] = None + libtest_name: Optional[str] = None verbose: bool = False @staticmethod @@ -40,6 +41,15 @@ class TestEnvironment: return os.path.abspath(tmp_dir) return os.path.join(os.path.dirname(__file__), "tmp~") + @staticmethod + def triple_to_arch(triple): + if "x86_64" in triple: + return "x64" + elif "aarch64" in triple: + return "arm64" + else: + raise Exception(f"Unrecognized target triple {triple}") + @classmethod def env_file_path(cls): return os.path.join(cls.tmp_dir(), "test_env.json") @@ -49,7 +59,7 @@ class TestEnvironment: return cls( os.path.abspath(args.rust), os.path.abspath(args.sdk), - args.target_arch, + args.target, verbose=args.verbose, ) @@ -60,7 +70,7 @@ class TestEnvironment: return cls( test_env["rust_dir"], test_env["sdk_dir"], - test_env["target_arch"], + test_env["target"], libstd_name=test_env["libstd_name"], libtest_name=test_env["libtest_name"], emu_addr=test_env["emu_addr"], @@ -68,13 +78,6 @@ class TestEnvironment: verbose=test_env["verbose"], ) - def image_name(self): - if self.target_arch == "x64": - return "qemu-x64" - if self.target_arch == "arm64": - return "qemu-arm64" - raise Exception(f"Unrecognized target architecture {self.target_arch}") - def write_to_file(self): with open(self.env_file_path(), "w", encoding="utf-8") as f: f.write(json.dumps(self.__dict__)) @@ -108,13 +111,6 @@ class TestEnvironment: def repo_dir(self): return os.path.join(self.tmp_dir(), self.TEST_REPO_NAME) - def rustlib_dir(self): - if self.target_arch == "x64": - return "x86_64-unknown-fuchsia" - if self.target_arch == "arm64": - return "aarch64-unknown-fuchsia" - raise Exception(f"Unrecognized target architecture {self.target_arch}") - def libs_dir(self): return os.path.join( self.rust_dir, @@ -125,7 +121,7 @@ class TestEnvironment: return os.path.join( self.libs_dir(), "rustlib", - self.rustlib_dir(), + self.target, "lib", ) @@ -151,6 +147,9 @@ class TestEnvironment: def zxdb_script_path(self): return os.path.join(self.tmp_dir(), "zxdb_script") + def pm_lockfile_path(self): + return os.path.join(self.tmp_dir(), "pm.lock") + def log_info(self, msg): print(msg) @@ -384,7 +383,7 @@ class TestEnvironment: "--emulator-log", self.emulator_log_path(), "--image-name", - self.image_name(), + "qemu-" + self.triple_to_arch(self.target), ], stdout=self.subprocess_output(), stderr=self.subprocess_output(), @@ -465,6 +464,9 @@ class TestEnvironment: stderr=self.subprocess_output(), ) + # Create lockfiles + open(self.pm_lockfile_path(), 'a').close() + # Write to file self.write_to_file() @@ -512,9 +514,8 @@ class TestEnvironment: bin/{exe_name}={bin_path} lib/{libstd_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libstd_name} lib/{libtest_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libtest_name} - lib/ld.so.1={sdk_dir}/arch/{target_arch}/sysroot/lib/libc.so - lib/libzircon.so={sdk_dir}/arch/{target_arch}/sysroot/lib/libzircon.so - lib/libfdio.so={sdk_dir}/arch/{target_arch}/lib/libfdio.so + lib/ld.so.1={sdk_dir}/arch/{target_arch}/sysroot/dist/lib/ld.so.1 + lib/libfdio.so={sdk_dir}/arch/{target_arch}/dist/libfdio.so """ TEST_ENV_VARS: ClassVar[List[str]] = [ @@ -642,11 +643,11 @@ class TestEnvironment: package_dir=package_dir, package_name=package_name, rust_dir=self.rust_dir, - rustlib_dir=self.rustlib_dir(), + rustlib_dir=self.target, sdk_dir=self.sdk_dir, libstd_name=self.libstd_name, libtest_name=self.libtest_name, - target_arch=self.target_arch, + target_arch=self.triple_to_arch(self.target), ) ) for shared_lib in shared_libs: @@ -682,19 +683,25 @@ class TestEnvironment: log("Publishing package to repo...") # Publish package to repo - subprocess.check_call( - [ - self.tool_path("pm"), - "publish", - "-a", - "-repo", - self.repo_dir(), - "-f", - far_path, - ], - stdout=log_file, - stderr=log_file, - ) + with open(self.pm_lockfile_path(), 'w') as pm_lockfile: + fcntl.lockf(pm_lockfile.fileno(), fcntl.LOCK_EX) + subprocess.check_call( + [ + self.tool_path("pm"), + "publish", + "-a", + "-repo", + self.repo_dir(), + "-f", + far_path, + ], + stdout=log_file, + stderr=log_file, + ) + # This lock should be released automatically when the pm + # lockfile is closed, but we'll be polite and unlock it now + # since the spec leaves some wiggle room. + fcntl.lockf(pm_lockfile.fileno(), fcntl.LOCK_UN) log("Running ffx test...") @@ -849,23 +856,34 @@ class TestEnvironment: "--", "--build-id-dir", os.path.join(self.sdk_dir, ".build-id"), - "--build-id-dir", - os.path.join(self.libs_dir(), ".build-id"), ] - # Add rust source if it's available - if args.rust_src is not None: + libs_build_id_path = os.path.join(self.libs_dir(), ".build-id") + if os.path.exists(libs_build_id_path): + # Add .build-id symbols if installed libs have been stripped into a + # .build-id directory command += [ - "--build-dir", - args.rust_src, + "--build-id-dir", + libs_build_id_path, + ] + else: + # If no .build-id directory is detected, then assume that the shared + # libs contain their debug symbols + command += [ + f"--symbol-path={self.rust_dir}/lib/rustlib/{self.target}/lib", ] + # Add rust source if it's available + rust_src_map = None + if args.rust_src is not None: + # This matches the remapped prefix used by compiletest. There's no + # clear way that we can determine this, so it's hard coded. + rust_src_map = f"/rustc/FAKE_PREFIX={args.rust_src}" + # Add fuchsia source if it's available + fuchsia_src_map = None if args.fuchsia_src is not None: - command += [ - "--build-dir", - os.path.join(args.fuchsia_src, "out", "default"), - ] + fuchsia_src_map = f"./../..={args.fuchsia_src}" # Load debug symbols for the test binary and automatically attach if args.test is not None: @@ -888,7 +906,28 @@ class TestEnvironment: test_name, ) + # The fake-test-src-base directory maps to the suite directory + # e.g. tests/ui/foo.rs has a path of rust/fake-test-src-base/foo.rs + fake_test_src_base = os.path.join( + args.rust_src, + "fake-test-src-base", + ) + real_test_src_base = os.path.join( + args.rust_src, + "tests", + args.test.split(os.path.sep)[0], + ) + test_src_map = f"{fake_test_src_base}={real_test_src_base}" + with open(self.zxdb_script_path(), mode="w", encoding="utf-8") as f: + print(f"set source-map += {test_src_map}", file=f) + + if rust_src_map is not None: + print(f"set source-map += {rust_src_map}", file=f) + + if fuchsia_src_map is not None: + print(f"set source-map += {fuchsia_src_map}", file=f) + print(f"attach {test_name[:31]}", file=f) command += [ @@ -905,6 +944,20 @@ class TestEnvironment: # Connect to the running emulator with zxdb subprocess.run(command, env=self.ffx_cmd_env(), check=False) + def syslog(self, args): + subprocess.run( + [ + self.tool_path("ffx"), + "--config", + self.ffx_user_config_path(), + "log", + "--since", + "now", + ], + env=self.ffx_cmd_env(), + check=False, + ) + def start(args): test_env = TestEnvironment.from_args(args) @@ -938,6 +991,12 @@ def debug(args): return 0 +def syslog(args): + test_env = TestEnvironment.read_from_file() + test_env.syslog(args) + return 0 + + def main(): parser = argparse.ArgumentParser() @@ -969,8 +1028,8 @@ def main(): action="store_true", ) start_parser.add_argument( - "--target-arch", - help="the architecture of the image to test", + "--target", + help="the target platform to test", required=True, ) start_parser.set_defaults(func=start) @@ -1033,6 +1092,11 @@ def main(): ) debug_parser.set_defaults(func=debug) + syslog_parser = subparsers.add_parser( + "syslog", help="prints the device syslog" + ) + syslog_parser.set_defaults(func=syslog) + args = parser.parse_args() return args.func(args) diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 743b57ed4..c594288dc 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -33,6 +33,7 @@ x--expand-yaml-anchors--remove: - &shared-ci-variables CI_JOB_NAME: ${{ matrix.name }} + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - &public-variables SCCACHE_BUCKET: rust-lang-ci-sccache2 @@ -73,15 +74,15 @@ x--expand-yaml-anchors--remove: env: {} - &job-linux-xl - os: ubuntu-20.04-xl + os: ubuntu-20.04-16core-64gb <<: *base-job - &job-macos-xl - os: macos-latest # We don't have an XL builder for this + os: macos-12-xl <<: *base-job - &job-windows-xl - os: windows-latest-xl + os: windows-2019-8core-32gb <<: *base-job - &job-aarch64-linux @@ -299,15 +300,13 @@ jobs: <<: *job-linux-xl tidy: true - - name: x86_64-gnu-llvm-13 + - name: x86_64-gnu-llvm-14 <<: *job-linux-xl tidy: false - name: x86_64-gnu-tools <<: *job-linux-xl tidy: false - env: - CI_ONLY_WHEN_SUBMODULES_CHANGED: 1 auto: permissions: @@ -450,12 +449,17 @@ jobs: - name: x86_64-gnu-distcheck <<: *job-linux-xl - - name: x86_64-gnu-llvm-13 + - name: x86_64-gnu-llvm-15 + env: + RUST_BACKTRACE: 1 + <<: *job-linux-xl + + - name: x86_64-gnu-llvm-14 env: RUST_BACKTRACE: 1 <<: *job-linux-xl - - name: x86_64-gnu-llvm-13-stage1 + - name: x86_64-gnu-llvm-14-stage1 env: RUST_BACKTRACE: 1 <<: *job-linux-xl @@ -619,9 +623,7 @@ jobs: - name: i686-mingw-1 env: - RUST_CONFIGURE_ARGS: >- - --build=i686-pc-windows-gnu - --set llvm.allow-old-toolchain + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu SCRIPT: make ci-mingw-subset-1 # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). @@ -631,9 +633,7 @@ jobs: - name: i686-mingw-2 env: - RUST_CONFIGURE_ARGS: >- - --build=i686-pc-windows-gnu - --set llvm.allow-old-toolchain + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu SCRIPT: make ci-mingw-subset-2 # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). @@ -647,7 +647,6 @@ jobs: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-gnu --enable-profiler - --set llvm.allow-old-toolchain # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 @@ -660,7 +659,6 @@ jobs: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-gnu --enable-profiler - --set llvm.allow-old-toolchain # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 @@ -675,7 +673,7 @@ jobs: --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler - SCRIPT: PGO_HOST=x86_64-pc-windows-msvc src/ci/pgo.sh python x.py dist bootstrap --include-default-paths + SCRIPT: PGO_HOST=x86_64-pc-windows-msvc python src/ci/stage-build.py python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows-xl @@ -711,7 +709,6 @@ jobs: --build=i686-pc-windows-gnu --enable-full-tools --enable-profiler - --set llvm.allow-old-toolchain # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 @@ -727,7 +724,6 @@ jobs: --build=x86_64-pc-windows-gnu --enable-full-tools --enable-profiler - --set llvm.allow-old-toolchain # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 diff --git a/src/ci/pgo.sh b/src/ci/pgo.sh deleted file mode 100755 index cbe32920a..000000000 --- a/src/ci/pgo.sh +++ /dev/null @@ -1,230 +0,0 @@ -#!/bin/bash -# ignore-tidy-linelength - -set -euxo pipefail - -ci_dir=`cd $(dirname $0) && pwd` -source "$ci_dir/shared.sh" - -# The root checkout, where the source is located -CHECKOUT=/checkout - -DOWNLOADED_LLVM=/rustroot - -# The main directory where the build occurs, which can be different between linux and windows -BUILD_ROOT=$CHECKOUT/obj - -if isWindows; then - CHECKOUT=$(pwd) - DOWNLOADED_LLVM=$CHECKOUT/citools/clang-rust - BUILD_ROOT=$CHECKOUT -fi - -# The various build artifacts used in other commands: to launch rustc builds, build the perf -# collector, and run benchmarks to gather profiling data -BUILD_ARTIFACTS=$BUILD_ROOT/build/$PGO_HOST -RUSTC_STAGE_0=$BUILD_ARTIFACTS/stage0/bin/rustc -CARGO_STAGE_0=$BUILD_ARTIFACTS/stage0/bin/cargo -RUSTC_STAGE_2=$BUILD_ARTIFACTS/stage2/bin/rustc - -# Windows needs these to have the .exe extension -if isWindows; then - RUSTC_STAGE_0="${RUSTC_STAGE_0}.exe" - CARGO_STAGE_0="${CARGO_STAGE_0}.exe" - RUSTC_STAGE_2="${RUSTC_STAGE_2}.exe" -fi - -# Make sure we have a temporary PGO work folder -PGO_TMP=/tmp/tmp-pgo -mkdir -p $PGO_TMP -rm -rf $PGO_TMP/* - -RUSTC_PERF=$PGO_TMP/rustc-perf - -# Compile several crates to gather execution PGO profiles. -# Arg0 => profiles (Debug, Opt) -# Arg1 => scenarios (Full, IncrFull, All) -# Arg2 => crates (syn, cargo, ...) -gather_profiles () { - cd $BUILD_ROOT - - # Compile libcore, both in opt-level=0 and opt-level=3 - RUSTC_BOOTSTRAP=1 $RUSTC_STAGE_2 \ - --edition=2021 --crate-type=lib $CHECKOUT/library/core/src/lib.rs \ - --out-dir $PGO_TMP - RUSTC_BOOTSTRAP=1 $RUSTC_STAGE_2 \ - --edition=2021 --crate-type=lib -Copt-level=3 $CHECKOUT/library/core/src/lib.rs \ - --out-dir $PGO_TMP - - cd $RUSTC_PERF - - # Run rustc-perf benchmarks - # Benchmark using profile_local with eprintln, which essentially just means - # don't actually benchmark -- just make sure we run rustc a bunch of times. - RUST_LOG=collector=debug \ - RUSTC=$RUSTC_STAGE_0 \ - RUSTC_BOOTSTRAP=1 \ - $CARGO_STAGE_0 run -p collector --bin collector -- \ - profile_local \ - eprintln \ - $RUSTC_STAGE_2 \ - --id Test \ - --profiles $1 \ - --cargo $CARGO_STAGE_0 \ - --scenarios $2 \ - --include $3 - - cd $BUILD_ROOT -} - -# This path has to be absolute -LLVM_PROFILE_DIRECTORY_ROOT=$PGO_TMP/llvm-pgo - -# We collect LLVM profiling information and rustc profiling information in -# separate phases. This increases build time -- though not by a huge amount -- -# but prevents any problems from arising due to different profiling runtimes -# being simultaneously linked in. -# LLVM IR PGO does not respect LLVM_PROFILE_FILE, so we have to set the profiling file -# path through our custom environment variable. We include the PID in the directory path -# to avoid updates to profile files being lost because of race conditions. -LLVM_PROFILE_DIR=${LLVM_PROFILE_DIRECTORY_ROOT}/prof-%p python3 $CHECKOUT/x.py build \ - --target=$PGO_HOST \ - --host=$PGO_HOST \ - --stage 2 library/std \ - --llvm-profile-generate - -# Compile rustc-perf: -# - get the expected commit source code: on linux, the Dockerfile downloads a source archive before -# running this script. On Windows, we do that here. -if isLinux; then - cp -r /tmp/rustc-perf $RUSTC_PERF - chown -R $(whoami): $RUSTC_PERF -else - # rustc-perf version from 2022-07-22 - PERF_COMMIT=3c253134664fdcba862c539d37f0de18557a9a4c - retry curl -LS -o $PGO_TMP/perf.zip \ - https://github.com/rust-lang/rustc-perf/archive/$PERF_COMMIT.zip && \ - cd $PGO_TMP && unzip -q perf.zip && \ - mv rustc-perf-$PERF_COMMIT $RUSTC_PERF && \ - rm perf.zip -fi - -# - build rustc-perf's collector ahead of time, which is needed to make sure the rustc-fake binary -# used by the collector is present. -cd $RUSTC_PERF - -RUSTC=$RUSTC_STAGE_0 \ -RUSTC_BOOTSTRAP=1 \ -$CARGO_STAGE_0 build -p collector - -# Here we're profiling LLVM, so we only care about `Debug` and `Opt`, because we want to stress -# codegen. We also profile some of the most prolific crates. -gather_profiles "Debug,Opt" "Full" \ - "syn-1.0.89,cargo-0.60.0,serde-1.0.136,ripgrep-13.0.0,regex-1.5.5,clap-3.1.6,hyper-0.14.18" - -LLVM_PROFILE_MERGED_FILE=$PGO_TMP/llvm-pgo.profdata - -# Merge the profile data we gathered for LLVM -# Note that this uses the profdata from the clang we used to build LLVM, -# which likely has a different version than our in-tree clang. -$DOWNLOADED_LLVM/bin/llvm-profdata merge -o ${LLVM_PROFILE_MERGED_FILE} ${LLVM_PROFILE_DIRECTORY_ROOT} - -echo "LLVM PGO statistics" -du -sh ${LLVM_PROFILE_MERGED_FILE} -du -sh ${LLVM_PROFILE_DIRECTORY_ROOT} -echo "Profile file count" -find ${LLVM_PROFILE_DIRECTORY_ROOT} -type f | wc -l - -# We don't need the individual .profraw files now that they have been merged into a final .profdata -rm -r $LLVM_PROFILE_DIRECTORY_ROOT - -# Rustbuild currently doesn't support rebuilding LLVM when PGO options -# change (or any other llvm-related options); so just clear out the relevant -# directories ourselves. -rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld - -# Okay, LLVM profiling is done, switch to rustc PGO. - -# The path has to be absolute -RUSTC_PROFILE_DIRECTORY_ROOT=$PGO_TMP/rustc-pgo - -python3 $CHECKOUT/x.py build --target=$PGO_HOST --host=$PGO_HOST \ - --stage 2 library/std \ - --rust-profile-generate=${RUSTC_PROFILE_DIRECTORY_ROOT} - -# Here we're profiling the `rustc` frontend, so we also include `Check`. -# The benchmark set includes various stress tests that put the frontend under pressure. -if isLinux; then - # The profile data is written into a single filepath that is being repeatedly merged when each - # rustc invocation ends. Empirically, this can result in some profiling data being lost. That's - # why we override the profile path to include the PID. This will produce many more profiling - # files, but the resulting profile will produce a slightly faster rustc binary. - LLVM_PROFILE_FILE=${RUSTC_PROFILE_DIRECTORY_ROOT}/default_%m_%p.profraw gather_profiles \ - "Check,Debug,Opt" "All" \ - "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0" -else - # On windows, we don't do that yet (because it generates a lot of data, hitting disk space - # limits on the builder), and use the default profraw merging behavior. - gather_profiles \ - "Check,Debug,Opt" "All" \ - "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0" -fi - -RUSTC_PROFILE_MERGED_FILE=$PGO_TMP/rustc-pgo.profdata - -# Merge the profile data we gathered -$BUILD_ARTIFACTS/llvm/bin/llvm-profdata \ - merge -o ${RUSTC_PROFILE_MERGED_FILE} ${RUSTC_PROFILE_DIRECTORY_ROOT} - -echo "Rustc PGO statistics" -du -sh ${RUSTC_PROFILE_MERGED_FILE} -du -sh ${RUSTC_PROFILE_DIRECTORY_ROOT} -echo "Profile file count" -find ${RUSTC_PROFILE_DIRECTORY_ROOT} -type f | wc -l - -# We don't need the individual .profraw files now that they have been merged into a final .profdata -rm -r $RUSTC_PROFILE_DIRECTORY_ROOT - -# Rustbuild currently doesn't support rebuilding LLVM when PGO options -# change (or any other llvm-related options); so just clear out the relevant -# directories ourselves. -rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld - -if isLinux; then - # Gather BOLT profile (BOLT is currently only available on Linux) - python3 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \ - --stage 2 library/std \ - --llvm-profile-use=${LLVM_PROFILE_MERGED_FILE} \ - --llvm-bolt-profile-generate - - BOLT_PROFILE_MERGED_FILE=/tmp/bolt.profdata - - # Here we're profiling Bolt. - gather_profiles "Check,Debug,Opt" "Full" \ - "syn-1.0.89,serde-1.0.136,ripgrep-13.0.0,regex-1.5.5,clap-3.1.6,hyper-0.14.18" - - merge-fdata /tmp/prof.fdata* > ${BOLT_PROFILE_MERGED_FILE} - - echo "BOLT statistics" - du -sh /tmp/prof.fdata* - du -sh ${BOLT_PROFILE_MERGED_FILE} - echo "Profile file count" - find /tmp/prof.fdata* -type f | wc -l - - rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld - - # This produces the actual final set of artifacts, using both the LLVM and rustc - # collected profiling data. - $@ \ - --rust-profile-use=${RUSTC_PROFILE_MERGED_FILE} \ - --llvm-profile-use=${LLVM_PROFILE_MERGED_FILE} \ - --llvm-bolt-profile-use=${BOLT_PROFILE_MERGED_FILE} -else - $@ \ - --rust-profile-use=${RUSTC_PROFILE_MERGED_FILE} \ - --llvm-profile-use=${LLVM_PROFILE_MERGED_FILE} -fi - -echo "Rustc binary size" -ls -la ./build/$PGO_HOST/stage2/bin -ls -la ./build/$PGO_HOST/stage2/lib diff --git a/src/ci/run.sh b/src/ci/run.sh index 0db9c993e..efeb850cd 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -45,6 +45,8 @@ fi ci_dir=`cd $(dirname $0) && pwd` source "$ci_dir/shared.sh" +export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse + if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch try-perf; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.metrics" @@ -57,6 +59,14 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-locked-deps" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-cargo-native-static" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-units-std=1" +# When building for mingw, limit the number of parallel linker jobs during +# the LLVM build, as not to run out of memory. +# This is an attempt to fix the spurious build error tracked by +# https://github.com/rust-lang/rust/issues/108227. +if isWindows && [[ ${CUSTOM_MINGW-0} -eq 1 ]]; then + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.link-jobs=1" +fi + # Only produce xz tarballs on CI. gz tarballs will be generated by the release # process by recompressing the existing xz ones. This decreases the storage # space required for CI artifacts. diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh index 1685fbbbb..7eccb9b86 100755 --- a/src/ci/scripts/install-mingw.sh +++ b/src/ci/scripts/install-mingw.sh @@ -2,24 +2,6 @@ # If we need to download a custom MinGW, do so here and set the path # appropriately. # -# Here we also do a pretty heinous thing which is to mangle the MinGW -# installation we just downloaded. Currently, as of this writing, we're using -# MinGW-w64 builds of gcc, and that's currently at 6.3.0. We use 6.3.0 as it -# appears to be the first version which contains a fix for #40546, builds -# randomly failing during LLVM due to ar.exe/ranlib.exe failures. -# -# Unfortunately, though, 6.3.0 *also* is the first version of MinGW-w64 builds -# to contain a regression in gdb (#40184). As a result if we were to use the -# gdb provided (7.11.1) then we would fail all debuginfo tests. -# -# In order to fix spurious failures (pretty high priority) we use 6.3.0. To -# avoid disabling gdb tests we download an *old* version of gdb, specifically -# that found inside the 6.2.0 distribution. We then overwrite the 6.3.0 gdb -# with the 6.2.0 gdb to get tests passing. -# -# Note that we don't literally overwrite the gdb.exe binary because it appears -# to just use gdborig.exe, so that's the binary we deal with instead. -# # Otherwise install MinGW through `pacman` set -euo pipefail @@ -27,8 +9,8 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" -MINGW_ARCHIVE_32="i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z" -MINGW_ARCHIVE_64="x86_64-6.3.0-release-posix-seh-rt_v5-rev2.7z" +MINGW_ARCHIVE_32="i686-12.2.0-release-posix-dwarf-rt_v10-rev0.7z" +MINGW_ARCHIVE_64="x86_64-12.2.0-release-posix-seh-rt_v10-rev0.7z" if isWindows; then case "${CI_JOB_NAME}" in @@ -66,7 +48,6 @@ if isWindows; then curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}" 7z x -y mingw.7z > /dev/null - curl -o "${mingw_dir}/bin/gdborig.exe" "${MIRRORS_BASE}/2017-04-20-${bits}bit-gdborig.exe" ciCommandAddPath "$(pwd)/${mingw_dir}/bin" fi fi diff --git a/src/ci/scripts/should-skip-this.sh b/src/ci/scripts/should-skip-this.sh index 85d772253..48127166a 100755 --- a/src/ci/scripts/should-skip-this.sh +++ b/src/ci/scripts/should-skip-this.sh @@ -1,46 +1,11 @@ #!/bin/bash -# Set the SKIP_JOB environment variable if this job is supposed to only run -# when submodules are updated and they were not. The following time consuming -# tasks will be skipped when the environment variable is present. +# Set the SKIP_JOB environment variable if this job is not supposed to run on the current builder. set -euo pipefail IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" -if [[ -n "${CI_ONLY_WHEN_SUBMODULES_CHANGED-}" ]]; then - git fetch "https://github.com/$GITHUB_REPOSITORY" "$GITHUB_BASE_REF" - BASE_COMMIT="$(git merge-base FETCH_HEAD HEAD)" - - echo "Searching for toolstate changes between $BASE_COMMIT and $(git rev-parse HEAD)" - - if git diff "$BASE_COMMIT" | grep --quiet "^index .* 160000"; then - # Submodules pseudo-files inside git have the 160000 permissions, so when - # those files are present in the diff a submodule was updated. - echo "Submodules were updated" - elif ! (git diff --quiet "$BASE_COMMIT" -- \ - src/tools/clippy src/tools/rustfmt src/tools/miri \ - library/std/src/sys); then - # There is not an easy blanket search for subtrees. For now, manually list - # the subtrees. - # Also run this when the platform-specific parts of std change, in case - # that breaks Miri. - echo "Tool subtrees were updated" - elif ! (git diff --quiet "$BASE_COMMIT" -- \ - tests/rustdoc-gui \ - src/librustdoc \ - src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile \ - src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version \ - src/tools/rustdoc-gui); then - # There was a change in either rustdoc or in its GUI tests. - echo "Rustdoc was updated" - else - echo "Not executing this job since no submodules nor subtrees were updated" - ciCommandSetEnv SKIP_JOB 1 - exit 0 - fi -fi - if [[ -n "${CI_ONLY_WHEN_CHANNEL-}" ]]; then if [[ "${CI_ONLY_WHEN_CHANNEL}" = "$(cat src/ci/channel)" ]]; then echo "The channel is the expected one" diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py new file mode 100644 index 000000000..bd8fd524a --- /dev/null +++ b/src/ci/stage-build.py @@ -0,0 +1,842 @@ +#!/usr/bin/env python3 +# ignore-tidy-linelength + +# Compatible with Python 3.6+ + +import contextlib +import getpass +import glob +import json +import logging +import os +import pprint +import shutil +import subprocess +import sys +import time +import traceback +import urllib.request +from io import StringIO +from pathlib import Path +from typing import Callable, ContextManager, Dict, Iterable, Iterator, List, Optional, \ + Tuple, Union + +PGO_HOST = os.environ["PGO_HOST"] + +LOGGER = logging.getLogger("stage-build") + +LLVM_PGO_CRATES = [ + "syn-1.0.89", + "cargo-0.60.0", + "serde-1.0.136", + "ripgrep-13.0.0", + "regex-1.5.5", + "clap-3.1.6", + "hyper-0.14.18" +] + +RUSTC_PGO_CRATES = [ + "externs", + "ctfe-stress-5", + "cargo-0.60.0", + "token-stream-stress", + "match-stress", + "tuple-stress", + "diesel-1.4.8", + "bitmaps-3.1.0" +] + +LLVM_BOLT_CRATES = LLVM_PGO_CRATES + + +class Pipeline: + # Paths + def checkout_path(self) -> Path: + """ + The root checkout, where the source is located. + """ + raise NotImplementedError + + def downloaded_llvm_dir(self) -> Path: + """ + Directory where the host LLVM is located. + """ + raise NotImplementedError + + def build_root(self) -> Path: + """ + The main directory where the build occurs. + """ + raise NotImplementedError + + def build_artifacts(self) -> Path: + return self.build_root() / "build" / PGO_HOST + + def rustc_stage_0(self) -> Path: + return self.build_artifacts() / "stage0" / "bin" / "rustc" + + def cargo_stage_0(self) -> Path: + return self.build_artifacts() / "stage0" / "bin" / "cargo" + + def rustc_stage_2(self) -> Path: + return self.build_artifacts() / "stage2" / "bin" / "rustc" + + def opt_artifacts(self) -> Path: + raise NotImplementedError + + def llvm_profile_dir_root(self) -> Path: + return self.opt_artifacts() / "llvm-pgo" + + def llvm_profile_merged_file(self) -> Path: + return self.opt_artifacts() / "llvm-pgo.profdata" + + def rustc_perf_dir(self) -> Path: + return self.opt_artifacts() / "rustc-perf" + + def build_rustc_perf(self): + raise NotImplementedError() + + def rustc_profile_dir_root(self) -> Path: + return self.opt_artifacts() / "rustc-pgo" + + def rustc_profile_merged_file(self) -> Path: + return self.opt_artifacts() / "rustc-pgo.profdata" + + def rustc_profile_template_path(self) -> Path: + """ + The profile data is written into a single filepath that is being repeatedly merged when each + rustc invocation ends. Empirically, this can result in some profiling data being lost. That's + why we override the profile path to include the PID. This will produce many more profiling + files, but the resulting profile will produce a slightly faster rustc binary. + """ + return self.rustc_profile_dir_root() / "default_%m_%p.profraw" + + def supports_bolt(self) -> bool: + raise NotImplementedError + + def llvm_bolt_profile_merged_file(self) -> Path: + return self.opt_artifacts() / "bolt.profdata" + + def metrics_path(self) -> Path: + return self.build_root() / "build" / "metrics.json" + + +class LinuxPipeline(Pipeline): + def checkout_path(self) -> Path: + return Path("/checkout") + + def downloaded_llvm_dir(self) -> Path: + return Path("/rustroot") + + def build_root(self) -> Path: + return self.checkout_path() / "obj" + + def opt_artifacts(self) -> Path: + return Path("/tmp/tmp-multistage/opt-artifacts") + + def build_rustc_perf(self): + # /tmp/rustc-perf comes from the Dockerfile + shutil.copytree("/tmp/rustc-perf", self.rustc_perf_dir()) + cmd(["chown", "-R", f"{getpass.getuser()}:", self.rustc_perf_dir()]) + + with change_cwd(self.rustc_perf_dir()): + cmd([self.cargo_stage_0(), "build", "-p", "collector"], env=dict( + RUSTC=str(self.rustc_stage_0()), + RUSTC_BOOTSTRAP="1" + )) + + def supports_bolt(self) -> bool: + return True + + +class WindowsPipeline(Pipeline): + def __init__(self): + self.checkout_dir = Path(os.getcwd()) + + def checkout_path(self) -> Path: + return self.checkout_dir + + def downloaded_llvm_dir(self) -> Path: + return self.checkout_path() / "citools" / "clang-rust" + + def build_root(self) -> Path: + return self.checkout_path() + + def opt_artifacts(self) -> Path: + return self.checkout_path() / "opt-artifacts" + + def rustc_stage_0(self) -> Path: + return super().rustc_stage_0().with_suffix(".exe") + + def cargo_stage_0(self) -> Path: + return super().cargo_stage_0().with_suffix(".exe") + + def rustc_stage_2(self) -> Path: + return super().rustc_stage_2().with_suffix(".exe") + + def build_rustc_perf(self): + # rustc-perf version from 2022-07-22 + perf_commit = "3c253134664fdcba862c539d37f0de18557a9a4c" + rustc_perf_zip_path = self.opt_artifacts() / "perf.zip" + + def download_rustc_perf(): + download_file( + f"https://github.com/rust-lang/rustc-perf/archive/{perf_commit}.zip", + rustc_perf_zip_path + ) + with change_cwd(self.opt_artifacts()): + unpack_archive(rustc_perf_zip_path) + move_path(Path(f"rustc-perf-{perf_commit}"), self.rustc_perf_dir()) + delete_file(rustc_perf_zip_path) + + retry_action(download_rustc_perf, "Download rustc-perf") + + with change_cwd(self.rustc_perf_dir()): + cmd([self.cargo_stage_0(), "build", "-p", "collector"], env=dict( + RUSTC=str(self.rustc_stage_0()), + RUSTC_BOOTSTRAP="1" + )) + + def rustc_profile_template_path(self) -> Path: + """ + On Windows, we don't have enough space to use separate files for each rustc invocation. + Therefore, we use a single file for the generated profiles. + """ + return self.rustc_profile_dir_root() / "default_%m.profraw" + + def supports_bolt(self) -> bool: + return False + + +def get_timestamp() -> float: + return time.time() + + +Duration = float + + +def iterate_timers(timer: "Timer", name: str, level: int = 0) -> Iterator[ + Tuple[int, str, Duration]]: + """ + Hierarchically iterate the children of a timer, in a depth-first order. + """ + yield (level, name, timer.total_duration()) + for (child_name, child_timer) in timer.children: + yield from iterate_timers(child_timer, child_name, level=level + 1) + + +class Timer: + def __init__(self, parent_names: Tuple[str, ...] = ()): + self.children: List[Tuple[str, Timer]] = [] + self.section_active = False + self.parent_names = parent_names + self.duration_excluding_children: Duration = 0 + + @contextlib.contextmanager + def section(self, name: str) -> ContextManager["Timer"]: + assert not self.section_active + self.section_active = True + + start = get_timestamp() + exc = None + + child_timer = Timer(parent_names=self.parent_names + (name,)) + full_name = " > ".join(child_timer.parent_names) + try: + LOGGER.info(f"Section `{full_name}` starts") + yield child_timer + except BaseException as exception: + exc = exception + raise + finally: + end = get_timestamp() + duration = end - start + + child_timer.duration_excluding_children = duration - child_timer.total_duration() + self.add_child(name, child_timer) + if exc is None: + LOGGER.info(f"Section `{full_name}` ended: OK ({duration:.2f}s)") + else: + LOGGER.info(f"Section `{full_name}` ended: FAIL ({duration:.2f}s)") + self.section_active = False + + def total_duration(self) -> Duration: + return self.duration_excluding_children + sum( + c.total_duration() for (_, c) in self.children) + + def has_children(self) -> bool: + return len(self.children) > 0 + + def print_stats(self): + rows = [] + for (child_name, child_timer) in self.children: + for (level, name, duration) in iterate_timers(child_timer, child_name, level=0): + label = f"{' ' * level}{name}:" + rows.append((label, duration)) + + # Empty row + rows.append(("", "")) + + total_duration_label = "Total duration:" + total_duration = self.total_duration() + rows.append((total_duration_label, humantime(total_duration))) + + space_after_label = 2 + max_label_length = max(16, max(len(label) for (label, _) in rows)) + space_after_label + + table_width = max_label_length + 23 + divider = "-" * table_width + + with StringIO() as output: + print(divider, file=output) + for (label, duration) in rows: + if isinstance(duration, Duration): + pct = (duration / total_duration) * 100 + value = f"{duration:>12.2f}s ({pct:>5.2f}%)" + else: + value = f"{duration:>{len(total_duration_label) + 7}}" + print(f"{label:<{max_label_length}} {value}", file=output) + print(divider, file=output, end="") + LOGGER.info(f"Timer results\n{output.getvalue()}") + + def add_child(self, name: str, timer: "Timer"): + self.children.append((name, timer)) + + def add_duration(self, name: str, duration: Duration): + timer = Timer(parent_names=self.parent_names + (name,)) + timer.duration_excluding_children = duration + self.add_child(name, timer) + + +class BuildStep: + def __init__(self, type: str, children: List["BuildStep"], duration: float): + self.type = type + self.children = children + self.duration = duration + + def find_all_by_type(self, type: str) -> Iterator["BuildStep"]: + if type == self.type: + yield self + for child in self.children: + yield from child.find_all_by_type(type) + + def __repr__(self): + return f"BuildStep(type={self.type}, duration={self.duration}, children={len(self.children)})" + + +def load_last_metrics(path: Path) -> BuildStep: + """ + Loads the metrics of the most recent bootstrap execution from a metrics.json file. + """ + with open(path, "r") as f: + metrics = json.load(f) + invocation = metrics["invocations"][-1] + + def parse(entry) -> Optional[BuildStep]: + if "kind" not in entry or entry["kind"] != "rustbuild_step": + return None + type = entry.get("type", "") + duration = entry.get("duration_excluding_children_sec", 0) + children = [] + + for child in entry.get("children", ()): + step = parse(child) + if step is not None: + children.append(step) + duration += step.duration + return BuildStep(type=type, children=children, duration=duration) + + children = [parse(child) for child in invocation.get("children", ())] + return BuildStep( + type="root", + children=children, + duration=invocation.get("duration_including_children_sec", 0) + ) + + +@contextlib.contextmanager +def change_cwd(dir: Path): + """ + Temporarily change working directory to `dir`. + """ + cwd = os.getcwd() + LOGGER.debug(f"Changing working dir from `{cwd}` to `{dir}`") + os.chdir(dir) + try: + yield + finally: + LOGGER.debug(f"Reverting working dir to `{cwd}`") + os.chdir(cwd) + + +def humantime(time_s: float) -> str: + hours = time_s // 3600 + time_s = time_s % 3600 + minutes = time_s // 60 + seconds = time_s % 60 + + result = "" + if hours > 0: + result += f"{int(hours)}h " + if minutes > 0: + result += f"{int(minutes)}m " + result += f"{round(seconds)}s" + return result + + +def move_path(src: Path, dst: Path): + LOGGER.info(f"Moving `{src}` to `{dst}`") + shutil.move(src, dst) + + +def delete_file(path: Path): + LOGGER.info(f"Deleting file `{path}`") + os.unlink(path) + + +def delete_directory(path: Path): + LOGGER.info(f"Deleting directory `{path}`") + shutil.rmtree(path) + + +def unpack_archive(archive: Path): + LOGGER.info(f"Unpacking archive `{archive}`") + shutil.unpack_archive(archive) + + +def download_file(src: str, target: Path): + LOGGER.info(f"Downloading `{src}` into `{target}`") + urllib.request.urlretrieve(src, str(target)) + + +def retry_action(action, name: str, max_fails: int = 5): + LOGGER.info(f"Attempting to perform action `{name}` with retry") + for iteration in range(max_fails): + LOGGER.info(f"Attempt {iteration + 1}/{max_fails}") + try: + action() + return + except: + LOGGER.error(f"Action `{name}` has failed\n{traceback.format_exc()}") + + raise Exception(f"Action `{name}` has failed after {max_fails} attempts") + + +def cmd( + args: List[Union[str, Path]], + env: Optional[Dict[str, str]] = None, + output_path: Optional[Path] = None +): + args = [str(arg) for arg in args] + + environment = os.environ.copy() + + cmd_str = "" + if env is not None: + environment.update(env) + cmd_str += " ".join(f"{k}={v}" for (k, v) in (env or {}).items()) + cmd_str += " " + cmd_str += " ".join(args) + if output_path is not None: + cmd_str += f" > {output_path}" + LOGGER.info(f"Executing `{cmd_str}`") + + if output_path is not None: + with open(output_path, "w") as f: + return subprocess.run( + args, + env=environment, + check=True, + stdout=f + ) + return subprocess.run(args, env=environment, check=True) + + +def run_compiler_benchmarks( + pipeline: Pipeline, + profiles: List[str], + scenarios: List[str], + crates: List[str], + env: Optional[Dict[str, str]] = None +): + env = env if env is not None else {} + + # Compile libcore, both in opt-level=0 and opt-level=3 + with change_cwd(pipeline.build_root()): + cmd([ + pipeline.rustc_stage_2(), + "--edition", "2021", + "--crate-type", "lib", + str(pipeline.checkout_path() / "library/core/src/lib.rs"), + "--out-dir", pipeline.opt_artifacts() + ], env=dict(RUSTC_BOOTSTRAP="1", **env)) + + cmd([ + pipeline.rustc_stage_2(), + "--edition", "2021", + "--crate-type", "lib", + "-Copt-level=3", + str(pipeline.checkout_path() / "library/core/src/lib.rs"), + "--out-dir", pipeline.opt_artifacts() + ], env=dict(RUSTC_BOOTSTRAP="1", **env)) + + # Run rustc-perf benchmarks + # Benchmark using profile_local with eprintln, which essentially just means + # don't actually benchmark -- just make sure we run rustc a bunch of times. + with change_cwd(pipeline.rustc_perf_dir()): + cmd([ + pipeline.cargo_stage_0(), + "run", + "-p", "collector", "--bin", "collector", "--", + "profile_local", "eprintln", + pipeline.rustc_stage_2(), + "--id", "Test", + "--cargo", pipeline.cargo_stage_0(), + "--profiles", ",".join(profiles), + "--scenarios", ",".join(scenarios), + "--include", ",".join(crates) + ], env=dict( + RUST_LOG="collector=debug", + RUSTC=str(pipeline.rustc_stage_0()), + RUSTC_BOOTSTRAP="1", + **env + )) + + +# https://stackoverflow.com/a/31631711/1107768 +def format_bytes(size: int) -> str: + """Return the given bytes as a human friendly KiB, MiB or GiB string.""" + KB = 1024 + MB = KB ** 2 # 1,048,576 + GB = KB ** 3 # 1,073,741,824 + TB = KB ** 4 # 1,099,511,627,776 + + if size < KB: + return f"{size} B" + elif KB <= size < MB: + return f"{size / KB:.2f} KiB" + elif MB <= size < GB: + return f"{size / MB:.2f} MiB" + elif GB <= size < TB: + return f"{size / GB:.2f} GiB" + else: + return str(size) + + +# https://stackoverflow.com/a/63307131/1107768 +def count_files(path: Path) -> int: + return sum(1 for p in path.rglob("*") if p.is_file()) + + +def count_files_with_prefix(path: Path) -> int: + return sum(1 for p in glob.glob(f"{path}*") if Path(p).is_file()) + + +# https://stackoverflow.com/a/55659577/1107768 +def get_path_size(path: Path) -> int: + if path.is_dir(): + return sum(p.stat().st_size for p in path.rglob("*")) + return path.stat().st_size + + +def get_path_prefix_size(path: Path) -> int: + """ + Get size of all files beginning with the prefix `path`. + Alternative to shell `du -sh *`. + """ + return sum(Path(p).stat().st_size for p in glob.glob(f"{path}*")) + + +def get_files(directory: Path, filter: Optional[Callable[[Path], bool]] = None) -> Iterable[Path]: + for file in os.listdir(directory): + path = directory / file + if filter is None or filter(path): + yield path + + +def build_rustc( + pipeline: Pipeline, + args: List[str], + env: Optional[Dict[str, str]] = None +): + arguments = [ + sys.executable, + pipeline.checkout_path() / "x.py", + "build", + "--target", PGO_HOST, + "--host", PGO_HOST, + "--stage", "2", + "library/std" + ] + args + cmd(arguments, env=env) + + +def create_pipeline() -> Pipeline: + if sys.platform == "linux": + return LinuxPipeline() + elif sys.platform in ("cygwin", "win32"): + return WindowsPipeline() + else: + raise Exception(f"Optimized build is not supported for platform {sys.platform}") + + +def gather_llvm_profiles(pipeline: Pipeline): + LOGGER.info("Running benchmarks with PGO instrumented LLVM") + run_compiler_benchmarks( + pipeline, + profiles=["Debug", "Opt"], + scenarios=["Full"], + crates=LLVM_PGO_CRATES + ) + + profile_path = pipeline.llvm_profile_merged_file() + LOGGER.info(f"Merging LLVM PGO profiles to {profile_path}") + cmd([ + pipeline.downloaded_llvm_dir() / "bin" / "llvm-profdata", + "merge", + "-o", profile_path, + pipeline.llvm_profile_dir_root() + ]) + + LOGGER.info("LLVM PGO statistics") + LOGGER.info(f"{profile_path}: {format_bytes(get_path_size(profile_path))}") + LOGGER.info( + f"{pipeline.llvm_profile_dir_root()}: {format_bytes(get_path_size(pipeline.llvm_profile_dir_root()))}") + LOGGER.info(f"Profile file count: {count_files(pipeline.llvm_profile_dir_root())}") + + # We don't need the individual .profraw files now that they have been merged + # into a final .profdata + delete_directory(pipeline.llvm_profile_dir_root()) + + +def gather_rustc_profiles(pipeline: Pipeline): + LOGGER.info("Running benchmarks with PGO instrumented rustc") + + # Here we're profiling the `rustc` frontend, so we also include `Check`. + # The benchmark set includes various stress tests that put the frontend under pressure. + run_compiler_benchmarks( + pipeline, + profiles=["Check", "Debug", "Opt"], + scenarios=["All"], + crates=RUSTC_PGO_CRATES, + env=dict( + LLVM_PROFILE_FILE=str(pipeline.rustc_profile_template_path()) + ) + ) + + profile_path = pipeline.rustc_profile_merged_file() + LOGGER.info(f"Merging Rustc PGO profiles to {profile_path}") + cmd([ + pipeline.build_artifacts() / "llvm" / "bin" / "llvm-profdata", + "merge", + "-o", profile_path, + pipeline.rustc_profile_dir_root() + ]) + + LOGGER.info("Rustc PGO statistics") + LOGGER.info(f"{profile_path}: {format_bytes(get_path_size(profile_path))}") + LOGGER.info( + f"{pipeline.rustc_profile_dir_root()}: {format_bytes(get_path_size(pipeline.rustc_profile_dir_root()))}") + LOGGER.info(f"Profile file count: {count_files(pipeline.rustc_profile_dir_root())}") + + # We don't need the individual .profraw files now that they have been merged + # into a final .profdata + delete_directory(pipeline.rustc_profile_dir_root()) + + +def gather_llvm_bolt_profiles(pipeline: Pipeline): + LOGGER.info("Running benchmarks with BOLT instrumented LLVM") + run_compiler_benchmarks( + pipeline, + profiles=["Check", "Debug", "Opt"], + scenarios=["Full"], + crates=LLVM_BOLT_CRATES + ) + + merged_profile_path = pipeline.llvm_bolt_profile_merged_file() + profile_files_path = Path("/tmp/prof.fdata") + LOGGER.info(f"Merging LLVM BOLT profiles to {merged_profile_path}") + + profile_files = sorted(glob.glob(f"{profile_files_path}*")) + cmd([ + "merge-fdata", + *profile_files, + ], output_path=merged_profile_path) + + LOGGER.info("LLVM BOLT statistics") + LOGGER.info(f"{merged_profile_path}: {format_bytes(get_path_size(merged_profile_path))}") + LOGGER.info( + f"{profile_files_path}: {format_bytes(get_path_prefix_size(profile_files_path))}") + LOGGER.info(f"Profile file count: {count_files_with_prefix(profile_files_path)}") + + +def clear_llvm_files(pipeline: Pipeline): + """ + Rustbuild currently doesn't support rebuilding LLVM when PGO options + change (or any other llvm-related options); so just clear out the relevant + directories ourselves. + """ + LOGGER.info("Clearing LLVM build files") + delete_directory(pipeline.build_artifacts() / "llvm") + delete_directory(pipeline.build_artifacts() / "lld") + + +def print_binary_sizes(pipeline: Pipeline): + bin_dir = pipeline.build_artifacts() / "stage2" / "bin" + binaries = get_files(bin_dir) + + lib_dir = pipeline.build_artifacts() / "stage2" / "lib" + libraries = get_files(lib_dir, lambda p: p.suffix == ".so") + + paths = sorted(binaries) + sorted(libraries) + with StringIO() as output: + for path in paths: + path_str = f"{path.name}:" + print(f"{path_str:<50}{format_bytes(path.stat().st_size):>14}", file=output) + LOGGER.info(f"Rustc binary size\n{output.getvalue()}") + + +def print_free_disk_space(pipeline: Pipeline): + usage = shutil.disk_usage(pipeline.opt_artifacts()) + total = usage.total + used = usage.used + free = usage.free + + logging.info( + f"Free disk space: {format_bytes(free)} out of total {format_bytes(total)} ({(used / total) * 100:.2f}% used)") + + +def log_metrics(step: BuildStep): + substeps: List[Tuple[int, BuildStep]] = [] + + def visit(step: BuildStep, level: int): + substeps.append((level, step)) + for child in step.children: + visit(child, level=level + 1) + + visit(step, 0) + + output = StringIO() + for (level, step) in substeps: + label = f"{'.' * level}{step.type}" + print(f"{label:<65}{step.duration:>8.2f}s", file=output) + logging.info(f"Build step durations\n{output.getvalue()}") + + +def record_metrics(pipeline: Pipeline, timer: Timer): + metrics = load_last_metrics(pipeline.metrics_path()) + if metrics is None: + return + llvm_steps = tuple(metrics.find_all_by_type("bootstrap::native::Llvm")) + assert len(llvm_steps) > 0 + llvm_duration = sum(step.duration for step in llvm_steps) + + rustc_steps = tuple(metrics.find_all_by_type("bootstrap::compile::Rustc")) + assert len(rustc_steps) > 0 + rustc_duration = sum(step.duration for step in rustc_steps) + + # The LLVM step is part of the Rustc step + rustc_duration -= llvm_duration + + timer.add_duration("LLVM", llvm_duration) + timer.add_duration("Rustc", rustc_duration) + + log_metrics(metrics) + + +def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: List[str]): + # Clear and prepare tmp directory + shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True) + os.makedirs(pipeline.opt_artifacts(), exist_ok=True) + + pipeline.build_rustc_perf() + + # Stage 1: Build rustc + PGO instrumented LLVM + with timer.section("Stage 1 (LLVM PGO)") as stage1: + with stage1.section("Build rustc and LLVM") as rustc_build: + build_rustc(pipeline, args=[ + "--llvm-profile-generate" + ], env=dict( + LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p") + )) + record_metrics(pipeline, rustc_build) + + with stage1.section("Gather profiles"): + gather_llvm_profiles(pipeline) + print_free_disk_space(pipeline) + + clear_llvm_files(pipeline) + final_build_args += [ + "--llvm-profile-use", + pipeline.llvm_profile_merged_file() + ] + + # Stage 2: Build PGO instrumented rustc + LLVM + with timer.section("Stage 2 (rustc PGO)") as stage2: + with stage2.section("Build rustc and LLVM") as rustc_build: + build_rustc(pipeline, args=[ + "--rust-profile-generate", + pipeline.rustc_profile_dir_root() + ]) + record_metrics(pipeline, rustc_build) + + with stage2.section("Gather profiles"): + gather_rustc_profiles(pipeline) + print_free_disk_space(pipeline) + + clear_llvm_files(pipeline) + final_build_args += [ + "--rust-profile-use", + pipeline.rustc_profile_merged_file() + ] + + # Stage 3: Build rustc + BOLT instrumented LLVM + if pipeline.supports_bolt(): + with timer.section("Stage 3 (LLVM BOLT)") as stage3: + with stage3.section("Build rustc and LLVM") as rustc_build: + build_rustc(pipeline, args=[ + "--llvm-profile-use", + pipeline.llvm_profile_merged_file(), + "--llvm-bolt-profile-generate", + ]) + record_metrics(pipeline, rustc_build) + + with stage3.section("Gather profiles"): + gather_llvm_bolt_profiles(pipeline) + + print_free_disk_space(pipeline) + clear_llvm_files(pipeline) + final_build_args += [ + "--llvm-bolt-profile-use", + pipeline.llvm_bolt_profile_merged_file() + ] + + # Stage 4: Build PGO optimized rustc + PGO/BOLT optimized LLVM + with timer.section("Stage 4 (final build)") as stage4: + cmd(final_build_args) + record_metrics(pipeline, stage4) + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.DEBUG, + format="%(name)s %(levelname)-4s: %(message)s", + ) + + LOGGER.info(f"Running multi-stage build using Python {sys.version}") + LOGGER.info(f"Environment values\n{pprint.pformat(dict(os.environ), indent=2)}") + + build_args = sys.argv[1:] + + timer = Timer() + pipeline = create_pipeline() + try: + execute_build_pipeline(timer, pipeline, build_args) + except BaseException as e: + LOGGER.error("The multi-stage build has failed") + raise e + finally: + timer.print_stats() + print_free_disk_space(pipeline) + + print_binary_sizes(pipeline) diff --git a/src/doc/book/.github/workflows/main.yml b/src/doc/book/.github/workflows/main.yml index 64ab49c51..65574a070 100644 --- a/src/doc/book/.github/workflows/main.yml +++ b/src/doc/book/.github/workflows/main.yml @@ -12,8 +12,8 @@ jobs: - name: Install Rust run: | rustup set profile minimal - rustup toolchain install 1.65 -c rust-docs - rustup default 1.65 + rustup toolchain install 1.67 -c rust-docs + rustup default 1.67 - name: Install mdbook run: | mkdir bin diff --git a/src/doc/book/CONTRIBUTING.md b/src/doc/book/CONTRIBUTING.md index 3c9500687..a58ea4203 100644 --- a/src/doc/book/CONTRIBUTING.md +++ b/src/doc/book/CONTRIBUTING.md @@ -38,11 +38,13 @@ that governs all sub-projects, including this one. Please respect it! ## Expectations -Because the book is [printed](https://nostarch.com/rust), and because we want +Because the book is [printed][nostarch], and because we want to keep the online version of the book close to the print version when possible, it may take longer than you're used to for us to address your issue or pull request. +[nostarch]: https://nostarch.com/rust-programming-language-2nd-edition + So far, we've been doing a larger revision to coincide with [Rust Editions](https://doc.rust-lang.org/edition-guide/). Between those larger revisions, we will only be correcting errors. If your issue or pull request diff --git a/src/doc/book/README.md b/src/doc/book/README.md index 94e1a004a..f6341efc9 100644 --- a/src/doc/book/README.md +++ b/src/doc/book/README.md @@ -6,7 +6,7 @@ This repository contains the source of "The Rust Programming Language" book. [The book is available in dead-tree form from No Starch Press][nostarch]. -[nostarch]: https://nostarch.com/rust +[nostarch]: https://nostarch.com/rust-programming-language-2nd-edition You can also read the book for free online. Please see the book as shipped with the latest [stable], [beta], or [nightly] Rust releases. Be aware that issues @@ -73,7 +73,7 @@ kinds of contributions we're looking for. [contrib]: https://github.com/rust-lang/book/blob/main/CONTRIBUTING.md -Because the book is [printed](https://nostarch.com/rust), and because we want +Because the book is [printed][nostarch], and because we want to keep the online version of the book close to the print version when possible, it may take longer than you're used to for us to address your issue or pull request. diff --git a/src/doc/book/ci/dictionary.txt b/src/doc/book/ci/dictionary.txt index 9eb695d5b..a91df4a03 100644 --- a/src/doc/book/ci/dictionary.txt +++ b/src/doc/book/ci/dictionary.txt @@ -546,6 +546,7 @@ unsafety unsized unsynchronized Unyank +UpperCamelCase URIs UsefulType username diff --git a/src/doc/book/listings/ch02-guessing-game-tutorial/listing-02-04/output.txt b/src/doc/book/listings/ch02-guessing-game-tutorial/listing-02-04/output.txt index 70a0c930c..3640c0694 100644 --- a/src/doc/book/listings/ch02-guessing-game-tutorial/listing-02-04/output.txt +++ b/src/doc/book/listings/ch02-guessing-game-tutorial/listing-02-04/output.txt @@ -18,6 +18,7 @@ error[E0308]: mismatched types = note: expected reference `&String` found reference `&{integer}` note: associated function defined here + --> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/cmp.rs:783:8 For more information about this error, try `rustc --explain E0308`. error: could not compile `guessing_game` due to previous error diff --git a/src/doc/book/listings/ch02-guessing-game-tutorial/no-listing-02-without-expect/output.txt b/src/doc/book/listings/ch02-guessing-game-tutorial/no-listing-02-without-expect/output.txt index 8095bbd8d..9517af95f 100644 --- a/src/doc/book/listings/ch02-guessing-game-tutorial/no-listing-02-without-expect/output.txt +++ b/src/doc/book/listings/ch02-guessing-game-tutorial/no-listing-02-without-expect/output.txt @@ -4,10 +4,10 @@ warning: unused `Result` that must be used --> src/main.rs:10:5 | 10 | io::stdin().read_line(&mut guess); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[warn(unused_must_use)]` on by default = note: this `Result` may be an `Err` variant, which should be handled + = note: `#[warn(unused_must_use)]` on by default warning: `guessing_game` (bin "guessing_game") generated 1 warning Finished dev [unoptimized + debuginfo] target(s) in 0.59s diff --git a/src/doc/book/listings/ch03-common-programming-concepts/no-listing-23-statements-dont-return-values/output.txt b/src/doc/book/listings/ch03-common-programming-concepts/no-listing-23-statements-dont-return-values/output.txt index c0484ea1b..d27a7fae2 100644 --- a/src/doc/book/listings/ch03-common-programming-concepts/no-listing-23-statements-dont-return-values/output.txt +++ b/src/doc/book/listings/ch03-common-programming-concepts/no-listing-23-statements-dont-return-values/output.txt @@ -8,7 +8,7 @@ error[E0308]: mismatched types | | | implicitly returns `()` as its body has no tail or `return` expression 8 | x + 1; - | - help: remove this semicolon + | - help: remove this semicolon to return this value For more information about this error, try `rustc --explain E0308`. error: could not compile `functions` due to previous error diff --git a/src/doc/book/listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/output.txt b/src/doc/book/listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/output.txt index 05987f7c8..4a5356701 100644 --- a/src/doc/book/listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/output.txt +++ b/src/doc/book/listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/output.txt @@ -12,6 +12,10 @@ error[E0382]: borrow of moved value: `s1` | ^^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +3 | let s2 = s1.clone(); + | ++++++++ For more information about this error, try `rustc --explain E0382`. error: could not compile `ownership` due to previous error diff --git a/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-07-cant-use-option-directly/output.txt b/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-07-cant-use-option-directly/output.txt index d4a040e8e..a16dc0180 100644 --- a/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-07-cant-use-option-directly/output.txt +++ b/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-07-cant-use-option-directly/output.txt @@ -8,15 +8,10 @@ error[E0277]: cannot add `Option` to `i8` | = help: the trait `Add>` is not implemented for `i8` = help: the following other types implement trait `Add`: - <&'a f32 as Add> - <&'a f64 as Add> - <&'a i128 as Add> - <&'a i16 as Add> - <&'a i32 as Add> - <&'a i64 as Add> <&'a i8 as Add> - <&'a isize as Add> - and 48 others + <&i8 as Add<&i8>> + > + For more information about this error, try `rustc --explain E0277`. error: could not compile `enums` due to previous error diff --git a/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/output.txt b/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/output.txt index bec72849a..75d056f84 100644 --- a/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/output.txt +++ b/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/output.txt @@ -7,6 +7,10 @@ error[E0004]: non-exhaustive patterns: `None` not covered | ^ pattern `None` not covered | note: `Option` defined here + --> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/option.rs:518:1 + | + = note: +/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/option.rs:522:5: not covered = note: the matched value is of type `Option` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/src/doc/book/listings/ch07-managing-growing-projects/listing-07-12/output.txt b/src/doc/book/listings/ch07-managing-growing-projects/listing-07-12/output.txt index 39b650540..1bc89bf32 100644 --- a/src/doc/book/listings/ch07-managing-growing-projects/listing-07-12/output.txt +++ b/src/doc/book/listings/ch07-managing-growing-projects/listing-07-12/output.txt @@ -1,11 +1,5 @@ $ cargo build Compiling restaurant v0.1.0 (file:///projects/restaurant) -error[E0433]: failed to resolve: use of undeclared crate or module `hosting` - --> src/lib.rs:11:9 - | -11 | hosting::add_to_waitlist(); - | ^^^^^^^ use of undeclared crate or module `hosting` - warning: unused import: `crate::front_of_house::hosting` --> src/lib.rs:7:5 | @@ -14,6 +8,12 @@ warning: unused import: `crate::front_of_house::hosting` | = note: `#[warn(unused_imports)]` on by default +error[E0433]: failed to resolve: use of undeclared crate or module `hosting` + --> src/lib.rs:11:9 + | +11 | hosting::add_to_waitlist(); + | ^^^^^^^ use of undeclared crate or module `hosting` + For more information about this error, try `rustc --explain E0433`. warning: `restaurant` (lib) generated 1 warning error: could not compile `restaurant` due to previous error; 1 warning emitted diff --git a/src/doc/book/listings/ch08-common-collections/listing-08-19/output.txt b/src/doc/book/listings/ch08-common-collections/listing-08-19/output.txt index 95577772e..3a682457c 100644 --- a/src/doc/book/listings/ch08-common-collections/listing-08-19/output.txt +++ b/src/doc/book/listings/ch08-common-collections/listing-08-19/output.txt @@ -14,7 +14,6 @@ error[E0277]: the type `String` cannot be indexed by `{integer}` >> >> >> - > For more information about this error, try `rustc --explain E0277`. error: could not compile `collections` due to previous error diff --git a/src/doc/book/listings/ch11-writing-automated-tests/listing-11-03/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/listing-11-03/output.txt index 2fa5cf077..e8c31e79e 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/listing-11-03/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/listing-11-03/output.txt @@ -10,7 +10,7 @@ test tests::exploration ... ok failures: ---- tests::another stdout ---- -thread 'main' panicked at 'Make this test fail', src/lib.rs:10:9 +thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:10:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch11-writing-automated-tests/listing-11-10/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/listing-11-10/output.txt index 681cfbb81..b0d10deaa 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/listing-11-10/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/listing-11-10/output.txt @@ -11,7 +11,7 @@ failures: ---- tests::this_test_will_fail stdout ---- I got the value 8 -thread 'main' panicked at 'assertion failed: `(left == right)` +thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)` left: `5`, right: `10`', src/lib.rs:19:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/output.txt index 7c62822f7..6fbb5e496 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/output.txt @@ -10,7 +10,7 @@ test tests::smaller_cannot_hold_larger ... ok failures: ---- tests::larger_can_hold_smaller stdout ---- -thread 'main' panicked at 'assertion failed: larger.can_hold(&smaller)', src/lib.rs:28:9 +thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed: larger.can_hold(&smaller)', src/lib.rs:28:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt index 28e2414be..00bf63fff 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt @@ -9,7 +9,7 @@ test tests::it_adds_two ... FAILED failures: ---- tests::it_adds_two stdout ---- -thread 'main' panicked at 'assertion failed: `(left == right)` +thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)` left: `4`, right: `5`', src/lib.rs:11:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/output.txt index 3366e3ace..68d724f11 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/output.txt @@ -9,7 +9,7 @@ test tests::greeting_contains_name ... FAILED failures: ---- tests::greeting_contains_name stdout ---- -thread 'main' panicked at 'assertion failed: result.contains(\"Carol\")', src/lib.rs:12:9 +thread 'tests::greeting_contains_name' panicked at 'assertion failed: result.contains(\"Carol\")', src/lib.rs:12:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/output.txt index cebebdaee..03d6ad7f1 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/output.txt @@ -9,7 +9,7 @@ test tests::greeting_contains_name ... FAILED failures: ---- tests::greeting_contains_name stdout ---- -thread 'main' panicked at 'Greeting did not contain name, value was `Hello!`', src/lib.rs:12:9 +thread 'tests::greeting_contains_name' panicked at 'Greeting did not contain name, value was `Hello!`', src/lib.rs:12:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/output.txt index c176e88b8..0b045e8be 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/output.txt @@ -9,7 +9,7 @@ test tests::greater_than_100 - should panic ... FAILED failures: ---- tests::greater_than_100 stdout ---- -thread 'main' panicked at 'Guess value must be greater than or equal to 1, got 200.', src/lib.rs:13:13 +thread 'tests::greater_than_100' panicked at 'Guess value must be greater than or equal to 1, got 200.', src/lib.rs:13:13 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: panic did not contain expected string panic message: `"Guess value must be greater than or equal to 1, got 200."`, diff --git a/src/doc/book/listings/ch11-writing-automated-tests/output-only-01-show-output/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/output-only-01-show-output/output.txt index 4ececf245..bc03145a1 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/output-only-01-show-output/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/output-only-01-show-output/output.txt @@ -20,7 +20,7 @@ failures: ---- tests::this_test_will_fail stdout ---- I got the value 8 -thread 'main' panicked at 'assertion failed: `(left == right)` +thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)` left: `5`, right: `10`', src/lib.rs:19:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-12/output.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-12/output.txt index c18902518..9870b2125 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-12/output.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-12/output.txt @@ -4,10 +4,10 @@ warning: unused `Result` that must be used --> src/main.rs:19:5 | 19 | run(config); - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^ | - = note: `#[warn(unused_must_use)]` on by default = note: this `Result` may be an `Err` variant, which should be handled + = note: `#[warn(unused_must_use)]` on by default warning: `minigrep` (bin "minigrep") generated 1 warning Finished dev [unoptimized + debuginfo] target(s) in 0.71s diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-16/output.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-16/output.txt index 3c34e3945..be4a97eea 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-16/output.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-16/output.txt @@ -9,7 +9,7 @@ test tests::one_result ... FAILED failures: ---- tests::one_result stdout ---- -thread 'main' panicked at 'assertion failed: `(left == right)` +thread 'tests::one_result' panicked at 'assertion failed: `(left == right)` left: `["safe, fast, productive."]`, right: `[]`', src/lib.rs:44:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch13-functional-features/listing-13-14/output.txt b/src/doc/book/listings/ch13-functional-features/listing-13-14/output.txt index 228c764ed..993037991 100644 --- a/src/doc/book/listings/ch13-functional-features/listing-13-14/output.txt +++ b/src/doc/book/listings/ch13-functional-features/listing-13-14/output.txt @@ -4,10 +4,10 @@ warning: unused `Map` that must be used --> src/main.rs:4:5 | 4 | v1.iter().map(|x| x + 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[warn(unused_must_use)]` on by default = note: iterators are lazy and do nothing unless consumed + = note: `#[warn(unused_must_use)]` on by default warning: `iterators` (bin "iterators") generated 1 warning Finished dev [unoptimized + debuginfo] target(s) in 0.47s diff --git a/src/doc/book/listings/ch15-smart-pointers/listing-15-03/output.txt b/src/doc/book/listings/ch15-smart-pointers/listing-15-03/output.txt index d5522cd53..04b6976fe 100644 --- a/src/doc/book/listings/ch15-smart-pointers/listing-15-03/output.txt +++ b/src/doc/book/listings/ch15-smart-pointers/listing-15-03/output.txt @@ -4,11 +4,11 @@ error[E0072]: recursive type `List` has infinite size --> src/main.rs:1:1 | 1 | enum List { - | ^^^^^^^^^ recursive type has infinite size + | ^^^^^^^^^ 2 | Cons(i32, List), | ---- recursive without indirection | -help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle | 2 | Cons(i32, Box), | ++++ + diff --git a/src/doc/book/listings/ch15-smart-pointers/listing-15-23/output.txt b/src/doc/book/listings/ch15-smart-pointers/listing-15-23/output.txt index 3749c845c..0ffabf765 100644 --- a/src/doc/book/listings/ch15-smart-pointers/listing-15-23/output.txt +++ b/src/doc/book/listings/ch15-smart-pointers/listing-15-23/output.txt @@ -9,7 +9,7 @@ test tests::it_sends_an_over_75_percent_warning_message ... FAILED failures: ---- tests::it_sends_an_over_75_percent_warning_message stdout ---- -thread 'main' panicked at 'already borrowed: BorrowMutError', src/lib.rs:60:53 +thread 'tests::it_sends_an_over_75_percent_warning_message' panicked at 'already borrowed: BorrowMutError', src/lib.rs:60:53 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch16-fearless-concurrency/listing-16-14/output.txt b/src/doc/book/listings/ch16-fearless-concurrency/listing-16-14/output.txt index 0634b86e5..e8cf21a73 100644 --- a/src/doc/book/listings/ch16-fearless-concurrency/listing-16-14/output.txt +++ b/src/doc/book/listings/ch16-fearless-concurrency/listing-16-14/output.txt @@ -22,6 +22,9 @@ note: required because it's used within this closure 11 | let handle = thread::spawn(move || { | ^^^^^^^ note: required by a bound in `spawn` + --> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/std/src/thread/mod.rs:704:8 + | + = note: required by this bound in `spawn` For more information about this error, try `rustc --explain E0277`. error: could not compile `shared-state` due to previous error diff --git a/src/doc/book/listings/ch18-patterns-and-matching/listing-18-08/output.txt b/src/doc/book/listings/ch18-patterns-and-matching/listing-18-08/output.txt index 0fd5373b8..52efabb5c 100644 --- a/src/doc/book/listings/ch18-patterns-and-matching/listing-18-08/output.txt +++ b/src/doc/book/listings/ch18-patterns-and-matching/listing-18-08/output.txt @@ -9,11 +9,19 @@ error[E0005]: refutable pattern in local binding: `None` not covered = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Option` defined here + --> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/option.rs:518:1 + | + = note: +/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/option.rs:522:5: not covered = note: the matched value is of type `Option` help: you might want to use `if let` to ignore the variant that isn't matched | 3 | let x = if let Some(x) = some_option_value { x } else { todo!() }; | ++++++++++ ++++++++++++++++++++++ +help: alternatively, you might want to use let else to handle the variant that isn't matched + | +3 | let Some(x) = some_option_value else { todo!() }; + | ++++++++++++++++ For more information about this error, try `rustc --explain E0005`. error: could not compile `patterns` due to previous error diff --git a/src/doc/book/listings/ch18-patterns-and-matching/listing-18-10/output.txt b/src/doc/book/listings/ch18-patterns-and-matching/listing-18-10/output.txt index 702d10a23..6488fb29c 100644 --- a/src/doc/book/listings/ch18-patterns-and-matching/listing-18-10/output.txt +++ b/src/doc/book/listings/ch18-patterns-and-matching/listing-18-10/output.txt @@ -6,9 +6,9 @@ warning: irrefutable `if let` pattern 2 | if let x = 5 { | ^^^^^^^^^ | - = note: `#[warn(irrefutable_let_patterns)]` on by default = note: this pattern will always match, so the `if let` is useless = help: consider replacing the `if let` with a `let` + = note: `#[warn(irrefutable_let_patterns)]` on by default warning: `patterns` (bin "patterns") generated 1 warning Finished dev [unoptimized + debuginfo] target(s) in 0.39s diff --git a/src/doc/book/listings/ch19-advanced-features/listing-19-20/output.txt b/src/doc/book/listings/ch19-advanced-features/listing-19-20/output.txt index a3b281e3f..594287604 100644 --- a/src/doc/book/listings/ch19-advanced-features/listing-19-20/output.txt +++ b/src/doc/book/listings/ch19-advanced-features/listing-19-20/output.txt @@ -11,8 +11,8 @@ error[E0790]: cannot call associated function on trait without specifying the co | help: use the fully-qualified path to the only available implementation | -20 | println!("A baby dog is called a {}", <::Dog as Animal>::baby_name()); - | +++++++++ + +20 | println!("A baby dog is called a {}", ::baby_name()); + | +++++++ + For more information about this error, try `rustc --explain E0790`. error: could not compile `traits-example` due to previous error diff --git a/src/doc/book/listings/ch20-web-server/listing-20-22/output.txt b/src/doc/book/listings/ch20-web-server/listing-20-22/output.txt index a6c9e8d3b..342bf9a16 100644 --- a/src/doc/book/listings/ch20-web-server/listing-20-22/output.txt +++ b/src/doc/book/listings/ch20-web-server/listing-20-22/output.txt @@ -9,6 +9,7 @@ error[E0507]: cannot move out of `worker.thread` which is behind a mutable refer | move occurs because `worker.thread` has type `JoinHandle<()>`, which does not implement the `Copy` trait | note: this function takes ownership of the receiver `self`, which moves `worker.thread` + --> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/std/src/thread/mod.rs:1581:17 For more information about this error, try `rustc --explain E0507`. error: could not compile `hello` due to previous error diff --git a/src/doc/book/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt b/src/doc/book/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt index e4c0eeb2a..fec2377dc 100644 --- a/src/doc/book/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt +++ b/src/doc/book/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt @@ -7,6 +7,7 @@ error[E0599]: no method named `join` found for enum `Option` in the current scop | ^^^^ method not found in `Option>` | note: the method `join` exists on the type `JoinHandle<()>` + --> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/std/src/thread/mod.rs:1581:5 help: consider using `Option::expect` to unwrap the `JoinHandle<()>` value, panicking if the value is an `Option::None` | 52 | worker.thread.expect("REASON").join().unwrap(); diff --git a/src/doc/book/rust-toolchain b/src/doc/book/rust-toolchain index 5b6cd6b3c..9ebd7af32 100644 --- a/src/doc/book/rust-toolchain +++ b/src/doc/book/rust-toolchain @@ -1 +1 @@ -1.65 +1.67 diff --git a/src/doc/book/src/ch00-00-introduction.md b/src/doc/book/src/ch00-00-introduction.md index 9df8e6c88..536988cb1 100644 --- a/src/doc/book/src/ch00-00-introduction.md +++ b/src/doc/book/src/ch00-00-introduction.md @@ -4,7 +4,7 @@ > Language][nsprust] available in print and ebook format from [No Starch > Press][nsp]. -[nsprust]: https://nostarch.com/rust +[nsprust]: https://nostarch.com/rust-programming-language-2nd-edition [nsp]: https://nostarch.com/ Welcome to *The Rust Programming Language*, an introductory book about Rust. diff --git a/src/doc/book/src/ch03-01-variables-and-mutability.md b/src/doc/book/src/ch03-01-variables-and-mutability.md index 883a53050..058f7bb5c 100644 --- a/src/doc/book/src/ch03-01-variables-and-mutability.md +++ b/src/doc/book/src/ch03-01-variables-and-mutability.md @@ -82,7 +82,7 @@ First, you aren’t allowed to use `mut` with constants. Constants aren’t just immutable by default—they’re always immutable. You declare constants using the `const` keyword instead of the `let` keyword, and the type of the value *must* be annotated. We’ll cover types and type annotations in the next section, -[“Data Types,”][data-types], so don’t worry about the details +[“Data Types”][data-types], so don’t worry about the details right now. Just know that you must always annotate the type. Constants can be declared in any scope, including the global scope, which makes diff --git a/src/doc/book/src/ch10-01-syntax.md b/src/doc/book/src/ch10-01-syntax.md index c22aef7c3..431dba966 100644 --- a/src/doc/book/src/ch10-01-syntax.md +++ b/src/doc/book/src/ch10-01-syntax.md @@ -33,9 +33,9 @@ the duplication by introducing a generic type parameter in a single function. To parameterize the types in a new single function, we need to name the type parameter, just as we do for the value parameters to a function. You can use any identifier as a type parameter name. But we’ll use `T` because, by -convention, type parameter names in Rust are short, often just a letter, and Rust’s -type-naming convention is CamelCase. Short for “type,” `T` is the default -choice of most Rust programmers. +convention, type parameter names in Rust are short, often just a letter, and +Rust’s type-naming convention is UpperCamelCase. Short for “type,” `T` is the +default choice of most Rust programmers. When we use a parameter in the body of the function, we have to declare the parameter name in the signature so the compiler knows what that name means. @@ -274,7 +274,7 @@ method. ### Performance of Code Using Generics You might be wondering whether there is a runtime cost when using generic type -parameters. The good news is that using generic types won't make your program run +parameters. The good news is that using generic types won't make your program run any slower than it would with concrete types. Rust accomplishes this by performing monomorphization of the code using diff --git a/src/doc/book/src/title-page.md b/src/doc/book/src/title-page.md index 12d1a6598..5f7a7a680 100644 --- a/src/doc/book/src/title-page.md +++ b/src/doc/book/src/title-page.md @@ -2,7 +2,7 @@ *by Steve Klabnik and Carol Nichols, with contributions from the Rust Community* -This version of the text assumes you’re using Rust 1.65 (released 2022-11-03) +This version of the text assumes you’re using Rust 1.67.1 (released 2023-02-09) or later. See the [“Installation” section of Chapter 1][install] to install or update Rust. @@ -18,7 +18,7 @@ Press][nsprust]. [install]: ch01-01-installation.html [editions]: appendix-05-editions.html -[nsprust]: https://nostarch.com/rust +[nsprust]: https://nostarch.com/rust-programming-language-2nd-edition [translations]: appendix-06-translation.html > **🚨 Want a more interactive learning experience? Try out a different version diff --git a/src/doc/embedded-book/src/assets/verify.jpeg b/src/doc/embedded-book/src/assets/verify.jpeg index a6e9376c5..ed4664578 100644 Binary files a/src/doc/embedded-book/src/assets/verify.jpeg and b/src/doc/embedded-book/src/assets/verify.jpeg differ diff --git a/src/doc/embedded-book/src/collections/index.md b/src/doc/embedded-book/src/collections/index.md index 7319a397a..aa03666ac 100644 --- a/src/doc/embedded-book/src/collections/index.md +++ b/src/doc/embedded-book/src/collections/index.md @@ -49,9 +49,8 @@ in your program instead of this allocator. ``` rust,ignore // Bump pointer allocator implementation -extern crate cortex_m; - -use core::alloc::GlobalAlloc; +use core::alloc::{GlobalAlloc, Layout}; +use core::cell::UnsafeCell; use core::ptr; use cortex_m::interrupt; @@ -144,8 +143,7 @@ as they are exact same implementation. allocator. Just `use` its collections and proceed to instantiate them: ```rust,ignore -extern crate heapless; // v0.4.x - +// heapless version: v0.4.x use heapless::Vec; use heapless::consts::*; @@ -155,6 +153,7 @@ fn main() -> ! { xs.push(42).unwrap(); assert_eq!(xs.pop(), Some(42)); + loop {} } ``` diff --git a/src/doc/embedded-book/src/interoperability/c-with-rust.md b/src/doc/embedded-book/src/interoperability/c-with-rust.md index d5d8db39b..6bec3bd87 100644 --- a/src/doc/embedded-book/src/interoperability/c-with-rust.md +++ b/src/doc/embedded-book/src/interoperability/c-with-rust.md @@ -125,8 +125,6 @@ For projects with limited dependencies or complexity, or for projects where it i In the simplest case of compiling a single C file as a dependency to a static library, an example `build.rs` script using the [`cc` crate] would look like this: ```rust,ignore -extern crate cc; - fn main() { cc::Build::new() .file("foo.c") diff --git a/src/doc/embedded-book/src/intro/install/macos.md b/src/doc/embedded-book/src/intro/install/macos.md index b3e51bcf1..9a797563a 100644 --- a/src/doc/embedded-book/src/intro/install/macos.md +++ b/src/doc/embedded-book/src/intro/install/macos.md @@ -1,8 +1,11 @@ # macOS -All the tools can be install using [Homebrew]: +All the tools can be installed using [Homebrew] or [MacPorts]: [Homebrew]: http://brew.sh/ +[MacPorts]: https://www.macports.org/ + +## Install tools with [Homebrew] ``` text $ # GDB @@ -20,6 +23,21 @@ $ brew install qemu $ brew install --HEAD openocd ``` +## Install tools with [MacPorts] + +``` text +$ # GDB +$ sudo port install arm-none-eabi-gcc + +$ # OpenOCD +$ sudo port install openocd + +$ # QEMU +$ sudo port install qemu +``` + + + That's all! Go to the [next section]. [next section]: verify.md diff --git a/src/doc/embedded-book/src/intro/install/verify.md b/src/doc/embedded-book/src/intro/install/verify.md index 921db8cc7..e60911dc3 100644 --- a/src/doc/embedded-book/src/intro/install/verify.md +++ b/src/doc/embedded-book/src/intro/install/verify.md @@ -8,7 +8,7 @@ discovery board has two USB connectors; use the one labeled "USB ST-LINK" that sits on the center of the edge of the board. Also check that the ST-LINK header is populated. See the picture below; the -ST-LINK header is circled in red. +ST-LINK header is highlighted.

diff --git a/src/doc/embedded-book/src/peripherals/singletons.md b/src/doc/embedded-book/src/peripherals/singletons.md index 3f04aa170..1f3a556e8 100644 --- a/src/doc/embedded-book/src/peripherals/singletons.md +++ b/src/doc/embedded-book/src/peripherals/singletons.md @@ -61,8 +61,7 @@ This has a small runtime overhead because we must wrap the `SerialPort` structur Although we created our own `Peripherals` structure above, it is not necessary to do this for your code. the `cortex_m` crate contains a macro called `singleton!()` that will perform this action for you. ```rust,ignore -#[macro_use(singleton)] -extern crate cortex_m; +use cortex_m::singleton; fn main() { // OK if `main` is executed only once diff --git a/src/doc/embedded-book/src/start/registers.md b/src/doc/embedded-book/src/start/registers.md index d5bb3e0cc..fe184792c 100644 --- a/src/doc/embedded-book/src/start/registers.md +++ b/src/doc/embedded-book/src/start/registers.md @@ -24,7 +24,7 @@ You may well find that the code you need to access the peripherals in your micro A board crate is the perfect starting point, if you're new to embedded Rust. They nicely abstract the HW details that might be overwhelming when starting studying this subject, and makes standard tasks easy, like turning a LED on or off. The functionality it exposes varies a lot between boards. Since this book aims at staying hardware agnostic, the board crates won't be covered by this book. -If you want to experiment with the STM32F3DISCOVERY board, it is highly recommmand to take a look at the [stm32f3-discovery] board crate, which provides functionality to blink the board LEDs, access its compass, bluetooth and more. The [Discovery] book offers a great introduction to the use of a board crate. +If you want to experiment with the STM32F3DISCOVERY board, it is highly recommended to take a look at the [stm32f3-discovery] board crate, which provides functionality to blink the board LEDs, access its compass, bluetooth and more. The [Discovery] book offers a great introduction to the use of a board crate. But if you're working on a system that doesn't yet have dedicated board crate, or you need functionality not provided by existing crates, read on as we start from the bottom, with the micro-architecture crates. diff --git a/src/doc/index.md b/src/doc/index.md index bf08960f3..7c97c16c2 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -4,6 +4,20 @@ nav { display: none; } +body { + font-family: serif; +} +h1, h2, h3, h4, h5, h6 { + font-family: sans-serif; +} +h3 { + font-size: 1.35rem; +} +h4 { + font-size: 1.1rem; +} + +/* Formatting for docs search bar */ #search-input { width: calc(100% - 58px); } @@ -21,53 +35,74 @@ nav { #search-but:hover, #search-input:focus { border-color: #55a9ff; } -h2 { - font-size: 18px; + +/* Formatting for external link icon */ +svg.external-link { + display: inline-block; + position: relative; + vertical-align: super; + width: 0.7rem; + height: 0.7rem; + padding-left: 2px; + top: 3px; } -Welcome to an overview of the documentation provided by the [Rust project]. -All of these projects are managed by the Docs Team; there are other -unofficial documentation resources as well! +Welcome to an overview of the documentation provided by the [Rust +project]. This page contains links to various helpful references, +most of which are available offline (if opened with `rustup doc`). Many of these +resources take the form of "books"; we collectively call these "The Rust +Bookshelf." Some are large, some are small. -Many of these resources take the form of "books"; we collectively call these -"The Rust Bookshelf." Some are large, some are small. +All of these books are managed by the Rust Organization, but other unofficial +documentation resources are included here as well! -# Learn Rust +If you're just looking for the standard library reference, here it is: +[Rust API documentation](std/index.html) -If you'd like to learn Rust, this is the spot for you! All of these resources + +## Learning Rust + +If you'd like to learn Rust, this is the section for you! All of these resources assume that you have programmed before, but not in any specific language: -## The Rust Programming Language +### The Rust Programming Language -Affectionately nicknamed "the book," [The Rust Programming -Language](book/index.html) will give you an overview of the language from -first principles. You'll build a few projects along the way, and by the end, -you'll have a solid grasp of the language. +Affectionately nicknamed "the book," [The Rust Programming Language](book/index.html) +will give you an overview of the language from first principles. You'll build a +few projects along the way, and by the end, you'll have a solid grasp of how to +use the language. -## Rust By Example +### Rust By Example If reading multiple hundreds of pages about a language isn't your style, then -[Rust By Example](rust-by-example/index.html) has you covered. While the book talks about code with -a lot of words, RBE shows off a bunch of code, and keeps the talking to a -minimum. It also includes exercises! +[Rust By Example](rust-by-example/index.html) has you covered. RBE shows off a +bunch of code without using a lot of words. It also includes exercises! + +### Rustlings + +[Rustlings](https://github.com/rust-lang/rustlings) guides you +through downloading and setting up the Rust toolchain, then provides an +interactive tool that teaches you how to solve coding challenges in Rust. + +### Rust Playground -## Rustlings +The [Rust Playground](https://play.rust-lang.org) is a great place +to try out and share small bits of code, or experiment with some of the most +popular crates. -[Rustlings](https://github.com/rust-lang/rustlings) guides you through downloading and setting up the Rust toolchain, -and teaches you the basics of reading and writing Rust syntax. It's an -alternative to Rust by Example that works with your own environment. -# Use Rust +## Using Rust -Once you've gotten familiar with the language, these resources can help you -when you're actually using it day-to-day. +Once you've gotten familiar with the language, these resources can help you put +it to work. -## The Standard Library +### The Standard Library -Rust's standard library has [extensive API documentation](std/index.html), -with explanations of how to use various things, as well as example code for -accomplishing various tasks. +Rust's standard library has [extensive API documentation](std/index.html), with +explanations of how to use various things, as well as example code for +accomplishing various tasks. Code examples have a "Run" button on hover that +opens the sample in the playground.

@@ -77,76 +112,143 @@ accomplishing various tasks.
-## The Edition Guide +### Your Personal Documentation -[The Edition Guide](edition-guide/index.html) describes the Rust editions. +Whenever you are working in a crate, `cargo doc --open` will generate +documentation for your project _and_ all its dependencies in their correct +version, and open it in your browser. Add the flag `--document-private-items` to +also show items not marked `pub`. -## The Rustc Book +### The Edition Guide -[The Rustc Book](rustc/index.html) describes the Rust compiler, `rustc`. +[The Edition Guide](edition-guide/index.html) describes the Rust editions and +their differences. -## The Cargo Book +### The `rustc` Book -[The Cargo Book](cargo/index.html) is a guide to Cargo, Rust's build tool and dependency manager. +[The `rustc` Book](rustc/index.html) describes the Rust compiler, `rustc`. -## The Rustdoc Book +### The Cargo Book + +[The Cargo Book](cargo/index.html) is a guide to Cargo, Rust's build tool and +dependency manager. + +### The Rustdoc Book [The Rustdoc Book](rustdoc/index.html) describes our documentation tool, `rustdoc`. -## The Clippy Book +### The Clippy Book [The Clippy Book](clippy/index.html) describes our static analyzer, Clippy. -## Extended Error Listing +### Extended Error Listing Many of Rust's errors come with error codes, and you can request extended -diagnostics from the compiler on those errors. You can also [read them -here](error_codes/index.html), if you prefer to read them that way. +diagnostics from the compiler on those errors (with `rustc --explain`). You can +also read them here if you prefer: [rustc error codes](error_codes/index.html) + -# Master Rust +## Mastering Rust Once you're quite familiar with the language, you may find these advanced resources useful. -## The Reference +### The Reference -[The Reference](reference/index.html) is not a formal spec, but is more detailed and -comprehensive than the book. +[The Reference](reference/index.html) is not a formal spec, but is more detailed +and comprehensive than the book. -## The Style Guide +### The Style Guide -[The Rust Style Guide](style-guide/index.html) describes the standard formatting of Rust -code. Most developers use rustfmt to format their code, and rustfmt's default -formatting matches this style guide. +[The Rust Style Guide](style-guide/index.html) describes the standard formatting +of Rust code. Most developers use `cargo fmt` to invoke `rustfmt` and format the +code automatically (the result matches this style guide). -## The Rustonomicon +### The Rustonomicon -[The Rustonomicon](nomicon/index.html) is your guidebook to the dark arts of unsafe -Rust. It's also sometimes called "the 'nomicon." +[The Rustonomicon](nomicon/index.html) is your guidebook to the dark arts of +unsafe Rust. It's also sometimes called "the 'nomicon." -## The Unstable Book +### The Unstable Book -[The Unstable Book](unstable-book/index.html) has documentation for unstable features. +[The Unstable Book](unstable-book/index.html) has documentation for unstable +features. -## The `rustc` Contribution Guide +### The `rustc` Contribution Guide -[The `rustc` Guide](https://rustc-dev-guide.rust-lang.org/) documents how -the compiler works and how to contribute to it. This is useful if you want to build -or modify the Rust compiler from source (e.g. to target something non-standard). +[The `rustc` Guide](https://rustc-dev-guide.rust-lang.org/) +documents how the compiler works and how to contribute to it. This is useful if +you want to build or modify the Rust compiler from source (e.g. to target +something non-standard). -# Specialize Rust -When using Rust in specific domain areas, consider using the following resources tailored to each domain. +## Specialized Rust -## Embedded Systems +When using Rust in specific domains, consider using the following resources +tailored to each area. -When developing for Bare Metal or Embedded Linux systems, you may find these resources maintained by the [Embedded Working Group] useful. +### Embedded Systems + +When developing for Bare Metal or Embedded Linux systems, you may find these +resources maintained by the [Embedded Working Group] useful. [Embedded Working Group]: https://github.com/rust-embedded -### The Embedded Rust Book +#### The Embedded Rust Book -[The Embedded Rust Book] is targeted at developers familiar with embedded development and familiar with Rust, but have not used Rust for embedded development. +[The Embedded Rust Book] is targeted at developers familiar with embedded +development and familiar with Rust, but have not used Rust for embedded +development. [The Embedded Rust Book]: embedded-book/index.html [Rust project]: https://www.rust-lang.org + + diff --git a/src/doc/nomicon/src/dropck.md b/src/doc/nomicon/src/dropck.md index 75940219c..4063d5619 100644 --- a/src/doc/nomicon/src/dropck.md +++ b/src/doc/nomicon/src/dropck.md @@ -250,7 +250,7 @@ fn main() { inspector: None, days: Box::new(1), }; - world.inspector = Some(Inspector(&world.days, "gatget")); + world.inspector = Some(Inspector(&world.days, "gadget")); } ``` diff --git a/src/doc/nomicon/src/ffi.md b/src/doc/nomicon/src/ffi.md index 8d1a882b3..684e7125b 100644 --- a/src/doc/nomicon/src/ffi.md +++ b/src/doc/nomicon/src/ffi.md @@ -721,17 +721,20 @@ No `transmute` required! ## FFI and unwinding -It’s important to be mindful of unwinding when working with FFI. Each -non-`Rust` ABI comes in two variants, one with `-unwind` suffix and one without. If -you expect Rust `panic`s or foreign (e.g. C++) exceptions to cross an FFI -boundary, that boundary must use the appropriate `-unwind` ABI string (note -that compiling with `panic=abort` will still cause `panic!` to immediately -abort the process, regardless of which ABI is specified by the function that -`panic`s). +It’s important to be mindful of unwinding when working with FFI. Most +ABI strings come in two variants, one with an `-unwind` suffix and one without. +The `Rust` ABI always permits unwinding, so there is no `Rust-unwind` ABI. +If you expect Rust `panic`s or foreign (e.g. C++) exceptions to cross an FFI +boundary, that boundary must use the appropriate `-unwind` ABI string. Conversely, if you do not expect unwinding to cross an ABI boundary, use one of -the non-`unwind` ABI strings (other than `Rust`, which always permits -unwinding). If an unwinding operation does encounter an ABI boundary that is +the non-`unwind` ABI strings. + +> Note: Compiling with `panic=abort` will still cause `panic!` to immediately +abort the process, regardless of which ABI is specified by the function that +`panic`s. + +If an unwinding operation does encounter an ABI boundary that is not permitted to unwind, the behavior depends on the source of the unwinding (Rust `panic` or a foreign exception): diff --git a/src/doc/nomicon/src/send-and-sync.md b/src/doc/nomicon/src/send-and-sync.md index 34539daa3..808a5c3ae 100644 --- a/src/doc/nomicon/src/send-and-sync.md +++ b/src/doc/nomicon/src/send-and-sync.md @@ -94,6 +94,7 @@ to the heap. use std::{ mem::{align_of, size_of}, ptr, + cmp::max, }; struct Carton(ptr::NonNull); @@ -105,8 +106,8 @@ impl Carton { let mut memptr: *mut T = ptr::null_mut(); unsafe { let ret = libc::posix_memalign( - (&mut memptr).cast(), - align_of::(), + (&mut memptr as *mut *mut T).cast(), + max(align_of::(), size_of::()), size_of::() ); assert_eq!(ret, 0, "Failed to allocate or invalid alignment"); diff --git a/src/doc/nomicon/src/subtyping.md b/src/doc/nomicon/src/subtyping.md index 6f0c12db4..cc48a5970 100644 --- a/src/doc/nomicon/src/subtyping.md +++ b/src/doc/nomicon/src/subtyping.md @@ -339,7 +339,7 @@ lifetimes to be covariant: as soon as you try to stuff them in something like a mutable reference, they inherit invariance and you're prevented from doing anything bad. -However Box makes it easier to focus on by-value aspect of references that we +However, Box makes it easier to focus on the by-value aspect of references that we partially glossed over. Unlike a lot of languages which allow values to be freely aliased at all times, diff --git a/src/doc/reference/src/behavior-considered-undefined.md b/src/doc/reference/src/behavior-considered-undefined.md index f8bffd13e..31963d1e5 100644 --- a/src/doc/reference/src/behavior-considered-undefined.md +++ b/src/doc/reference/src/behavior-considered-undefined.md @@ -25,7 +25,7 @@ code. * Data races. * Evaluating a [dereference expression] (`*expr`) on a raw pointer that is [dangling] or unaligned, even in [place expression context] - (e.g. `addr_of!(&*expr)`). + (e.g. `addr_of!(*expr)`). * Breaking the [pointer aliasing rules]. `Box`, `&mut T` and `&T` follow LLVM’s scoped [noalias] model, except if the `&T` contains an [`UnsafeCell`]. References and boxes must not be [dangling] while they are diff --git a/src/doc/reference/src/destructors.md b/src/doc/reference/src/destructors.md index f89fde157..84aac89d3 100644 --- a/src/doc/reference/src/destructors.md +++ b/src/doc/reference/src/destructors.md @@ -162,7 +162,7 @@ smallest scope that contains the expression and is one of the following: * The `else` block of an `if` expression. * The condition expression of an `if` or `while` expression, or a `match` guard. -* The expression for a match arm. +* The body expression for a match arm. * The second operand of a [lazy boolean expression]. > **Notes**: diff --git a/src/doc/reference/src/expressions/if-expr.md b/src/doc/reference/src/expressions/if-expr.md index 591437fc9..fe387c828 100644 --- a/src/doc/reference/src/expressions/if-expr.md +++ b/src/doc/reference/src/expressions/if-expr.md @@ -129,7 +129,7 @@ The expression cannot be a [lazy boolean operator expression][_LazyBooleanOperat Use of a lazy boolean operator is ambiguous with a planned feature change of the language (the implementation of if-let chains - see [eRFC 2947][_eRFCIfLetChain_]). When lazy boolean operator expression is desired, this can be achieved by using parenthesis as below: - + ```rust,ignore // Before... if let PAT = EXPR && EXPR { .. } diff --git a/src/doc/reference/src/expressions/operator-expr.md b/src/doc/reference/src/expressions/operator-expr.md index 9f7e8edac..691f801e8 100644 --- a/src/doc/reference/src/expressions/operator-expr.md +++ b/src/doc/reference/src/expressions/operator-expr.md @@ -600,7 +600,7 @@ It will then set the value of the assigned operand's place to the value of perfo > **Note**: This is different than other expressions in that the right operand is evaluated before the left one. -Otherwise, this expression is syntactic sugar for calling the function of the overloading compound assigment trait of the operator (see the table earlier in this chapter). +Otherwise, this expression is syntactic sugar for calling the function of the overloading compound assignment trait of the operator (see the table earlier in this chapter). A mutable borrow of the assigned operand is automatically taken. For example, the following expression statements in `example` are equivalent: diff --git a/src/doc/reference/src/items/external-blocks.md b/src/doc/reference/src/items/external-blocks.md index d89536968..ce2190829 100644 --- a/src/doc/reference/src/items/external-blocks.md +++ b/src/doc/reference/src/items/external-blocks.md @@ -90,6 +90,7 @@ There are also some platform-specific ABI strings: `__fastcall` and GCC and clang's `__attribute__((fastcall))` * `extern "vectorcall"` -- The `vectorcall` ABI -- corresponds to MSVC's `__vectorcall` and clang's `__attribute__((vectorcall))` +* `extern "efiapi"` -- The ABI used for [UEFI] functions. ## Variadic functions @@ -288,6 +289,7 @@ Attributes on extern function parameters follow the same rules and restrictions as [regular function parameters]. [IDENTIFIER]: ../identifiers.md +[UEFI]: https://uefi.org/specifications [WebAssembly module]: https://webassembly.github.io/spec/core/syntax/modules.html [functions]: functions.md [statics]: static-items.md diff --git a/src/doc/reference/src/linkage.md b/src/doc/reference/src/linkage.md index b15200511..82864b004 100644 --- a/src/doc/reference/src/linkage.md +++ b/src/doc/reference/src/linkage.md @@ -73,7 +73,7 @@ be ignored in favor of only building the artifacts specified by command line. being built for a different target. Note that these outputs are stackable in the sense that if multiple are -specified, then the compiler will produce each form of output at once without +specified, then the compiler will produce each form of output without having to recompile. However, this only applies for outputs specified by the same method. If only `crate_type` attributes are specified, then they will all be built, but if one or more `--crate-type` command line flags are specified, diff --git a/src/doc/reference/src/patterns.md b/src/doc/reference/src/patterns.md index 14bbac155..370e1990c 100644 --- a/src/doc/reference/src/patterns.md +++ b/src/doc/reference/src/patterns.md @@ -380,16 +380,19 @@ match tuple { > **Syntax**\ > _RangePattern_ :\ ->       _InclusiveRangePattern_\ ->    | _HalfOpenRangePattern_\ +>       _RangeInclusivePattern_\ +>    | _RangeFromPattern_\ +>    | _RangeToInclusivePattern_\ >    | _ObsoleteRangePattern_ > -> _InclusiveRangePattern_ :\ +> _RangeInclusivePattern_ :\ >       _RangePatternBound_ `..=` _RangePatternBound_ > -> _HalfOpenRangePattern_ :\ +> _RangeFromPattern_ :\ >       _RangePatternBound_ `..` ->    | `..=` _RangePatternBound_ +> +> _RangeToInclusivePattern_ :\ +>       `..=` _RangePatternBound_ > > _ObsoleteRangePattern_ :\ >    _RangePatternBound_ `...` _RangePatternBound_ @@ -402,16 +405,13 @@ match tuple { >    | [_PathExpression_] *Range patterns* match scalar values within the range defined by their bounds. -A bound on the left of its sigils is a *lower bound*. +They comprise a *sigil* (one of `..`, `..=`, or `...`) and a bound on one or both sides. +A bound on the left of the sigil is a *lower bound*. A bound on the right is an *upper bound*. -A range pattern may be closed or half-open. - -A range pattern is *closed* if it has both a lower and an upper bound. -The only closed ranged pattern is the inclusive range pattern. -*Inclusive range patterns* match all the values between and including both of its bounds. -It is written as its lower bounds, followed by `..=`, followed by its upper bounds. -The type of it is the type unification of its upper and lower bounds. +A range pattern with both a lower and upper bound will match all values between and including both of its bounds. +It is written as its lower bound, followed by `..=`, followed by its upper bound. +The type of the range pattern is the type unification of its upper and lower bounds. For example, a pattern `'m'..='p'` will match only the values `'m'`, `'n'`, `'o'`, and `'p'`. @@ -419,19 +419,15 @@ The lower bound cannot be greater than the upper bound. That is, in `a..=b`, a ≤ b must be the case. For example, it is an error to have a range pattern `10..=0`. -Range patterns are *half-open* if they have only an upper or lower bound. -They have the same type as their upper or lower bound. - -A half open range with only a lower bound is written as its lower bound followed by `..`. -These range patterns will match on any value greater than or equal to the lower bound. +A range pattern with only a lower bound will match any value greater than or equal to the lower bound. +It is written as its lower bound followed by `..`, and has the same type as its lower bound. For example, `1..` will match 1, 9, or 9001, or 9007199254740991 (if it is of an appropriate size), but not 0, and not negative numbers for signed integers. -The bounds can be literals or paths that point to constant values. -A half open range with only an upper bound is written as `..=` followed by its upper bound. -These range patterns will match on any value less than or equal to the upper bound. -For example, `..=10` will match 10, 1, 0, and for signed interger types, all negative values. +A range pattern with only an upper bound matches any value less than or equal to the upper bound. +It is written as `..=` followed by its upper bound, and has the same type as its upper bound. +For example, `..=10` will match 10, 1, 0, and for signed integer types, all negative values. -Half-open range patterns cannot be used as the top-level pattern for subpatterns in [slice patterns](#slice-patterns). +Range patterns with only one bound cannot be used as the top-level pattern for subpatterns in [slice patterns](#slice-patterns). The bounds is written as one of: @@ -529,7 +525,7 @@ The range of values for a `char` type are precisely those ranges containing all Floating point range patterns are deprecated and may be removed in a future Rust release. See [issue #41620](https://github.com/rust-lang/rust/issues/41620) for more information. -> **Edition Differences**: Before the 2021 edition, closed range patterns may also be written using `...` as an alternative to `..=`, with the same meaning. +> **Edition Differences**: Before the 2021 edition, range patterns with both a lower and upper bound may also be written using `...` in place of `..=`, with the same meaning. > **Note**: Although range patterns use the same syntax as [range expressions], there are no exclusive range patterns. > That is, neither `x .. y` nor `.. x` are valid range patterns. @@ -747,7 +743,8 @@ match v[..] { Slice patterns are irrefutable when matching an array as long as each element is irrefutable. When matching a slice, it is irrefutable only in the form with a single `..` [rest pattern](#rest-patterns) or [identifier pattern](#identifier-patterns) with the `..` rest pattern as a subpattern. -Within a slice, a half-open range pattern like `a..` must be enclosed in parentheses, as in `(a..)`, to clarify it is intended to match a single value. +Within a slice, a range pattern without both lower and upper bound must be enclosed in parentheses, as in `(a..)`, to clarify it is intended to match against a single slice element. +A range pattern with both lower and upper bound, like `a..=b`, is not required to be enclosed in parentheses. ## Path patterns diff --git a/src/doc/reference/src/trait-bounds.md b/src/doc/reference/src/trait-bounds.md index c8dab3f1c..0a6731288 100644 --- a/src/doc/reference/src/trait-bounds.md +++ b/src/doc/reference/src/trait-bounds.md @@ -27,7 +27,7 @@ provided on any type in a [where clause]. There are also shorter forms for certain common cases: * Bounds written after declaring a [generic parameter][generic]: - `fn f() {}` is the same as `fn f where A: Copy () {}`. + `fn f() {}` is the same as `fn f() where A: Copy {}`. * In trait declarations as [supertraits]: `trait Circle : Shape {}` is equivalent to `trait Circle where Self : Shape {}`. * In trait declarations as bounds on [associated types]: diff --git a/src/doc/rust-by-example/src/error/option_unwrap/and_then.md b/src/doc/rust-by-example/src/error/option_unwrap/and_then.md index c065f2043..42a1f3ec0 100644 --- a/src/doc/rust-by-example/src/error/option_unwrap/and_then.md +++ b/src/doc/rust-by-example/src/error/option_unwrap/and_then.md @@ -39,10 +39,7 @@ fn have_recipe(food: Food) -> Option { fn cookable_v1(food: Food) -> Option { match have_recipe(food) { None => None, - Some(food) => match have_ingredients(food) { - None => None, - Some(food) => Some(food), - }, + Some(food) => have_ingredients(food), } } diff --git a/src/doc/rust-by-example/src/error/result/result_map.md b/src/doc/rust-by-example/src/error/result/result_map.md index 24537c318..c453d9fa9 100644 --- a/src/doc/rust-by-example/src/error/result/result_map.md +++ b/src/doc/rust-by-example/src/error/result/result_map.md @@ -56,7 +56,7 @@ use std::num::ParseIntError; // As with `Option`, we can use combinators such as `map()`. // This function is otherwise identical to the one above and reads: -// Modify n if the value is valid, otherwise pass on the error. +// Multiply if both values can be parsed from str, otherwise pass on the error. fn multiply(first_number_str: &str, second_number_str: &str) -> Result { first_number_str.parse::().and_then(|first_number| { second_number_str.parse::().map(|second_number| first_number * second_number) diff --git a/src/doc/rust-by-example/src/generics/bounds.md b/src/doc/rust-by-example/src/generics/bounds.md index 5d7e849a8..86e54e670 100644 --- a/src/doc/rust-by-example/src/generics/bounds.md +++ b/src/doc/rust-by-example/src/generics/bounds.md @@ -58,10 +58,10 @@ fn main() { let _triangle = Triangle { length: 3.0, height: 4.0 }; print_debug(&rectangle); - println!("Area: {}", rectangle.area()); + println!("Area: {}", area(&rectangle)); //print_debug(&_triangle); - //println!("Area: {}", _triangle.area()); + //println!("Area: {}", area(&_triangle)); // ^ TODO: Try uncommenting these. // | Error: Does not implement either `Debug` or `HasArea`. } diff --git a/src/doc/rust-by-example/src/hello/comment.md b/src/doc/rust-by-example/src/hello/comment.md index 5027f0a22..4ea6dcd44 100644 --- a/src/doc/rust-by-example/src/hello/comment.md +++ b/src/doc/rust-by-example/src/hello/comment.md @@ -14,7 +14,7 @@ a few different varieties: fn main() { // This is an example of a line comment. // There are two slashes at the beginning of the line. - // And nothing written inside these will be read by the compiler. + // And nothing written after these will be read by the compiler. // println!("Hello, world!"); diff --git a/src/doc/rust-by-example/src/index.md b/src/doc/rust-by-example/src/index.md index fecc1906a..ecadff4cc 100644 --- a/src/doc/rust-by-example/src/index.md +++ b/src/doc/rust-by-example/src/index.md @@ -27,7 +27,7 @@ Now let's begin! - [Flow of Control](flow_control.md) - `if`/`else`, `for`, and others. -- [Functions](fn.md) - Learn about Methods, Closures and High Order Functions. +- [Functions](fn.md) - Learn about Methods, Closures and Higher Order Functions. - [Modules](mod.md) - Organize code using modules diff --git a/src/doc/rust-by-example/src/primitives/tuples.md b/src/doc/rust-by-example/src/primitives/tuples.md index e745d89be..75c265e75 100644 --- a/src/doc/rust-by-example/src/primitives/tuples.md +++ b/src/doc/rust-by-example/src/primitives/tuples.md @@ -43,7 +43,7 @@ fn main() { let pair = (1, true); println!("Pair is {:?}", pair); - println!("Uhe reversed pair is {:?}", reverse(pair)); + println!("The reversed pair is {:?}", reverse(pair)); // To create one element tuples, the comma is required to tell them apart // from a literal surrounded by parentheses. diff --git a/src/doc/rust-by-example/src/std_misc/file/create.md b/src/doc/rust-by-example/src/std_misc/file/create.md index 709213c9d..4f113df88 100644 --- a/src/doc/rust-by-example/src/std_misc/file/create.md +++ b/src/doc/rust-by-example/src/std_misc/file/create.md @@ -53,6 +53,6 @@ proident, sunt in culpa qui officia deserunt mollit anim id est laborum. (As in the previous example, you are encouraged to test this example under failure conditions.) -There is [`OpenOptions`] struct that can be used to configure how a file is opened. +The [`OpenOptions`] struct can be used to configure how a file is opened. [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index a21e342cb..2346698d4 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -16,10 +16,11 @@ jobs: env: MDBOOK_VERSION: 0.4.21 MDBOOK_LINKCHECK_VERSION: 0.7.6 - MDBOOK_MERMAID_VERSION: 0.11.2 - MDBOOK_TOC_VERSION: 0.9.0 + MDBOOK_MERMAID_VERSION: 0.12.6 + MDBOOK_TOC_VERSION: 0.11.2 DEPLOY_DIR: book/html BASE_SHA: ${{ github.event.pull_request.base.sha }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v3 with: diff --git a/src/doc/rustc-dev-guide/CITATION.cff b/src/doc/rustc-dev-guide/CITATION.cff new file mode 100644 index 000000000..fee82ad8c --- /dev/null +++ b/src/doc/rustc-dev-guide/CITATION.cff @@ -0,0 +1,9 @@ +cff-version: 1.2.0 +message: If you use this guide, please cite it using these metadata. +title: Rust Compiler Development Guide (rustc-dev-guide) +abstract: A guide to developing the Rust compiler (rustc) +authors: + - name: "The Rust Project Developers" +date-released: "2018-01-16" +license: "MIT OR Apache-2.0" +repository-code: "https://github.com/rust-lang/rustc-dev-guide" diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 006ea3231..e501c9161 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -43,7 +43,7 @@ rustdocs][rustdocs]. To build a local static HTML site, install [`mdbook`](https://github.com/rust-lang/mdBook) with: ``` -> cargo install mdbook mdbook-linkcheck mdbook-toc +> cargo install mdbook mdbook-linkcheck mdbook-toc mdbook-mermaid ``` and execute the following command in the root of the repository: diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml index dc216760e..a5d794b50 100644 --- a/src/doc/rustc-dev-guide/book.toml +++ b/src/doc/rustc-dev-guide/book.toml @@ -1,7 +1,7 @@ [book] -title = "Guide to Rustc Development" -author = "Rustc developers" -description = "A guide to developing rustc" +title = "Rust Compiler Development Guide" +author = "The Rust Project Developers" +description = "A guide to developing the Rust compiler (rustc)" [build] create-missing = false @@ -18,6 +18,9 @@ git-repository-url = "https://github.com/rust-lang/rustc-dev-guide" edit-url-template = "https://github.com/rust-lang/rustc-dev-guide/edit/master/{path}" additional-js = ["mermaid.min.js", "mermaid-init.js"] +[output.html.search] +use-boolean-and = true + [output.html.fold] enable = true level = 0 @@ -40,6 +43,9 @@ exclude = [ cache-timeout = 86400 warning-policy = "error" +[output.linkcheck.http-headers] +'github\.com' = ["Authorization: Bearer $GITHUB_TOKEN"] + [output.html.redirect] "/compiletest.html" = "tests/compiletest.html" "/diagnostics/sessiondiagnostic.html" = "diagnostics/diagnostic-structs.html" diff --git a/src/doc/rustc-dev-guide/ci/date-check/src/main.rs b/src/doc/rustc-dev-guide/ci/date-check/src/main.rs index 70fce8b1c..d9e8145a3 100644 --- a/src/doc/rustc-dev-guide/ci/date-check/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/date-check/src/main.rs @@ -170,7 +170,7 @@ fn main() { for (path, dates) in dates_by_file { println!( - "- [ ] {}", + "- {}", path.strip_prefix(&root_dir_path).unwrap_or(&path).display(), ); for (line, date) in dates { diff --git a/src/doc/rustc-dev-guide/examples/README b/src/doc/rustc-dev-guide/examples/README new file mode 100644 index 000000000..ca49dd74d --- /dev/null +++ b/src/doc/rustc-dev-guide/examples/README @@ -0,0 +1,11 @@ +For each example to compile, you will need to first run the following: + + rustup component add rustc-dev llvm-tools + +To create an executable: + + rustc rustc-driver-example.rs + +To run an executable: + + rustup run nightly ./rustc-driver-example diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index 8d8b40cd7..9708ab01d 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -1,10 +1,6 @@ #![feature(rustc_private)] -// NOTE: For the example to compile, you will need to first run the following: -// rustup component add rustc-dev llvm-tools-preview - -// version: rustc 1.68.0-nightly (935dc0721 2022-12-19) - +extern crate rustc_driver; extern crate rustc_error_codes; extern crate rustc_errors; extern crate rustc_hash; @@ -46,10 +42,9 @@ fn main() { "# .into(), }, - input_path: None, // Option - output_dir: None, // Option - output_file: None, // Option - file_loader: None, // Option> + output_dir: None, // Option + output_file: None, // Option + file_loader: None, // Option> lint_caps: FxHashMap::default(), // FxHashMap // This is a callback from the driver that is called when [`ParseSess`] is created. parse_sess_created: None, //Option> @@ -71,17 +66,17 @@ fn main() { rustc_interface::run_compiler(config, |compiler| { compiler.enter(|queries| { // Parse the program and print the syntax tree. - let parse = queries.parse().unwrap().take(); + let parse = queries.parse().unwrap().get_mut().clone(); println!("{parse:?}"); // Analyze the program and inspect the types of definitions. - queries.global_ctxt().unwrap().take().enter(|tcx| { + queries.global_ctxt().unwrap().enter(|tcx| { for id in tcx.hir().items() { let hir = tcx.hir(); let item = hir.item(id); match item.kind { rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn(_, _, _) => { let name = item.ident; - let ty = tcx.type_of(hir.local_def_id(item.hir_id())); + let ty = tcx.type_of(item.hir_id().owner.def_id); println!("{name:?}:\t{ty:?}") } _ => (), diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-getting-diagnostics.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-getting-diagnostics.rs index 49ee9ff44..5bc2312a2 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-getting-diagnostics.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-getting-diagnostics.rs @@ -1,10 +1,6 @@ #![feature(rustc_private)] -// NOTE: For the example to compile, you will need to first run the following: -// rustup component add rustc-dev llvm-tools-preview - -// version: rustc 1.68.0-nightly (935dc0721 2022-12-19) - +extern crate rustc_driver; extern crate rustc_error_codes; extern crate rustc_errors; extern crate rustc_hash; @@ -67,7 +63,6 @@ fn main() { }, crate_cfg: rustc_hash::FxHashSet::default(), crate_check_cfg: CheckCfg::default(), - input_path: None, output_dir: None, output_file: None, file_loader: None, @@ -80,7 +75,7 @@ fn main() { }; rustc_interface::run_compiler(config, |compiler| { compiler.enter(|queries| { - queries.global_ctxt().unwrap().take().enter(|tcx| { + queries.global_ctxt().unwrap().enter(|tcx| { // Run the analysis phase on the local crate to trigger the type error. let _ = tcx.analysis(()); }); diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index 07b09e9df..53f8df81a 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -1,11 +1,7 @@ #![feature(rustc_private)] -// NOTE: For the example to compile, you will need to first run the following: -// rustup component add rustc-dev llvm-tools-preview - -// version: rustc 1.68.0-nightly (935dc0721 2022-12-19) - extern crate rustc_ast_pretty; +extern crate rustc_driver; extern crate rustc_error_codes; extern crate rustc_errors; extern crate rustc_hash; @@ -45,7 +41,6 @@ fn main() { }, crate_cfg: rustc_hash::FxHashSet::default(), crate_check_cfg: CheckCfg::default(), - input_path: None, output_dir: None, output_file: None, file_loader: None, @@ -59,13 +54,12 @@ fn main() { rustc_interface::run_compiler(config, |compiler| { compiler.enter(|queries| { // TODO: add this to -Z unpretty - let ast_krate = queries.parse().unwrap().take(); + let ast_krate = queries.parse().unwrap().get_mut().clone(); for item in ast_krate.items { println!("{}", item_to_string(&item)); } - // Analyze the crate and inspect the types under the cursor. - queries.global_ctxt().unwrap().take().enter(|tcx| { + queries.global_ctxt().unwrap().enter(|tcx| { // Every compilation contains a single crate. let hir_krate = tcx.hir(); // Iterate over the top-level items in the crate, looking for the main function. @@ -78,7 +72,7 @@ fn main() { if let rustc_hir::StmtKind::Local(local) = block.stmts[0].kind { if let Some(expr) = local.init { let hir_id = expr.hir_id; // hir_id identifies the string "Hello, world!" - let def_id = tcx.hir().local_def_id(item.hir_id()); // def_id identifies the main function + let def_id = item.hir_id().owner.def_id; // def_id identifies the main function let ty = tcx.typeck(def_id).node_type(hir_id); println!("{expr:#?}: {ty:?}"); } diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 360265c0e..adc397fd8 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -8,7 +8,7 @@ # Building and debugging `rustc` -- [How to Build and Run the Compiler](./building/how-to-build-and-run.md) +- [How to build and run the compiler](./building/how-to-build-and-run.md) - [Prerequisites](./building/prerequisites.md) - [Suggested Workflows](./building/suggested.md) - [Distribution artifacts](./building/build-install-distribution-artifacts.md) @@ -25,7 +25,7 @@ - [Test headers](./tests/headers.md) - [Performance testing](./tests/perf.md) - [Crater](./tests/crater.md) -- [Debugging the Compiler](./compiler-debugging.md) +- [Debugging the compiler](./compiler-debugging.md) - [Using the tracing/logging instrumentation](./tracing.md) - [Profiling the compiler](./profiling.md) - [with the linux perf tool](./profiling/with_perf.md) @@ -57,7 +57,7 @@ # High-level Compiler Architecture - [Prologue](./part-2-intro.md) -- [Overview of the Compiler](./overview.md) +- [Overview of the compiler](./overview.md) - [The compiler source code](./compiler-src.md) - [Bootstrapping](./building/bootstrapping.md) - [Queries: demand-driven compilation](./query.md) @@ -75,7 +75,7 @@ - [Prologue](./part-3-intro.md) - [Command-line arguments](./cli.md) -- [The Rustc Driver and Interface](./rustc-driver.md) +- [rustc_driver and rustc_interface](./rustc-driver.md) - [Example: Type checking](./rustc-driver-interacting-with-the-ast.md) - [Example: Getting diagnostics](./rustc-driver-getting-diagnostics.md) - [Syntax and the AST](./syntax-intro.md) @@ -95,7 +95,7 @@ - [MIR construction](./mir/construction.md) - [MIR visitor and traversal](./mir/visitor.md) - [MIR passes: getting the MIR for a function](./mir/passes.md) -- [Identifiers in the Compiler](./identifiers.md) +- [Identifiers in the compiler](./identifiers.md) - [Closure expansion](./closure.md) - [Inline assembly](./asm.md) @@ -117,6 +117,10 @@ - [Lowering to logic](./traits/lowering-to-logic.md) - [Goals and clauses](./traits/goals-and-clauses.md) - [Canonical queries](./traits/canonical-queries.md) + - [Next-gen trait solving](./solve/trait-solving.md) + - [The solver](./solve/the-solver.md) + - [Canonicalization](./solve/canonicalization.md) + - [Coinduction](./solve/coinduction.md) - [Type checking](./type-checking.md) - [Method Lookup](./method-lookup.md) - [Variance](./variance.md) @@ -164,7 +168,7 @@ - [Profile-guided Optimization](./profile-guided-optimization.md) - [LLVM Source-Based Code Coverage](./llvm-coverage-instrumentation.md) - [Sanitizers Support](./sanitizers.md) -- [Debugging Support in the Rust Compiler](./debugging-support-in-rustc.md) +- [Debugging support in the Rust compiler](./debugging-support-in-rustc.md) --- diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md index 42306dc1c..019d31138 100644 --- a/src/doc/rustc-dev-guide/src/appendix/glossary.md +++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md @@ -23,7 +23,7 @@ Term | Meaning double pointer   | A pointer with additional metadata. See "fat pointer" for more. drop glue   | (internal) compiler-generated instructions that handle calling the destructors (`Drop`) for data types. DST   | Short for Dynamically-Sized Type, this is a type for which the compiler cannot statically know the size in memory (e.g. `str` or `[u8]`). Such types don't implement `Sized` and cannot be allocated on the stack. They can only occur as the last field in a struct. They can only be used behind a pointer (e.g. `&str` or `&[u8]`). -early-bound lifetime   | A lifetime region that is substituted at its definition site. Bound in an item's `Generics` and substituted using a `Substs`. Contrast with **late-bound lifetime**. ([see more](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.RegionKind.html#bound-regions)) +early-bound lifetime   | A lifetime region that is substituted at its definition site. Bound in an item's `Generics` and substituted using a `Substs`. Contrast with **late-bound lifetime**. ([see more](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.RegionKind.html#bound-regions)) empty type   | see "uninhabited type". fat pointer   | A two word value carrying the address of some value, along with some further information necessary to put the value to use. Rust includes two kinds of "fat pointers": references to slices, and trait objects. A reference to a slice carries the starting address of the slice and its length. A trait object carries a value's address and a pointer to the trait's implementation appropriate to that value. "Fat pointers" are also known as "wide pointers", and "double pointers". free variable   | A "free variable" is one that is not bound within an expression or term; see [the background chapter for more](./background.md#free-vs-bound) @@ -42,7 +42,7 @@ Term | Meaning IRLO   | `IRLO` or `irlo` is sometimes used as an abbreviation for [internals.rust-lang.org](https://internals.rust-lang.org). item   | A kind of "definition" in the language, such as a static, const, use statement, module, struct, etc. Concretely, this corresponds to the `Item` type. lang item   | Items that represent concepts intrinsic to the language itself, such as special built-in traits like `Sync` and `Send`; or traits representing operations such as `Add`; or functions that are called by the compiler. ([see more](https://doc.rust-lang.org/1.9.0/book/lang-items.html)) -late-bound lifetime   | A lifetime region that is substituted at its call site. Bound in a HRTB and substituted by specific functions in the compiler, such as `liberate_late_bound_regions`. Contrast with **early-bound lifetime**. ([see more](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.RegionKind.html#bound-regions)) +late-bound lifetime   | A lifetime region that is substituted at its call site. Bound in a HRTB and substituted by specific functions in the compiler, such as `liberate_late_bound_regions`. Contrast with **early-bound lifetime**. ([see more](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.RegionKind.html#bound-regions)) local crate   | The crate currently being compiled. This is in contrast to "upstream crates" which refer to dependencies of the local crate. LTO   | Short for Link-Time Optimizations, this is a set of optimizations offered by LLVM that occur just before the final binary is linked. These include optimizations like removing functions that are never used in the final program, for example. _ThinLTO_ is a variant of LTO that aims to be a bit more scalable and efficient, but possibly sacrifices some optimizations. You may also read issues in the Rust repo about "FatLTO", which is the loving nickname given to non-Thin LTO. LLVM documentation: [here][lto] and [here][thinlto]. [LLVM]   | (actually not an acronym :P) an open-source compiler backend. It accepts LLVM IR and outputs native binaries. Various languages (e.g. Rust) can then implement a compiler front-end that outputs LLVM IR and use LLVM to compile to all the platforms LLVM supports. diff --git a/src/doc/rustc-dev-guide/src/asm.md b/src/doc/rustc-dev-guide/src/asm.md index 3b6b5e093..b19f2ad46 100644 --- a/src/doc/rustc-dev-guide/src/asm.md +++ b/src/doc/rustc-dev-guide/src/asm.md @@ -277,7 +277,7 @@ pub enum InlineAsmOperand<'tcx> { } ``` -As part of HAIR lowering, `InOut` and `SplitInOut` operands are lowered to a split form with a +As part of THIR lowering, `InOut` and `SplitInOut` operands are lowered to a split form with a separate `in_value` and `out_place`. Semantically, the `InlineAsm` terminator is similar to the `Call` terminator except that it has diff --git a/src/doc/rustc-dev-guide/src/ast-validation.md b/src/doc/rustc-dev-guide/src/ast-validation.md index ff549ca62..f3ac35e8a 100644 --- a/src/doc/rustc-dev-guide/src/ast-validation.md +++ b/src/doc/rustc-dev-guide/src/ast-validation.md @@ -1,6 +1,36 @@ # AST Validation -AST validation is the process of checking various correctness properties about -the AST after macro expansion. + -**TODO**: write this chapter. [#656](https://github.com/rust-lang/rustc-dev-guide/issues/656) +## About + +_AST validation_ is a separate AST pass that visits each +item in the tree and performs simple checks. This pass +doesn't perform any complex analysis, type checking or +name resolution. + +Before performing any validation, the compiler first expands +the macros. Then this pass performs validations to check +that each AST item is in the correct state. And when this pass +is done, the compiler runs the crate resolution pass. + +## Validations + +Validations are defined in `AstValidator` type, which +itself is located in `rustc_ast_passes` crate. This +type implements various simple checks which emit errors +when certain language rules are broken. + +In addition, `AstValidator` implements `Visitor` trait +that defines how to visit AST items (which can be functions, +traits, enums, etc). + +For each item, visitor performs specific checks. For +example, when visiting a function declaration, +`AstValidator` checks that the function has: + +* no more than `u16::MAX` parameters; +* c-variadic functions are declared with at least one named argument; +* c-variadic argument goes the last in the declaration; +* documentation comments aren't applied to function parameters; +* and other validations. diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index ea50cd754..c5eb57e64 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -2,16 +2,21 @@ -As of Aug 2022, `rustc_codegen_ssa` provides an -abstract interface for all backends to implement, to allow other codegen -backends (e.g. [Cranelift]). +[`rustc_codegen_ssa`] +provides an abstract interface for all backends to implement, +namely LLVM, [Cranelift], and [GCC]. -[Cranelift]: https://github.com/bytecodealliance/wasmtime/tree/HEAD/cranelift +[Cranelift]: https://github.com/bjorn3/rustc_codegen_cranelift +[GCC]: https://github.com/rust-lang/rustc_codegen_gcc +[`rustc_codegen_ssa`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html -# Refactoring of `rustc_codegen_llvm` +Below is some background information on the refactoring that created this +abstract interface. + +## Refactoring of `rustc_codegen_llvm` by Denis Merigoux, October 23rd 2018 -## State of the code before the refactoring +### State of the code before the refactoring All the code related to the compilation of MIR into LLVM IR was contained inside the `rustc_codegen_llvm` crate. Here is the breakdown of the most @@ -51,7 +56,7 @@ While the LLVM-specific code will be left in `rustc_codegen_llvm`, all the new traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name suggestion by @eddyb). -## Generic types and structures +### Generic types and structures @irinagpopa started to parametrize the types of `rustc_codegen_llvm` by a generic `Value` type, implemented in LLVM by a reference `&'ll Value`. This @@ -103,7 +108,7 @@ of the backend and it makes more sense to leave their definition to the backend implementor than to allow just a narrow spot via a generic field for the backend's context. -## Traits and interface +### Traits and interface Because they have to be defined by the backend, `CodegenCx` and `Builder` will be the structures implementing all the traits defining the backend's interface. @@ -170,7 +175,7 @@ called. However, when implementing a Rust backend for `rustc`, these methods will need information from `CodegenCx`, hence the additional parameter (unused in the LLVM implementation of the trait). -## State of the code after the refactoring +### State of the code after the refactoring The traits offer an API which is very similar to the API of LLVM. This is not the best solution since LLVM has a very special way of doing things: when diff --git a/src/doc/rustc-dev-guide/src/backend/monomorph.md b/src/doc/rustc-dev-guide/src/backend/monomorph.md index 4a0b55f62..cbc56acfc 100644 --- a/src/doc/rustc-dev-guide/src/backend/monomorph.md +++ b/src/doc/rustc-dev-guide/src/backend/monomorph.md @@ -133,7 +133,7 @@ the substitutions with the identity parameter before being added to the set of collected items - thereby reducing the copies from two (`[u16, u32]` and `[u64, u32]`) to one (`[A, u32]`). -`unused_generic_params` will also invoked during code generation when the +`unused_generic_params` will also be invoked during code generation when the symbol name for `foo` is being computed for use in the callsites of `foo` (which have the regular substitutions present, otherwise there would be a symbol mismatch between the caller and the function). diff --git a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md index 38fbb2e44..a02011149 100644 --- a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md +++ b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md @@ -2,7 +2,7 @@ - + There is no formal policy about when to update LLVM or what it can be updated to, but a few guidelines are applied: @@ -16,9 +16,9 @@ but a few guidelines are applied: There are two reasons we would want to update LLVM: -* A bug could have been fixed! Often we find bugs in the compiler and fix - them upstream in LLVM. We'll want to pull fixes back to the compiler itself as - they're merged upstream. +* A bug could have been fixed! + Note that if we are the ones who fixed such a bug, + we prefer to upstream it, then pull it back for use by rustc. * LLVM itself may have a new release. @@ -172,9 +172,11 @@ so let's go through each in detail. Ideally the above instructions are pretty smooth, but here's some caveats to keep in mind while going through them: -* LLVM bugs are hard to find, don't hesitate to ask for help! Bisection is - definitely your friend here (yes LLVM takes forever to build, yet bisection is - still your friend) +* LLVM bugs are hard to find, don't hesitate to ask for help! + Bisection is definitely your friend here + (yes LLVM takes forever to build, yet bisection is still your friend). + Note that you can make use of [Dev Desktops], + which is an initiative to provide the contributors with remote access to powerful hardware. * If you've got general questions, [wg-llvm] can help you out. * Creating branches is a privileged operation on GitHub, so you'll need someone with write access to create the branches for you most likely. @@ -184,3 +186,4 @@ keep in mind while going through them: [llvm/llvm-project repository]: https://github.com/llvm/llvm-project [`llvm-wrapper`]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_llvm/llvm-wrapper [wg-llvm]: https://rust-lang.zulipchat.com/#narrow/stream/187780-t-compiler.2Fwg-llvm +[Dev Desktops]: https://forge.rust-lang.org/infra/docs/dev-desktop.html diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/lifetime_parameters.md b/src/doc/rustc-dev-guide/src/borrow_check/region_inference/lifetime_parameters.md index 2dbbc0c1e..bd8a07843 100644 --- a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/lifetime_parameters.md +++ b/src/doc/rustc-dev-guide/src/borrow_check/region_inference/lifetime_parameters.md @@ -43,8 +43,8 @@ only variant of [`ty::RegionKind`] that we use is the [`ReVar`] variant. These region variables are broken into two major categories, based on their index: -[`ty::RegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.RegionKind.html -[`ReVar`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.RegionKind.html#variant.ReVar +[`ty::RegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.RegionKind.html +[`ReVar`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.RegionKind.html#variant.ReVar - 0..N: universal regions -- the ones we are discussing here. In this case, the code must be correct with respect to any value of those diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping.md b/src/doc/rustc-dev-guide/src/building/bootstrapping.md index 543c68c2e..fe34cb500 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping.md @@ -1,8 +1,7 @@ -# Bootstrapping the Compiler +# Bootstrapping the compiler - [*Bootstrapping*][boot] is the process of using a compiler to compile itself. More accurately, it means using an older compiler to compile a newer version of the same compiler. @@ -16,6 +15,11 @@ version. This is exactly how `x.py` works: it downloads the current beta release of rustc, then uses it to compile the new compiler. +Note that this documentation mostly covers user-facing information. See +[bootstrap/README.md][bootstrap-internals] to read about bootstrap internals. + +[bootstrap-internals]: https://github.com/rust-lang/rust/blob/master/src/bootstrap/README.md + ## Stages of bootstrapping Compiling `rustc` is done in stages. Here's a diagram, adapted from Joshua Nelson's @@ -86,7 +90,7 @@ because one must first build the new compiler with an older compiler and then use that to build the new compiler with itself. For development, you usually only want the `stage1` compiler, which you can build with `./x.py build library`. -See [Building the Compiler](./how-to-build-and-run.html#building-the-compiler). +See [Building the compiler](./how-to-build-and-run.html#building-the-compiler). ### Stage 3 @@ -135,31 +139,6 @@ bootstrapping the compiler. [intrinsics]: ../appendix/glossary.md#intrinsic [ocaml-compiler]: https://github.com/rust-lang/rust/tree/ef75860a0a72f79f97216f8aaa5b388d98da6480/src/boot -## Contributing to bootstrap - -When you use the bootstrap system, you'll call it through `x.py`. -However, most of the code lives in `src/bootstrap`. -`bootstrap` has a difficult problem: it is written in Rust, but yet it is run -before the Rust compiler is built! To work around this, there are two -components of bootstrap: the main one written in rust, and `bootstrap.py`. -`bootstrap.py` is what gets run by `x.py`. It takes care of downloading the -`stage0` compiler, which will then build the bootstrap binary written in -Rust. - -Because there are two separate codebases behind `x.py`, they need to -be kept in sync. In particular, both `bootstrap.py` and the bootstrap binary -parse `config.toml` and read the same command line arguments. `bootstrap.py` -keeps these in sync by setting various environment variables, and the -programs sometimes have to add arguments that are explicitly ignored, to be -read by the other. - -### Adding a setting to config.toml - -This section is a work in progress. In the meantime, you can see an example -contribution [here][bootstrap-build]. - -[bootstrap-build]: https://github.com/rust-lang/rust/pull/71994 - ## Understanding stages of bootstrap ### Overview @@ -407,44 +386,51 @@ usually means something is quite wrong -- or you're trying to compile e.g. the unlikely case that you actually need to invoke rustc in such a situation, you can tell the bootstrap shim to print all env variables by adding `-vvv` to your `x.py` command. -### Directories and artifacts generated by `bootstrap` - -This is an incomplete reference for the outputs generated by bootstrap: - -| Stage 0 Action | Output | -|-----------------------------------------------------------|----------------------------------------------| -| `beta` extracted | `build/HOST/stage0` | -| `stage0` builds `bootstrap` | `build/bootstrap` | -| `stage0` builds `test`/`std` | `build/HOST/stage0-std/TARGET` | -| copy `stage0-std` (HOST only) | `build/HOST/stage0-sysroot/lib/rustlib/HOST` | -| `stage0` builds `rustc` with `stage0-sysroot` | `build/HOST/stage0-rustc/HOST` | -| copy `stage0-rustc` (except executable) | `build/HOST/stage0-sysroot/lib/rustlib/HOST` | -| build `llvm` | `build/HOST/llvm` | -| `stage0` builds `codegen` with `stage0-sysroot` | `build/HOST/stage0-codegen/HOST` | -| `stage0` builds `rustdoc`, `clippy`, `miri`, with `stage0-sysroot` | `build/HOST/stage0-tools/HOST` | - -`--stage=0` stops here. - -| Stage 1 Action | Output | -|-----------------------------------------------------|---------------------------------------| -| copy (uplift) `stage0-rustc` executable to `stage1` | `build/HOST/stage1/bin` | -| copy (uplift) `stage0-codegen` to `stage1` | `build/HOST/stage1/lib` | -| copy (uplift) `stage0-sysroot` to `stage1` | `build/HOST/stage1/lib` | -| `stage1` builds `test`/`std` | `build/HOST/stage1-std/TARGET` | -| copy `stage1-std` (HOST only) | `build/HOST/stage1/lib/rustlib/HOST` | -| `stage1` builds `rustc` | `build/HOST/stage1-rustc/HOST` | -| copy `stage1-rustc` (except executable) | `build/HOST/stage1/lib/rustlib/HOST` | -| `stage1` builds `codegen` | `build/HOST/stage1-codegen/HOST` | - -`--stage=1` stops here. - -| Stage 2 Action | Output | -|--------------------------------------------------------|-----------------------------------------------------------------| -| copy (uplift) `stage1-rustc` executable | `build/HOST/stage2/bin` | -| copy (uplift) `stage1-sysroot` | `build/HOST/stage2/lib and build/HOST/stage2/lib/rustlib/HOST` | -| `stage2` builds `test`/`std` (not HOST targets) | `build/HOST/stage2-std/TARGET` | -| copy `stage2-std` (not HOST targets) | `build/HOST/stage2/lib/rustlib/TARGET` | -| `stage2` builds `rustdoc`, `clippy`, `miri` | `build/HOST/stage2-tools/HOST` | -| copy `rustdoc` | `build/HOST/stage2/bin` | - -`--stage=2` stops here. +Finally, bootstrap makes use of the [cc-rs crate] which has [its own +method][env-vars] of configuring C compilers and C flags via environment +variables. + +[cc-rs crate]: https://github.com/rust-lang/cc-rs +[env-vars]: https://github.com/rust-lang/cc-rs#external-configuration-via-environment-variables + +## Clarification of build command's stdout + +In this part, we will investigate the build command's stdout in an action +(similar, but more detailed and complete documentation compare to topic above). +When you execute `x.py build --dry-run` command, the build output will be something +like the following: + +```text +Building stage0 library artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu) +Copying stage0 library from stage0 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu / x86_64-unknown-linux-gnu) +Building stage0 compiler artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu) +Copying stage0 rustc from stage0 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu / x86_64-unknown-linux-gnu) +Assembling stage1 compiler (x86_64-unknown-linux-gnu) +Building stage1 library artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu) +Copying stage1 library from stage1 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu / x86_64-unknown-linux-gnu) +Building stage1 tool rust-analyzer-proc-macro-srv (x86_64-unknown-linux-gnu) +Building rustdoc for stage1 (x86_64-unknown-linux-gnu) +``` + +### Building stage0 {std,compiler} artifacts + +These steps use the provided (downloaded, usually) compiler to compile the +local Rust source into libraries we can use. + +### Copying stage0 {std,rustc} + +This copies the library and compiler artifacts from Cargo into +`stage0-sysroot/lib/rustlib/{target-triple}/lib` + +### Assembling stage1 compiler + +This copies the libraries we built in "building stage0 ... artifacts" into +the stage1 compiler's lib directory. These are the host libraries that the +compiler itself uses to run. These aren't actually used by artifacts the new +compiler generates. This step also copies the rustc and rustdoc binaries we +generated into `build/$HOST/stage/bin`. + +The stage1/bin/rustc is a fully functional compiler, but it doesn't yet have +any libraries to link built binaries or libraries to. The next 3 steps will +provide those libraries for it; they are mostly equivalent to constructing +the stage1/bin compiler so we don't go through them individually. diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 24786c0c0..6651b3691 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -1,4 +1,4 @@ -# How to Build and Run the Compiler +# How to build and run the compiler The compiler is built using a tool called `x.py`. You will need to have Python installed to run it. @@ -52,7 +52,7 @@ If you have already built `rustc` and you change settings related to LLVM, then execute `rm -rf build` for subsequent configuration changes to take effect. Note that `./x.py clean` will not cause a rebuild of LLVM. -## Building the Compiler +## Building the compiler Note that building will require a relatively large amount of storage space. You may want to have upwards of 10 or 15 gigabytes available to build the compiler. @@ -122,16 +122,11 @@ you will likely need to build at some point; for example, if you want to run the entire test suite). ```bash -rustup toolchain link stage1 build//stage1 -rustup toolchain link stage2 build//stage2 +rustup toolchain link stage0 build/host/stage0-sysroot # beta compiler + stage0 std +rustup toolchain link stage1 build/host/stage1 +rustup toolchain link stage2 build/host/stage2 ``` -The `` would typically be one of the following: - -- Linux: `x86_64-unknown-linux-gnu` -- Mac: `x86_64-apple-darwin` or `aarch64-apple-darwin` -- Windows: `x86_64-pc-windows-msvc` - Now you can run the `rustc` you built with. If you run with `-vV`, you should see a version number ending in `-dev`, indicating a build from your local environment: diff --git a/src/doc/rustc-dev-guide/src/building/prerequisites.md b/src/doc/rustc-dev-guide/src/building/prerequisites.md index 8fee0a4b5..3dc2ea934 100644 --- a/src/doc/rustc-dev-guide/src/building/prerequisites.md +++ b/src/doc/rustc-dev-guide/src/building/prerequisites.md @@ -29,7 +29,7 @@ laptop. We suggest downloading LLVM from CI so you don't have to build it from s Like `cargo`, the build system will use as many cores as possible. Sometimes this can cause you to run low on memory. You can use `-j` to adjust the number -concurrent jobs. If a full build takes more than ~45 minutes to an hour, you +of concurrent jobs. If a full build takes more than ~45 minutes to an hour, you are probably spending most of the time swapping memory in and out; try using `-j1`. diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index f81daa5bc..2e2592094 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -19,51 +19,25 @@ You can also install the hook as a step of running `./x.py setup`! ## Configuring `rust-analyzer` for `rustc` +### Visual Studio Code + `rust-analyzer` can help you check and format your code whenever you save a file. By default, `rust-analyzer` runs the `cargo check` and `rustfmt` commands, but you can override these commands to use more adapted versions -of these tools when hacking on `rustc`. For example, for Visual Studio Code, -you can write: - -```JSON -{ - "rust-analyzer.checkOnSave.overrideCommand": [ - "python3", - "x.py", - "check", - "--json-output" - ], - "rust-analyzer.rustfmt.overrideCommand": [ - "./build/host/stage0/bin/rustfmt", - "--edition=2021" - ], - "rust-analyzer.procMacro.server": "./build/host/stage0/libexec/rust-analyzer-proc-macro-srv", - "rust-analyzer.procMacro.enable": true, - "rust-analyzer.cargo.buildScripts.enable": true, - "rust-analyzer.cargo.buildScripts.invocationLocation": "root", - "rust-analyzer.cargo.buildScripts.invocationStrategy": "once", - "rust-analyzer.cargo.buildScripts.overrideCommand": [ - "python3", - "x.py", - "check", - "--json-output" - ], - "rust-analyzer.cargo.sysroot": "./build/host/stage0-sysroot", - "rust-analyzer.rustc.source": "./Cargo.toml", -} -``` - -in your `.vscode/settings.json` file. This will ask `rust-analyzer` to use -`./x.py check` to check the sources, and the stage 0 rustfmt to format them. +of these tools when hacking on `rustc`. For example, `x.py setup` will prompt +you to create a `.vscode/settings.json` file which will configure Visual Studio code. +This will ask `rust-analyzer` to use `./x.py check` to check the sources, and the +stage 0 rustfmt to format them. +The recommended `rust-analyzer` settings live at [`src/etc/vscode_settings.json`]. If you have enough free disk space and you would like to be able to run `x.py` commands while rust-analyzer runs in the background, you can also add `--build-dir build-rust-analyzer` to the `overrideCommand` to avoid x.py locking. If you're running `coc.nvim`, you can use `:CocLocalConfig` to create a -`.vim/coc-settings.json` and enter the same settings as above, but replacing -`editor.formatOnSave: true,` with -`"coc.preferences.formatOnSaveFiletypes": ["rust"],`. +`.vim/coc-settings.json` and copy the settings from [`src/etc/vscode_settings.json`]. + +[`src/etc/vscode_settings.json`]: https://github.com/rust-lang/rust/blob/master/src/etc/vscode_settings.json If running `./x.py check` on save is inconvenient, in VS Code you can use a [Build Task] instead: @@ -87,6 +61,35 @@ Task] instead: [Build Task]: https://code.visualstudio.com/docs/editor/tasks + +### Neovim + +For Neovim users there are several options for configuring for rustc. The easiest way is by using +[neoconf.nvim](https://github.com/folke/neoconf.nvim/), which allows for project-local +configuration files with the native LSP. The steps for how to use it are below. Note that requires +Rust-Analyzer to already be configured with Neovim. Steps for this can be +[found here](https://rust-analyzer.github.io/manual.html#nvim-lsp). + +1. First install the plugin. This can be done by following the steps in the README. +2. Run `x.py setup`, which will have a prompt for it to create a `.vscode/settings.json` file. +`neoconf` is able to read and update Rust-Analyzer settings automatically when the project is +opened when this file is detected. + +If you're running `coc.nvim`, you can use `:CocLocalConfig` to create a +`.vim/coc-settings.json` and copy the settings from +[this file](https://github.com/rust-lang/rust/blob/master/src/etc/vscode_settings.json). + +Another way is without a plugin, and creating your own logic in your configuration. To do this you +must translate the JSON to Lua yourself. The translation is 1:1 and fairly straight-forward. It +must be put in the `["rust-analyzer"]` key of the setup table, which is +[shown here](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#rust_analyzer) + +If you would like to use the build task that is described above, you may either make your own +command in your config, or you can install a plugin such as +[overseer.nvim](https://github.com/stevearc/overseer.nvim) that can [read VSCode's `task.json` +files](https://github.com/stevearc/overseer.nvim/blob/master/doc/guides.md#vs-code-tasks), and +follow the same instructions as above. + ## Check, check, and check again When doing simple refactorings, it can be useful to run `./x.py check` @@ -175,6 +178,27 @@ You can also use `--keep-stage 1` when running tests. Something like this: - Initial test run: `./x.py test tests/ui` - Subsequent test run: `./x.py test tests/ui --keep-stage 1` +## Using incremental compilation + +You can further enable the `--incremental` flag to save additional +time in subsequent rebuilds: + +```bash +./x.py test tests/ui --incremental --test-args issue-1234 +``` + +If you don't want to include the flag with every command, you can +enable it in the `config.toml`: + +```toml +[rust] +incremental = true +``` + +Note that incremental compilation will use more disk space than usual. +If disk space is a concern for you, you might want to check the size +of the `build` directory from time to time. + ## Fine-tuning optimizations Setting `optimize = false` makes the compiler too slow for tests. However, to diff --git a/src/doc/rustc-dev-guide/src/closure.md b/src/doc/rustc-dev-guide/src/closure.md index 5746fd4de..e356f415d 100644 --- a/src/doc/rustc-dev-guide/src/closure.md +++ b/src/doc/rustc-dev-guide/src/closure.md @@ -155,7 +155,7 @@ This uses the stage1 compiler and enables `debug!` logging for the The other option is to step through the code using lldb or gdb. -1. `rust-lldb build/x86_64-apple-darwin/stage1/bin/rustc test.rs` +1. `rust-lldb build/host/stage1/bin/rustc test.rs` 2. In lldb: 1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file` 2. `r` // Run the program until it hits the breakpoint diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index eac9aeb6d..6052ea58a 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -1,5 +1,4 @@ # Debugging the compiler -[debugging]: #debugging @@ -185,7 +184,7 @@ stack backtrace: Cool, now I have a backtrace for the error! -## Getting the the error creation location +## Getting the error creation location `-Z track-diagnostics` can help figure out where errors are emitted. It uses `#[track_caller]` for this and prints its location alongside the error: @@ -341,3 +340,37 @@ error: aborting due to previous error ``` [`Layout`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/struct.Layout.html + + +## Configuring CodeLLDB for debugging `rustc` + +If you are using VSCode, and have edited your `config.toml` to request debugging +level 1 or 2 for the parts of the code you're interested in, then you should be +able to use the [CodeLLDB] extension in VSCode to debug it. + +Here is a sample `launch.json` file, being used to run a stage 1 compiler direct +from the directory where it is built (does not have to be "installed"): + +```javascript +// .vscode/launch.json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Launch", + "args": [], // array of string command-line arguments to pass to compiler + "program": "${workspaceFolder}/build/host/stage1/bin/rustc", + "windows": { // applicable if using windows + "program": "${workspaceFolder}/build/host/stage1/bin/rustc.exe" + }, + "cwd": "${workspaceFolder}", // current working directory at program start + "stopOnEntry": false, + "sourceLanguages": ["rust"] + } + ] + } +``` + +[CodeLLDB]: https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb diff --git a/src/doc/rustc-dev-guide/src/compiler-src.md b/src/doc/rustc-dev-guide/src/compiler-src.md index 1f55a9d09..9c7d0bb2e 100644 --- a/src/doc/rustc-dev-guide/src/compiler-src.md +++ b/src/doc/rustc-dev-guide/src/compiler-src.md @@ -27,8 +27,10 @@ The repository consists of three main directories: `proc_macro`, `test`), as well as the Rust runtime (`backtrace`, `rtstartup`, `lang_start`). +- `tests/` contains the compiler tests. + - `src/` contains the source code for rustdoc, clippy, cargo, the build system, - compiler tests, language docs, etc. + language docs, etc. ## Compiler diff --git a/src/doc/rustc-dev-guide/src/constants.md b/src/doc/rustc-dev-guide/src/constants.md index a33a283f3..91d21bd32 100644 --- a/src/doc/rustc-dev-guide/src/constants.md +++ b/src/doc/rustc-dev-guide/src/constants.md @@ -78,5 +78,5 @@ the constant doesn't use them in any way. This can cause [`ty::Const`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Const.html [`ty::ConstKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.ConstKind.html -[`ty::TyKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[`ty::TyKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html [pcg-unused-substs]: https://github.com/rust-lang/project-const-generics/issues/33 diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index bc39508a4..383660fc1 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -202,7 +202,7 @@ Periodically the changes made to subtree based dependencies need to be synchroni repository and the upstream tool repositories. Subtree synchronizations are typically handled by the respective tool maintainers. Other users -are welcome to submit synchronization PRs, however, in order to do so you you will need to modify +are welcome to submit synchronization PRs, however, in order to do so you will need to modify your local git installation and follow a very precise set of instructions. These instructions are documented, along with several useful tips and tricks, in the [syncing subtree changes][clippy-sync-docs] section in Clippy's Contributing guide. @@ -320,7 +320,7 @@ Here are those same steps in detail: merged because CI will be broken. You'll want to write a message on the PR referencing your change, and how the PR should be merged once your change makes it into a nightly. 5. Wait for your PR to merge. -6. Wait for a nightly +6. Wait for a nightly. 7. (optional) Help land your PR on the upstream repository now that your changes are in nightly. 8. (optional) Send a PR to rust-lang/rust updating the submodule. @@ -390,20 +390,20 @@ Just a few things to keep in mind: add a special annotation before specifying the date: ```md - Jul 2022 + Jan 2023 ``` Example: ```md - As of Jul 2022, the foo did the bar. + As of Jan 2023, the foo did the bar. ``` For cases where the date should not be part of the visible rendered output, use the following instead: ```md - + ``` - A link to a relevant WG, tracking issue, `rustc` rustdoc page, or similar, that may provide diff --git a/src/doc/rustc-dev-guide/src/crates-io.md b/src/doc/rustc-dev-guide/src/crates-io.md index f012c5bb5..403d61a81 100644 --- a/src/doc/rustc-dev-guide/src/crates-io.md +++ b/src/doc/rustc-dev-guide/src/crates-io.md @@ -11,10 +11,9 @@ you should avoid adding dependencies to the compiler for several reasons: - The dependency may have transitive dependencies that have one of the above problems. -As of Aug 2022, -there is no official policy for vetting new dependencies to the compiler. -Decisions are made on a case-by-case basis, -during code review. + +Note that there is no official policy for vetting new dependencies to the compiler. +Decisions are made on a case-by-case basis, during code review. ## Permitted dependencies diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index b50b7bd18..d32de068e 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -112,10 +112,11 @@ Here are a few examples: - Dead code: this is a lint. While the user probably doesn't want dead code in their crate, making this a hard error would make refactoring and development very painful. -- [safe_packed_borrows future compatibility warning][safe_packed_borrows]: - this is a silencable lint related to safety. It was judged that the making - this a hard (fixed) error would cause too much breakage, so instead a - warning is emitted that eventually will be turned into a hard error. +- [future-incompatible lints]: + these are silencable lints. + It was decided that making them fixed errors would cause too much breakage, + so warnings are instead emitted, + and will eventually be turned into fixed (hard) errors. Hard-coded warnings (those using the `span_warn` methods) should be avoided for normal code, preferring to use lints instead. Some cases, such as warnings @@ -124,7 +125,7 @@ with CLI flags, will require the use of hard-coded warnings. See the `deny` [lint level](#diagnostic-levels) below for guidelines when to use an error-level lint instead of a fixed error. -[safe_packed_borrows]: https://github.com/rust-lang/rust/issues/46043 +[future-incompatible lints]: #future-incompatible-lints ## Diagnostic output style guide @@ -637,12 +638,12 @@ broader meaning than what rustc exposes to users of the compiler. Inside rustc, future-incompatible lints are for signalling to the user that code they have written may not compile in the future. In general, future-incompatible code exists for two reasons: -* the user has written unsound code that the compiler mistakenly accepted. While +* The user has written unsound code that the compiler mistakenly accepted. While it is within Rust's backwards compatibility guarantees to fix the soundness hole (breaking the user's code), the lint is there to warn the user that this will happen in some upcoming version of rustc *regardless of which edition the code uses*. This is the meaning that rustc exclusively exposes to users as "future incompatible". -* the user has written code that will either no longer compiler *or* will change +* The user has written code that will either no longer compiler *or* will change meaning in an upcoming *edition*. These are often called "edition lints" and can be typically seen in the various "edition compatibility" lint groups (e.g., `rust_2021_compatibility`) that are used to lint against code that will break if the user updates the crate's edition. @@ -665,7 +666,7 @@ declare_lint! { Notice the `reason` field which describes why the future incompatible change is happening. This will change the diagnostic message the user receives as well as determine which lint groups the lint is added to. In the example above, the lint is an "edition lint" -(since it's "reason" is `EditionError`) signifying to the user that the use of anonymous +(since its "reason" is `EditionError`), signifying to the user that the use of anonymous parameters will no longer compile in Rust 2018 and beyond. Inside [LintStore::register_lints][fi-lint-groupings], lints with `future_incompatible` diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md index dcaba533e..78fdd032d 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md @@ -48,7 +48,7 @@ A new diagnostic item can be added with these two steps: For the naming conventions of diagnostic items, please refer to [*Naming Conventions*](#naming-conventions). -2. +2. Diagnostic items in code are accessed via symbols in [`rustc_span::symbol::sym`]. To add your newly-created diagnostic item, diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md index e26ba5f34..790d74dcf 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md @@ -23,7 +23,7 @@ pub struct FieldAlreadyDeclared { #[primary_span] #[label] pub span: Span, - #[label(hir_analysis_previous_decl_label)] + #[label(previous_decl_label)] pub prev_span: Span, } ``` @@ -82,7 +82,7 @@ these attributes can also take a value that is the attribute name to look for Other types have special behavior when used in a `Diagnostic` derive: -- Any attribute applied to an `Option` and will only emit a +- Any attribute applied to an `Option` will only emit a subdiagnostic if the option is `Some(..)`. - Any attribute applied to a `Vec` will be repeated for each element of the vector. diff --git a/src/doc/rustc-dev-guide/src/diagnostics/translation.md b/src/doc/rustc-dev-guide/src/diagnostics/translation.md index e36333039..e3ccec7d7 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/translation.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/translation.md @@ -222,7 +222,7 @@ returned by `Emitter::fluent_bundle`. This bundle is used preferentially when translating messages, the fallback bundle is only used if the primary bundle is missing a message or not provided. -As of June 2022, there are no locale bundles +As of Jan 2023, there are no locale bundles distributed with the compiler, but mechanisms are implemented for loading bundles. diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index 4e1f520ff..bc294d1b3 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -81,7 +81,7 @@ normally used for: building, testing, creating releases, formatting, etc. [bootstrapping]: ./building/bootstrapping.md -### Configuring the Compiler +### Configuring the compiler In the top level of the repo: @@ -114,6 +114,10 @@ serious development work. In particular, `./x.py build` and `./x.py test` provide many ways to compile or test a subset of the code, which can save a lot of time. +Also, note that `x.py` supports all kinds of path suffixes for `compiler`, `library`, +and `src/tools` directories. So, you can simply run `x.py test tidy` instead of +`x.py test src/tools/tidy`. Or, `x.py build std` instead of `x.py build library/std`. + [rust-analyzer]: ./building/suggested.html#configuring-rust-analyzer-for-rustc See the chapters on [building](./building/how-to-build-and-run.md), diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index cac3d6534..a426157a2 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -238,18 +238,22 @@ Changes not staged for commit: no changes added to commit (use "git add" and/or "git commit -a") ``` -These changes are not changes to files: they are changes to submodules (more on -this [later](#git-submodules)). To get rid of those, run `git submodule update` -(or run any `x.py` command, which will automatically update the submodules). -Note that, -as of Aug 2022, +These changes are not changes to files: they are changes to submodules (more on this +[later](#git-submodules)). To get rid of those, run `./x.py --help`, which will automatically update +the submodules. + +Some submodules are not actually needed; for example, `src/llvm-project` doesn't need to be checked +out if you're using `download-ci-llvm`. To avoid having to keep fetching its history, you can use +`git submodule deinit -f src/llvm-project`, which will also avoid it showing as modified again. + +Note that, as of Aug 2022, there is a [bug][#77620] if you use worktrees, submodules, and `x.py` in a commit hook. If you run into an error like the following, it's not anything you did wrong: ``` -error: failed to read `/home/joshua/rustc-worktree/src/tools/miri/cargo-miri/Cargo.toml` +error: failed to read `/home/jyn/rustc-worktree/src/tools/cargo/Cargo.toml` Caused by: No such file or directory (os error 2) @@ -260,8 +264,6 @@ There is a workaround in [the issue][#77620-workaround]. [#77620]: https://github.com/rust-lang/rust/issues/77620 [#77620-workaround]: https://github.com/rust-lang/rust/issues/77620#issuecomment-705228229 -(Note that as of Sept 2022 `miri` is a subtree and not a submodule.) - ## Rebasing and Conflicts When you edit your code locally, you are making changes to the version of diff --git a/src/doc/rustc-dev-guide/src/hir.md b/src/doc/rustc-dev-guide/src/hir.md index 8316f7ce3..160b47645 100644 --- a/src/doc/rustc-dev-guide/src/hir.md +++ b/src/doc/rustc-dev-guide/src/hir.md @@ -117,9 +117,9 @@ that `n` must be some HIR expression, you can do [Expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Expr.html Finally, you can use the HIR map to find the parents of nodes, via -calls like [`tcx.hir().get_parent_node(n)`][get_parent_node]. +calls like [`tcx.hir().get_parent(n)`][get_parent]. -[get_parent_node]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.get_parent_node +[get_parent]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.get_parent ## HIR Bodies diff --git a/src/doc/rustc-dev-guide/src/identifiers.md b/src/doc/rustc-dev-guide/src/identifiers.md index 18a3efefc..1b60b3b0b 100644 --- a/src/doc/rustc-dev-guide/src/identifiers.md +++ b/src/doc/rustc-dev-guide/src/identifiers.md @@ -1,4 +1,4 @@ -# Identifiers in the Compiler +# Identifiers in the compiler If you have read the few previous chapters, you now know that `rustc` uses many different intermediate representations to perform different kinds of analyses. diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing_new_features.md index 9147c1b41..9bd853080 100644 --- a/src/doc/rustc-dev-guide/src/implementing_new_features.md +++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md @@ -33,7 +33,7 @@ like this; for example, the compiler team recommends filing a Major Change Proposal ([MCP][mcp]) as a lightweight way to garner support and feedback without requiring full consensus. -[mcp]: compiler/mcp.md#public-facing-changes-require-rfcbot-fcp +[mcp]: https://forge.rust-lang.org/compiler/mcp.html#public-facing-changes-require-rfcbot-fcp You don't need to have the implementation fully ready for r+ to propose an FCP, but it is generally a good idea to have at least a proof diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index b29b32a62..8cd765011 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -222,13 +222,9 @@ properly-configured variables in LLVM IR, according to very specific details of the [_LLVM Coverage Mapping Format_][coverage-mapping-format] (Version 6).[^llvm-and-covmap-versions] -[^llvm-and-covmap-versions]: The Rust compiler (as of December 2021) -supports _LLVM Coverage Mapping Format_ Version 5 or 6. Version 5 -was introduced in _LLVM 12_, -which is (as of this writing) the minimum LLVM -version supported by the current version of Rust. Version 6 was introduced in -_LLVM 13_, which is currently the default LLVM version for Rust. The Rust -compiler will automatically use the most up-to-date coverage mapping format +[^llvm-and-covmap-versions]: +The Rust compiler (as of Feb 2023) supports _LLVM Coverage Mapping Format_ 6. +The Rust compiler will automatically use the most up-to-date coverage mapping format version that is compatible with the compiler's built-in version of LLVM. ```rust @@ -301,11 +297,10 @@ $ ./x.py test tests/run-make-fulldeps/coverage --bless ``` [mir-opt-test]: https://github.com/rust-lang/rust/blob/master/tests/mir-opt/instrument_coverage.rs -[coverage-test-samples]: https://github.com/rust-lang/rust/tree/master/tests/run-make-fulldeps/coverage -[`coverage-reports`]: https://github.com/rust-lang/rust/tree/master/tests/run-make-fulldeps/coverage-reports -[`coverage-spanview`]: https://github.com/rust-lang/rust/tree/master/tests/run-make-fulldeps/coverage-spanview +[coverage-test-samples]: https://github.com/rust-lang/rust/tree/master/tests/run-make/coverage +[`coverage-reports`]: https://github.com/rust-lang/rust/tree/master/tests/run-make/coverage-reports [spanview-debugging]: compiler-debugging.md#viewing-spanview-output -[`coverage-llvmir`]: https://github.com/rust-lang/rust/tree/master/tests/run-make-fulldeps/coverage-llvmir +[`coverage-llvmir`]: https://github.com/rust-lang/rust/tree/master/tests/run-make/coverage-llvmir ## Implementation Details of the `InstrumentCoverage` MIR Pass diff --git a/src/doc/rustc-dev-guide/src/mir/debugging.md b/src/doc/rustc-dev-guide/src/mir/debugging.md index 3da17785b..4e1d68938 100644 --- a/src/doc/rustc-dev-guide/src/mir/debugging.md +++ b/src/doc/rustc-dev-guide/src/mir/debugging.md @@ -14,10 +14,10 @@ elements obscured by overlaps, and tooltips to view the MIR statements). This flag takes an optional value: `statement` (the default), `terminator`, or `block`, to generate span highlights with different levels of granularity. -`-Z dump-mir=F` is a handy compiler options that will let you view the MIR for +`-Z dump-mir=F` is a handy compiler option that will let you view the MIR for each function at each stage of compilation. `-Z dump-mir` takes a **filter** `F` which allows you to control which functions and which passes you are -interesting in. For example: +interested in. For example: ```bash > rustc -Z dump-mir=foo ... diff --git a/src/doc/rustc-dev-guide/src/mir/visitor.md b/src/doc/rustc-dev-guide/src/mir/visitor.md index 505b700b3..17186fe75 100644 --- a/src/doc/rustc-dev-guide/src/mir/visitor.md +++ b/src/doc/rustc-dev-guide/src/mir/visitor.md @@ -37,10 +37,10 @@ code that will execute whenever a `foo` is found. If you want to recursively walk the contents of the `foo`, you then invoke the `super_foo` method. (NB. You never want to override `super_foo`.) -A very simple example of a visitor can be found in [`LocalUseCounter`]. -By implementing `visit_local` method, this visitor counts how many times each local is used. +A very simple example of a visitor can be found in [`LocalUseVisitor`]. +By implementing `visit_local` method, this visitor counts how many times each local is mutably used. -[`LocalUseCounter`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/simplify_try/struct.LocalUseCounter.html +[`LocalUseVisitor`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/const_debuginfo/struct.LocalUseVisitor.html ## Traversal diff --git a/src/doc/rustc-dev-guide/src/overview.md b/src/doc/rustc-dev-guide/src/overview.md index ca37f94b2..de06c742b 100644 --- a/src/doc/rustc-dev-guide/src/overview.md +++ b/src/doc/rustc-dev-guide/src/overview.md @@ -1,4 +1,4 @@ -# Overview of the Compiler +# Overview of the compiler diff --git a/src/doc/rustc-dev-guide/src/profiling/with_perf.md b/src/doc/rustc-dev-guide/src/profiling/with_perf.md index af30b4732..c9bd88ecd 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with_perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with_perf.md @@ -90,14 +90,15 @@ You can also use that same command to use cachegrind or other profiling tools. If you prefer to run things manually, that is also possible. You first need to find the source for the test you want. Sources for the tests -are found in [the `collector/benchmarks` directory][dir]. So let's go -into the directory of a specific test; we'll use `clap-rs` as an -example: +are found in [the `collector/compile-benchmarks` directory][compile-time dir] +and [the `collector/runtime-benchmarks` directory][runtime dir]. So let's +go into the directory of a specific test; we'll use `clap-rs` as an example: -[dir]: https://github.com/rust-lang/rustc-perf/tree/master/collector/benchmarks +[compile-time dir]: https://github.com/rust-lang/rustc-perf/tree/master/collector/compile-benchmarks +[runtime dir]: https://github.com/rust-lang/rustc-perf/tree/master/collector/runtime-benchmarks ```bash -cd collector/benchmarks/clap-rs +cd collector/compile-benchmarks/clap-3.1.6 ``` In this case, let's say we want to profile the `cargo check` diff --git a/src/doc/rustc-dev-guide/src/rustc-driver-getting-diagnostics.md b/src/doc/rustc-dev-guide/src/rustc-driver-getting-diagnostics.md index bb19ad9d3..3c2102a50 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver-getting-diagnostics.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver-getting-diagnostics.md @@ -7,34 +7,8 @@ To get diagnostics from the compiler, configure `rustc_interface::Config` to output diagnostic to a buffer, and run `TyCtxt.analysis`. The following was tested -with `nightly-2022-12-19` (See [here][example] -for the complete example): - -[example]: https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-driver-getting-diagnostics.rs +with `nightly-2023-02-13`: ```rust -let buffer = sync::Arc::new(sync::Mutex::new(Vec::new())); -let config = rustc_interface::Config { - opts: config::Options { - // Configure the compiler to emit diagnostics in compact JSON format. - error_format: config::ErrorOutputType::Json { - pretty: false, - json_rendered: rustc_errors::emitter::HumanReadableErrorType::Default( - rustc_errors::emitter::ColorConfig::Never, - ), - }, - /* other config */ - }, - /* other config */ -}; -rustc_interface::run_compiler(config, |compiler| { - compiler.enter(|queries| { - queries.global_ctxt().unwrap().take().enter(|tcx| { - // Run the analysis phase on the local crate to trigger the type error. - let _ = tcx.analysis(()); - }); - }); -}); -// Read buffered diagnostics. -let diagnostics = String::from_utf8(buffer.lock().unwrap().clone()).unwrap(); +{{#include ../examples/rustc-driver-getting-diagnostics.rs}} ``` diff --git a/src/doc/rustc-dev-guide/src/rustc-driver-interacting-with-the-ast.md b/src/doc/rustc-dev-guide/src/rustc-driver-interacting-with-the-ast.md index 5b495b4fe..d058a5838 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver-interacting-with-the-ast.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver-interacting-with-the-ast.md @@ -5,45 +5,8 @@ ## Getting the type of an expression To get the type of an expression, use the `global_ctxt` to get a `TyCtxt`. -The following was tested with `nightly-2022-12-19` -(see [here][example] for the complete example): - -[example]: https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-driver-interacting-with-the-ast.rs +The following was tested with `nightly-2023-02-13`: ```rust -let config = rustc_interface::Config { - input: config::Input::Str { - name: source_map::FileName::Custom("main.rs".to_string()), - input: "fn main() { let message = \"Hello, world!\"; println!(\"{}\", message); }" - .to_string(), - }, - /* other config */ -}; -rustc_interface::run_compiler(config, |compiler| { - compiler.enter(|queries| { - // Analyze the crate and inspect the types under the cursor. - queries.global_ctxt().unwrap().take().enter(|tcx| { - // Every compilation contains a single crate. - let hir_krate = tcx.hir(); - // Iterate over the top-level items in the crate, looking for the main function. - for id in hir_krate.items() { - let item = hir_krate.item(id); - // Use pattern-matching to find a specific node inside the main function. - if let rustc_hir::ItemKind::Fn(_, _, body_id) = item.kind { - let expr = &tcx.hir().body(body_id).value; - if let rustc_hir::ExprKind::Block(block, _) = expr.kind { - if let rustc_hir::StmtKind::Local(local) = block.stmts[0].kind { - if let Some(expr) = local.init { - let hir_id = expr.hir_id; // hir_id identifies the string "Hello, world!" - let def_id = tcx.hir().local_def_id(item.hir_id()); // def_id identifies the main function - let ty = tcx.typeck(def_id).node_type(hir_id); - println!("{:?}: {:?}", expr, ty); - } - } - } - } - } - }) - }); -}); +{{#include ../examples/rustc-driver-interacting-with-the-ast.rs}} ``` diff --git a/src/doc/rustc-dev-guide/src/rustc-driver.md b/src/doc/rustc-dev-guide/src/rustc-driver.md index cef50111d..192811da1 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver.md @@ -1,4 +1,4 @@ -# The Rustc Driver and Interface +# `rustc_driver` and `rustc_interface` The [`rustc_driver`] is essentially `rustc`'s `main()` function. It acts as the glue for running the various phases of the compiler in the correct order, diff --git a/src/doc/rustc-dev-guide/src/solve/canonicalization.md b/src/doc/rustc-dev-guide/src/solve/canonicalization.md new file mode 100644 index 000000000..a14be5216 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/solve/canonicalization.md @@ -0,0 +1,84 @@ +# Canonicalization + +Canonicalization is the process of *isolating* a value from its context and is necessary +for global caching of goals which include inference variables. + +The idea is that given the goals `u32: Trait` and `u32: Trait`, where `?x` and `?y` +are two different currently unconstrained inference variables, we should get the same result +for both goals. We can therefore prove *the canonical query* `exists u32: Trait` once +and reuse the result. + +Let's first go over the way canonical queries work and then dive into the specifics of +how canonicalization works. + +## A walkthrough of canonical queries + +To make this a bit easier, let's use the trait goal `u32: Trait` as an example with the +assumption that the only relevant impl is `impl Trait> for u32`. + +### Canonicalizing the input + +We start by *canonicalizing* the goal, replacing inference variables with existential and +placeholders with universal bound variables. This would result in the *canonical goal* +`exists u32: Trait`. + +We remember the original values of all bound variables in the original context. Here this would +map `T` back to `?x`. These original values are used later on when dealing with the query +response. + +We now call the canonical query with the canonical goal. + +### Instantiating the canonical goal inside of the query + +To actually try to prove the canonical goal we start by instantiating the bound variables with +inference variables and placeholders again. + +This happens inside of the query in a completely separate `InferCtxt`. Inside of the query we +now have a goal `u32: Trait`. We also remember which value we've used to instantiate the bound +variables in the canonical goal, which maps `T` to `?0`. + +We now compute the goal `u32: Trait` and figure out that this holds, but we've constrained +`?0` to `Vec`. We finally convert this result to something useful to the caller. + +### Canonicalizing the query response + +We have to return to the caller both whether the goal holds, and the inference constraints +from inside of the query. + +To return the inference results to the caller we canonicalize the mapping from bound variables +to the instantiated values in the query. This means that the query response is `Certainty::Yes` +and a mapping from `T` to `exists Vec`. + +### Instantiating the query response + +The caller now has to apply the constraints returned by the query. For this they first +instantiate the bound variables of the canonical response with inference variables and +placeholders again, so the mapping in the response is now from `T` to `Vec`. + +It now equates the original value of `T` (`?x`) with the value for `T` in the +response (`Vec`), which correctly constrains `?x` to `Vec`. + +## `ExternalConstraints` + +Computing a trait goal may not only constrain inference variables, it can also add region +obligations, e.g. given a goal `(): AOutlivesB<'a, 'b>` we would like to return the fact that +`'a: 'b` has to hold. + +This is done by not only returning the mapping from bound variables to the instantiated values +from the query but also extracting additional `ExternalConstraints` from the `InferCtxt` context +while building the response. + +## How exactly does canonicalization work + +TODO: link to code once the PR lands and elaborate + +- types and consts: infer to existentially bound var, placeholder to universally bound var, + considering universes +- generic parameters in the input get treated as placeholders in the root universe +- all regions in the input get all mapped to existentially bound vars and we "uniquify" them. + `&'a (): Trait<'a>` gets canonicalized to `exists<'0, '1> &'0 (): Trait<'1>`. We do not care + about their universes and simply put all regions into the highest universe of the input. +- once we collected all canonical vars we compress their universes, see comment in `finalize`. +- in the output everything in a universe of the caller gets put into the root universe and only + gets its correct universe when we unify the var values with the orig values of the caller +- we do not uniquify regions in the response and don't canonicalize `'static` \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/solve/coinduction.md b/src/doc/rustc-dev-guide/src/solve/coinduction.md new file mode 100644 index 000000000..c682e002d --- /dev/null +++ b/src/doc/rustc-dev-guide/src/solve/coinduction.md @@ -0,0 +1,250 @@ +# Coinduction + +The trait solver may use coinduction when proving goals. +Coinduction is fairly subtle so we're giving it its own chapter. + +## Coinduction and induction + +With induction, we recursively apply proofs until we end up with a finite proof tree. +Consider the example of `Vec>>: Debug` which results in the following tree. + +- `Vec>>: Debug` + - `Vec>: Debug` + - `Vec: Debug` + - `u32: Debug` + +This tree is finite. But not all goals we would want to hold have finite proof trees, +consider the following example: + +```rust +struct List { + value: T, + next: Option>>, +} +``` + +For `List: Send` to hold all its fields have to recursively implement `Send` as well. +This would result in the following proof tree: + +- `List: Send` + - `T: Send` + - `Option>>: Send` + - `Box>: Send` + - `List: Send` + - `T: Send` + - `Option>>: Send` + - `Box>: Send` + - ... + +This tree would be infinitely large which is exactly what coinduction is about. + +> To **inductively** prove a goal you need to provide a finite proof tree for it. +> To **coinductively** prove a goal the provided proof tree may be infinite. + +## Why is coinduction correct + +When checking whether some trait goals holds, we're asking "does there exist an `impl` +which satisfies this bound". Even if are infinite chains of nested goals, we still have a +unique `impl` which should be used. + +## How to implement coinduction + +While our implementation can not check for coinduction by trying to construct an infinite +tree as that would take infinite resources, it still makes sense to think of coinduction +from this perspective. + +As we cannot check for infinite trees, we instead search for patterns for which we know that +they would result in an infinite proof tree. The currently pattern we detect are (canonical) +cycles. If `T: Send` relies on `T: Send` then it's pretty clear that this will just go on forever. + +With cycles we have to be careful with caching. Because of canonicalization of regions and +inference variables encountering a cycle doesn't mean that we would get an infinite proof tree. +Looking at the following example: +```rust +trait Foo {} +struct Wrapper(T); + +impl Foo for Wrapper> +where + Wrapper: Foo +{} +``` +Proving `Wrapper: Foo` uses the impl `impl Foo for Wrapper>` which constrains +`?0` to `Wrapper` and then requires `Wrapper: Foo`. Due to canonicalization this would be +detected as a cycle. + +The idea to solve is to return a *provisional result* whenever we detect a cycle and repeatedly +retry goals until the *provisional result* is equal to the final result of that goal. We +start out by using `Yes` with no constraints as the result and then update it to the result of +the previous iteration whenever we have to rerun. + +TODO: elaborate here. We use the same approach as chalk for coinductive cycles. +Note that the treatment for inductive cycles currently differs by simply returning `Overflow`. +See [the relevant chapters][chalk] in the chalk book. + +[chalk]: https://rust-lang.github.io/chalk/book/recursive/inductive_cycles.html + + +## Future work + +We currently only consider auto-traits, `Sized`, and `WF`-goals to be coinductive. +In the future we pretty much intend for all goals to be coinductive. +Lets first elaborate on why allowing more coinductive proofs is even desirable. + +### Recursive data types already rely on coinduction... + +...they just tend to avoid them in the trait solver. + +```rust +enum List { + Nil, + Succ(T, Box>), +} + +impl Clone for List { + fn clone(&self) -> Self { + match self { + List::Nil => List::Nil, + List::Succ(head, tail) => List::Succ(head.clone(), tail.clone()), + } + } +} +``` + +We are using `tail.clone()` in this impl. For this we have to prove `Box>: Clone` +which requires `List: Clone` but that relies on the impl which we are currently checking. +By adding that requirement to the `where`-clauses of the impl, which is what we would +do with [perfect derive], we move that cycle into the trait solver and [get an error][ex1]. + +### Recursive data types + +We also need coinduction to reason about recursive types containing projections, +e.g. the following currently fails to compile even though it should be valid. +```rust +use std::borrow::Cow; +pub struct Foo<'a>(Cow<'a, [Foo<'a>]>); +``` +This issue has been known since at least 2015, see +[#23714](https://github.com/rust-lang/rust/issues/23714) if you want to know more. + +### Explicitly checked implied bounds + +When checking an impl, we assume that the types in the impl headers are well-formed. +This means that when using instantiating the impl we have to prove that's actually the case. +[#100051](https://github.com/rust-lang/rust/issues/100051) shows that this is not the case. +To fix this, we have to add `WF` predicates for the types in impl headers. +Without coinduction for all traits, this even breaks `core`. + +```rust +trait FromResidual {} +trait Try: FromResidual<::Residual> { + type Residual; +} + +struct Ready(T); +impl Try for Ready { + type Residual = Ready<()>; +} +impl FromResidual< as Try>::Residual> for Ready {} +``` + +When checking that the impl of `FromResidual` is well formed we get the following cycle: + +The impl is well formed if ` as Try>::Residual` and `Ready` are well formed. +- `wf( as Try>::Residual)` requires +- `Ready: Try`, which requires because of the super trait +- `Ready: FromResidual as Try>::Residual>`, **because of implied bounds on impl** +- `wf( as Try>::Residual)` :tada: **cycle** + +### Issues when extending coinduction to more goals + +There are some additional issues to keep in mind when extending coinduction. +The issues here are not relevant for the current solver. + +#### Implied super trait bounds + +Our trait system currently treats super traits, e.g. `trait Trait: SuperTrait`, +by 1) requiring that `SuperTrait` has to hold for all types which implement `Trait`, +and 2) assuming `SuperTrait` holds if `Trait` holds. + +Relying on 2) while proving 1) is unsound. This can only be observed in case of +coinductive cycles. Without cycles, whenever we rely on 2) we must have also +proven 1) without relying on 2) for the used impl of `Trait`. + +```rust +trait Trait: SuperTrait {} + +impl Trait for T {} + +// Keeping the current setup for coinduction +// would allow this compile. Uff :< +fn sup() {} +fn requires_trait() { sup::() } +fn generic() { requires_trait::() } +``` +This is not really fundamental to coinduction but rather an existing property +which is made unsound because of it. + +##### Possible solutions + +The easiest way to solve this would be to completely remove 2) and always elaborate +`T: Trait` to `T: Trait` and `T: SuperTrait` outside of the trait solver. +This would allow us to also remove 1), but as we still have to prove ordinary +`where`-bounds on traits, that's just additional work. + +While one could imagine ways to disable cyclic uses of 2) when checking 1), +at least the ideas of myself - @lcnr - are all far to complex to be reasonable. + +#### `normalizes_to` goals and progress + +A `normalizes_to` goal represents the requirement that `::Assoc` normalizes +to some `U`. This is achieved by defacto first normalizing `::Assoc` and then +equating the resulting type with `U`. It should be a mapping as each projection should normalize +to exactly one type. By simply allowing infinite proof trees, we would get the following behavior: + +```rust +trait Trait { + type Assoc; +} + +impl Trait for () { + type Assoc = <() as Trait>::Assoc; +} +``` + +If we now compute `normalizes_to(<() as Trait>::Assoc, Vec)`, we would resolve the impl +and get the associated type `<() as Trait>::Assoc`. We then equate that with the expected type, +causing us to check `normalizes_to(<() as Trait>::Assoc, Vec)` again. +This just goes on forever, resulting in an infinite proof tree. + +This means that `<() as Trait>::Assoc` would be equal to any other type which is unsound. + +##### How to solve this + +**WARNING: THIS IS SUBTLE AND MIGHT BE WRONG** + +Unlike trait goals, `normalizes_to` has to be *productive*[^1]. A `normalizes_to` goal +is productive once the projection normalizes to a rigid type constructor, +so `<() as Trait>::Assoc` normalizing to `Vec<<() as Trait>::Assoc>` would be productive. + +A `normalizes_to` goal has two kinds of nested goals. Nested requirements needed to actually +normalize the projection, and the equality between the normalized projection and the +expected type. Only the equality has to be productive. A branch in the proof tree is productive +if it is either finite, or contains at least one `normalizes_to` where the alias is resolved +to a rigid type constructor. + +Alternatively, we could simply always treat the equate branch of `normalizes_to` as inductive. +Any cycles should result in infinite types, which aren't supported anyways and would only +result in overflow when deeply normalizing for codegen. + +experimentation and examples: https://hackmd.io/-8p0AHnzSq2VAE6HE_wX-w?view + +Another attempt at a summary. +- in projection eq, we must make progress with constraining the rhs +- a cycle is only ok if while equating we have a rigid ty on the lhs after norm at least once +- cycles outside of the recursive `eq` call of `normalizes_to` are always fine + +[^1]: related: https://coq.inria.fr/refman/language/core/coinductive.html#top-level-definitions-of-corecursive-functions + +[perfect derive]: https://smallcultfollowing.com/babysteps/blog/2022/04/12/implied-bounds-and-perfect-derive +[ex1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0a9c3830b93a2380e6978d6328df8f72 diff --git a/src/doc/rustc-dev-guide/src/solve/the-solver.md b/src/doc/rustc-dev-guide/src/solve/the-solver.md new file mode 100644 index 000000000..61e6cad1c --- /dev/null +++ b/src/doc/rustc-dev-guide/src/solve/the-solver.md @@ -0,0 +1,17 @@ +# The solver + +Also consider reading the documentation for [the recursive solver in chalk][chalk] +as it is very similar to this implementation and also talks about limitations of this +approach. + +[chalk]: https://rust-lang.github.io/chalk/book/recursive.html + +The basic structure of the solver is a pure function +`fn evaluate_goal(goal: Goal<'tcx>) -> Response`. +While the actual solver is not fully pure to deal with overflow and cycles, we are +going to defer that for now. + +To deal with inference variables and to improve caching, we use +[canonicalization](./canonicalization.md). + +TODO: write the remaining code for this as well. diff --git a/src/doc/rustc-dev-guide/src/solve/trait-solving.md b/src/doc/rustc-dev-guide/src/solve/trait-solving.md new file mode 100644 index 000000000..71f6581c2 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/solve/trait-solving.md @@ -0,0 +1,114 @@ +# Trait solving (new) + +This chapter describes how trait solving works with the new WIP solver located in +[`rustc_trait_selection/solve`][solve]. Feel free to also look at the docs for +[the current solver](../traits/resolution.md) and [the chalk solver](../traits/chalk.md) +can be found separately. + +## Core concepts + +The goal of the trait system is to check whether a given trait bound is satisfied. +Most notably when typechecking the body of - potentially generic - functions. +For example: + +```rust +fn uses_vec_clone(x: Vec) -> (Vec, Vec) { + (x.clone(), x) +} +``` +Here the call to `x.clone()` requires us to prove that `Vec` implements `Clone` given +the assumption that `T: Clone` is true. We can assume `T: Clone` as that will be proven by +callers of this function. + +The concept of "prove the `Vec: Clone` with the assumption `T: Clone`" is called a [`Goal`]. +Both `Vec: Clone` and `T: Clone` are represented using [`Predicate`]. There are other +predicates, most notably equality bounds on associated items: ` as IntoIterator>::Item == T`. +See the `PredicateKind` enum for an exhaustive list. A `Goal` is represented as the `predicate` we +have to prove and the `param_env` in which this predicate has to hold. + +We prove goals by checking whether each possible [`Candidate`] applies for the given goal by +recursively proving its nested goals. For a list of possible candidates with examples, look at +[`CandidateSource`]. The most important candidates are `Impl` candidates, i.e. trait implementations +written by the user, and `ParamEnv` candidates, i.e. assumptions in our current environment. + +Looking at the above example, to prove `Vec: Clone` we first use +`impl Clone for Vec`. To use this impl we have to prove the nested +goal that `T: Clone` holds. This can use the assumption `T: Clone` from the `ParamEnv` +which does not have any nested goals. Therefore `Vec: Clone` holds. + +The trait solver can either return success, ambiguity or an error as a [`CanonicalResponse`]. +For success and ambiguity it also returns constraints inference and region constraints. + +## Requirements + +Before we dive into the new solver lets first take the time to go through all of our requirements +on the trait system. We can then use these to guide our design later on. + +TODO: elaborate on these rules and get more precise about their meaning. +Also add issues where each of these rules have been broken in the past +(or still are). + +### 1. The trait solver has to be *sound* + +This means that we must never return *success* for goals for which no `impl` exists. That would +simply be unsound by assuming a trait is implemented even though it is not. When using predicates +from the `where`-bounds, the `impl` will be proved by the user of the item. + +### 2. If type checker solves generic goal concrete instantiations of that goal have the same result + +Pretty much: If we successfully typecheck a generic function concrete instantiations +of that function should also typeck. We should not get errors post-monomorphization. +We can however get overflow as in the following snippet: + +```rust +fn foo(x: ) +``` + +### 3. Trait goals in empty environments are proven by a unique impl + +If a trait goal holds with an empty environment, there is a unique `impl`, +either user-defined or builtin, which is used to prove that goal. + +This is necessary for codegen to select a unique method. +An exception here are *marker traits* which are allowed to overlap. + +### 4. Normalization in empty environments results in a unique type + +Normalization for alias types/consts has a unique result. Otherwise we can easily implement +transmute in safe code. Given the following function, we have to make sure that the input and +output types always get normalized to the same concrete type. +```rust +fn foo( + x: ::Assoc +) -> ::Assoc { + x +} +``` + +### 5. During coherence trait solving has to be complete + +During coherence we never return *error* for goals which can be proven. This allows overlapping +impls which would break rule 3. + +### 6. Trait solving must be (free) lifetime agnostic + +Trait solving during codegen should have the same result as during typeck. As we erase +all free regions during codegen we must not rely on them during typeck. A noteworthy example +is special behavior for `'static`. + +### 7. Removing ambiguity makes strictly more things compile + +We *should* not rely on ambiguity for things to compile. +Not doing that will cause future improvements to be breaking changes. + +### 8. semantic equality implies structural equality + +Two types being equal in the type system must mean that they have the same `TypeId`. + + +[solve]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/solve/index.html +[`Goal`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/solve/struct.Goal.html +[`Predicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Predicate.html +[`Candidate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/solve/assembly/struct.Candidate.html +[`CandidateSource`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/solve/assembly/enum.CandidateSource.html +[`CanonicalResponse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/solve/type.CanonicalResponse.html diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 9f0c56dc4..5fc6ba809 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -24,6 +24,11 @@ See the [Adding new tests](adding.md) chapter for a tutorial on creating a new test, and the [Running tests](running.md) chapter on how to run the test suite. +Compiletest itself tries to avoid running tests when the artifacts +that are involved (mainly the compiler) haven't changed. You can use +`x test --test-args --force-rerun` to rerun a test even when none of the +inputs have changed. + ## Test suites All of the tests are in the [`tests`] directory. diff --git a/src/doc/rustc-dev-guide/src/tests/running.md b/src/doc/rustc-dev-guide/src/tests/running.md index 96c869109..0a3de6f8b 100644 --- a/src/doc/rustc-dev-guide/src/tests/running.md +++ b/src/doc/rustc-dev-guide/src/tests/running.md @@ -175,27 +175,6 @@ By passing `--pass $mode`, you can reduce the testing time. For each mode, please see [Controlling pass/fail expectations](ui.md#controlling-passfail-expectations). -## Using incremental compilation - -You can further enable the `--incremental` flag to save additional -time in subsequent rebuilds: - -```bash -./x.py test tests/ui --incremental --test-args issue-1234 -``` - -If you don't want to include the flag with every command, you can -enable it in the `config.toml`: - -```toml -[rust] -incremental = true -``` - -Note that incremental compilation will use more disk space than usual. -If disk space is a concern for you, you might want to check the size -of the `build` directory from time to time. - ## Running tests with different "compare modes" UI tests may have different output depending on certain "modes" that diff --git a/src/doc/rustc-dev-guide/src/traits/resolution.md b/src/doc/rustc-dev-guide/src/traits/resolution.md index 9cf753b19..639ebbdec 100644 --- a/src/doc/rustc-dev-guide/src/traits/resolution.md +++ b/src/doc/rustc-dev-guide/src/traits/resolution.md @@ -163,7 +163,7 @@ impl Get for Box { } ``` -What happens when we invoke `get_it(&Box::new(1_u16))`, for example? In this +What happens when we invoke `get(&Box::new(1_u16))`, for example? In this case, the `Self` type is `Box` – that unifies with both impls, because the first applies to all types `T`, and the second to all `Box`. In order for this to be unambiguous, the compiler does a *winnowing* @@ -248,7 +248,7 @@ because we know that each resolution will resolve to a particular impl. One interesting twist has to do with nested obligations. In general, in codegen, we only to figure out which candidate applies, we do not care about nested obligations, -as these are already assumed to be true. Nonetheless, we *do* currently do fulfill all of them. +as these are already assumed to be true. Nonetheless, we *do* currently fulfill all of them. That is because it can sometimes inform the results of type inference. That is, we do not have the full substitutions in terms of the type variables of the impl available to us, so we must run trait selection to figure diff --git a/src/doc/rustc-dev-guide/src/ty.md b/src/doc/rustc-dev-guide/src/ty.md index 90e17e85f..4a6cdb7c7 100644 --- a/src/doc/rustc-dev-guide/src/ty.md +++ b/src/doc/rustc-dev-guide/src/ty.md @@ -133,7 +133,7 @@ the `ty::Ty` to be a thin pointer-like type. This allows us to do cheap comparisons for equality, along with the other benefits of interning. -[tykind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[tykind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html ## Allocating and working with types @@ -148,18 +148,62 @@ These methods all return a `Ty<'tcx>` – note that the lifetime you get back is arena that this `tcx` has access to. Types are always canonicalized and interned (so we never allocate exactly the same type twice). -> N.B. -> Because types are interned, it is possible to compare them for equality efficiently using `==` -> – however, this is almost never what you want to do unless you happen to be hashing and looking -> for duplicates. This is because often in Rust there are multiple ways to represent the same type, -> particularly once inference is involved. If you are going to be testing for type equality, you -> probably need to start looking into the inference code to do it right. - You can also find various common types in the `tcx` itself by accessing its fields: `tcx.types.bool`, `tcx.types.char`, etc. (See [`CommonTypes`] for more.) [`CommonTypes`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.CommonTypes.html + +## Comparing types + +Because types are interned, it is possible to compare them for equality efficiently using `==` +– however, this is almost never what you want to do unless you happen to be hashing and looking +for duplicates. This is because often in Rust there are multiple ways to represent the same type, +particularly once inference is involved. + +For example, the type `{integer}` (`ty::Infer(ty::IntVar(..))` an integer inference variable, +the type of an integer literal like `0`) and `u8` (`ty::UInt(..)`) should often be treated as +equal when testing whether they can be assigned to each other (which is a common operation in +diagnostics code). `==` on them will return `false` though, since they are different types. + +The simplest way to compare two types correctly requires an inference context (`infcx`). +If you have one, you can use `infcx.can_eq(param_env, ty1, ty2)` +to check whether the types can be made equal. +This is typically what you want to check during diagnostics, which is concerned with questions such +as whether two types can be assigned to each other, not whether they're represented identically in +the compiler's type-checking layer. + +When working with an inference context, you have to be careful to ensure that potential inference +variables inside the types actually belong to that inference context. If you are in a function +that has access to an inference context already, this should be the case. Specifically, this is the +case during HIR type checking or MIR borrow checking. + +Another consideration is normalization. Two types may actually be the same, but one is behind an +associated type. To compare them correctly, you have to normalize the types first. This is +primarily a concern during HIR type checking and with all types from a `TyCtxt` query +(for example from `tcx.type_of()`). + +When a `FnCtxt` or an `ObligationCtxt` is available during type checking, `.normalize(ty)` +should be used on them to normalize the type. After type checking, diagnostics code can use +`tcx.normalize_erasing_regions(ty)`. + +There are also cases where using `==` on `Ty` is fine. This is for example the case in late lints +or after monomorphization, since type checking has been completed, meaning all inference variables +are resolved and all regions have been erased. In these cases, if you know that inference variables +or normalization won't be a concern, `#[allow]` or `#[expect]`ing the lint is recommended. + +When diagnostics code does not have access to an inference context, it should be threaded through +the function calls if one is available in some place (like during type checking). + +If no inference context is available at all, then one can be created as described in +[type-inference]. But this is only useful when the involved types (for example, if +they came from a query like `tcx.type_of()`) are actually substituted with fresh +inference variables using [`fresh_substs_for_item`]. This can be used to answer questions +like "can `Vec` for any `T` be unified with `Vec`?". + +[type-inference]: ./type-inference.md#creating-an-inference-context +[`fresh_substs_for_item`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.fresh_substs_for_item + ## `ty::TyKind` Variants Note: `TyKind` is **NOT** the functional programming concept of *Kind*. @@ -207,16 +251,16 @@ There are many variants on the `TyKind` enum, which you can see by looking at it - [**And many more**...][kindvars] [wikiadt]: https://en.wikipedia.org/wiki/Algebraic_data_type -[kindadt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Adt -[kindforeign]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Foreign -[kindstr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Str -[kindslice]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Slice -[kindarray]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Array -[kindrawptr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.RawPtr -[kindref]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Ref -[kindparam]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Param -[kinderr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Error -[kindvars]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variants +[kindadt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Adt +[kindforeign]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Foreign +[kindstr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Str +[kindslice]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Slice +[kindarray]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Array +[kindrawptr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.RawPtr +[kindref]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Ref +[kindparam]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Param +[kinderr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Error +[kindvars]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variants ## Import conventions diff --git a/src/doc/rustc-dev-guide/src/type-checking.md b/src/doc/rustc-dev-guide/src/type-checking.md index 08f15e30b..b60694201 100644 --- a/src/doc/rustc-dev-guide/src/type-checking.md +++ b/src/doc/rustc-dev-guide/src/type-checking.md @@ -1,10 +1,12 @@ # Type checking -The [`rustc_typeck`][typeck] crate contains the source for "type collection" -and "type checking", as well as a few other bits of related functionality. (It -draws heavily on the [type inference] and [trait solving].) +The [`hir_analysis`] crate contains the source for "type collection" as well +as a bunch of related functionality. +Checking the bodies of functions is implemented in the [`hir_typeck`] crate. +These crates draw heavily on the [type inference] and [trait solving]. -[typeck]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/index.html +[`hir_analysis`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/index.html +[`hir_typeck`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/index.html [type inference]: ./type-inference.md [trait solving]: ./traits/resolution.md diff --git a/src/doc/rustc-dev-guide/src/type-inference.md b/src/doc/rustc-dev-guide/src/type-inference.md index ca88c1686..2bafeb247 100644 --- a/src/doc/rustc-dev-guide/src/type-inference.md +++ b/src/doc/rustc-dev-guide/src/type-inference.md @@ -36,18 +36,16 @@ signature, such as the `'a` in `for<'a> fn(&'a u32)`. A region is ## Creating an inference context -You create and "enter" an inference context by doing something like +You create an inference context by doing something like the following: ```rust,ignore -tcx.infer_ctxt().enter(|infcx| { - // Use the inference context `infcx` here. -}) +let infcx = tcx.infer_ctxt().build(); +// Use the inference context `infcx` here. ``` -Within the closure, -`infcx` has the type `InferCtxt<'a, 'tcx>` for some fresh `'a`, -while `'tcx` is the same as outside the inference context. +`infcx` has the type `InferCtxt<'tcx>`, the same `'tcx` lifetime as on +the `tcx` it was built from. The `tcx.infer_ctxt` method actually returns a builder, which means there are some kinds of configuration you can do before the `infcx` is diff --git a/src/doc/rustc/book.toml b/src/doc/rustc/book.toml index 372350403..cea6033ed 100644 --- a/src/doc/rustc/book.toml +++ b/src/doc/rustc/book.toml @@ -5,3 +5,4 @@ title = "The rustc book" [output.html] git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc" +edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/rustc/{path}" diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 7e355b7fc..c7f120daf 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -49,10 +49,10 @@ Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard) platform security feature. This flag is currently ignored for non-Windows targets. It takes one of the following values: -* `y`, `yes`, `on`, `checks`, or no value: enable Control Flow Guard. +* `y`, `yes`, `on`, `true`, `checks`, or no value: enable Control Flow Guard. * `nochecks`: emit Control Flow Guard metadata without runtime enforcement checks (this should only be used for testing purposes as it does not provide security enforcement). -* `n`, `no`, `off`: do not enable Control Flow Guard (the default). +* `n`, `no`, `off`, `false`: do not enable Control Flow Guard (the default). ## debug-assertions @@ -60,8 +60,8 @@ This flag lets you turn `cfg(debug_assertions)` [conditional compilation](../../reference/conditional-compilation.md#debug_assertions) on or off. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable debug-assertions. -* `n`, `no`, or `off`: disable debug-assertions. +* `y`, `yes`, `on`, `true`, or no value: enable debug-assertions. +* `n`, `no`, `off` or `false`: disable debug-assertions. If not specified, debug assertions are automatically enabled only if the [opt-level](#opt-level) is 0. @@ -82,8 +82,8 @@ Note: The [`-g` flag][option-g-debug] is an alias for `-C debuginfo=2`. This flag controls whether or not the linker includes its default libraries. It takes one of the following values: -* `y`, `yes`, `on`, or no value: include default libraries (the default). -* `n`, `no`, or `off`: exclude default libraries. +* `y`, `yes`, `on`, `true` or no value: include default libraries (the default). +* `n`, `no`, `off` or `false`: exclude default libraries. For example, for gcc flavor linkers, this issues the `-nodefaultlibs` flag to the linker. @@ -93,8 +93,8 @@ the linker. This flag controls whether or not the compiler embeds LLVM bitcode into object files. It takes one of the following values: -* `y`, `yes`, `on`, or no value: put bitcode in rlibs (the default). -* `n`, `no`, or `off`: omit bitcode from rlibs. +* `y`, `yes`, `on`, `true` or no value: put bitcode in rlibs (the default). +* `n`, `no`, `off` or `false`: omit bitcode from rlibs. LLVM bitcode is required when rustc is performing link-time optimization (LTO). It is also required on some targets like iOS ones where vendors look for LLVM @@ -135,8 +135,8 @@ flag][option-emit] for more information. This flag forces the use of frame pointers. It takes one of the following values: -* `y`, `yes`, `on`, or no value: force-enable frame pointers. -* `n`, `no`, or `off`: do not force-enable frame pointers. This does +* `y`, `yes`, `on`, `true` or no value: force-enable frame pointers. +* `n`, `no`, `off` or `false`: do not force-enable frame pointers. This does not necessarily mean frame pointers will be removed. The default behaviour, if frame pointers are not force-enabled, depends on the @@ -147,8 +147,8 @@ target. This flag forces the generation of unwind tables. It takes one of the following values: -* `y`, `yes`, `on`, or no value: Unwind tables are forced to be generated. -* `n`, `no`, or `off`: Unwind tables are not forced to be generated. If unwind +* `y`, `yes`, `on`, `true` or no value: Unwind tables are forced to be generated. +* `n`, `no`, `off` or `false`: Unwind tables are not forced to be generated. If unwind tables are required by the target an error will be emitted. The default if not specified depends on the target. @@ -202,8 +202,8 @@ options should be separated by spaces. This flag controls whether the linker will keep dead code. It takes one of the following values: -* `y`, `yes`, `on`, or no value: keep dead code. -* `n`, `no`, or `off`: remove dead code (the default). +* `y`, `yes`, `on`, `true` or no value: keep dead code. +* `n`, `no`, `off` or `false`: remove dead code (the default). An example of when this flag might be useful is when trying to construct code coverage metrics. @@ -215,8 +215,8 @@ linker will use libraries and objects shipped with Rust instead or those in the It takes one of the following values: * no value: rustc will use heuristic to disable self-contained mode if system has necessary tools. -* `y`, `yes`, `on`: use only libraries/objects shipped with Rust. -* `n`, `no`, or `off`: rely on the user or the linker to provide non-Rust libraries/objects. +* `y`, `yes`, `on`, `true`: use only libraries/objects shipped with Rust. +* `n`, `no`, `off` or `false`: rely on the user or the linker to provide non-Rust libraries/objects. This allows overriding cases when detection fails or user wants to use shipped libraries. @@ -261,8 +261,8 @@ This flag defers LTO optimizations to the linker. See [linker-plugin-LTO](../linker-plugin-lto.md) for more details. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable linker plugin LTO. -* `n`, `no`, or `off`: disable linker plugin LTO (the default). +* `y`, `yes`, `on`, `true` or no value: enable linker plugin LTO. +* `n`, `no`, `off` or `false`: disable linker plugin LTO (the default). * A path to the linker plugin. More specifically this flag will cause the compiler to replace its typical @@ -292,9 +292,9 @@ optimizations](https://llvm.org/docs/LinkTimeOptimization.html) to produce better optimized code, using whole-program analysis, at the cost of longer linking time. It takes one of the following values: -* `y`, `yes`, `on`, `fat`, or no value: perform "fat" LTO which attempts to +* `y`, `yes`, `on`, `true`, `fat`, or no value: perform "fat" LTO which attempts to perform optimizations across all crates within the dependency graph. -* `n`, `no`, `off`: disables LTO. +* `n`, `no`, `off`, `false`: disables LTO. * `thin`: perform ["thin" LTO](http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html). This is similar to "fat", but takes substantially less time to run while @@ -333,8 +333,8 @@ This flag allows you to disable [the red zone](https://en.wikipedia.org/wiki/Red_zone_\(computing\)). It takes one of the following values: -* `y`, `yes`, `on`, or no value: disable the red zone. -* `n`, `no`, or `off`: enable the red zone. +* `y`, `yes`, `on`, `true` or no value: disable the red zone. +* `n`, `no`, `off` or `false`: enable the red zone. The default behaviour, if the flag is not specified, depends on the target. @@ -376,8 +376,8 @@ overflow](../../reference/expressions/operator-expr.md#overflow). When overflow-checks are enabled, a panic will occur on overflow. This flag takes one of the following values: -* `y`, `yes`, `on`, or no value: enable overflow checks. -* `n`, `no`, or `off`: disable overflow checks. +* `y`, `yes`, `on`, `true` or no value: enable overflow checks. +* `n`, `no`, `off` or `false`: disable overflow checks. If not specified, overflow checks are enabled if [debug-assertions](#debug-assertions) are enabled, disabled otherwise. @@ -409,8 +409,8 @@ for determining whether or not it is possible to statically or dynamically link with a dependency. For example, `cdylib` crate types may only use static linkage. This flag takes one of the following values: -* `y`, `yes`, `on`, or no value: use dynamic linking. -* `n`, `no`, or `off`: use static linking (the default). +* `y`, `yes`, `on`, `true` or no value: use dynamic linking. +* `n`, `no`, `off` or `false`: use static linking (the default). ## profile-generate @@ -487,24 +487,24 @@ The list of passes should be separated by spaces. This flag controls whether [`rpath`](https://en.wikipedia.org/wiki/Rpath) is enabled. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable rpath. -* `n`, `no`, or `off`: disable rpath (the default). +* `y`, `yes`, `on`, `true` or no value: enable rpath. +* `n`, `no`, `off` or `false`: disable rpath (the default). ## save-temps This flag controls whether temporary files generated during compilation are deleted once compilation finishes. It takes one of the following values: -* `y`, `yes`, `on`, or no value: save temporary files. -* `n`, `no`, or `off`: delete temporary files (the default). +* `y`, `yes`, `on`, `true` or no value: save temporary files. +* `n`, `no`, `off` or `false`: delete temporary files (the default). ## soft-float This option controls whether `rustc` generates code that emulates floating point instructions in software. It takes one of the following values: -* `y`, `yes`, `on`, or no value: use soft floats. -* `n`, `no`, or `off`: use hardware floats (the default). +* `y`, `yes`, `on`, `true` or no value: use soft floats. +* `n`, `no`, `off` or `false`: use hardware floats (the default). ## split-debuginfo @@ -562,7 +562,7 @@ Supported values for this option are: * `v0` — The "v0" mangling scheme. The specific format is not specified at this time. -The default if not specified will use a compiler-chosen default which may +The default, if not specified, will use a compiler-chosen default which may change in the future. [name mangling]: https://en.wikipedia.org/wiki/Name_mangling diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md index efbf861ea..d8843280b 100644 --- a/src/doc/rustc/src/json.md +++ b/src/doc/rustc/src/json.md @@ -223,7 +223,6 @@ flag][option-emit] documentation. - "link": The generated crate as specified by the crate-type. - "dep-info": The `.d` file with dependency information in a Makefile-like syntax. - "metadata": The Rust `.rmeta` file containing metadata about the crate. - - "save-analysis": A JSON file emitted by the `-Zsave-analysis` feature. */ "emit": "link" } diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 160570482..9eafa27e2 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -13,6 +13,8 @@ for targets at each tier, see the [Target Tier Policy](target-tier-policy.md). Targets are identified by their "target triple" which is the string to inform the compiler what kind of output should be produced. +Component availability is tracked [here](https://rust-lang.github.io/rustup-components-history/). + ## Tier 1 with Host Tools Tier 1 targets can be thought of as "guaranteed to work". The Rust project @@ -216,7 +218,7 @@ target | std | host | notes [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * | | ARM64 Nintendo Switch, Horizon [`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | -[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.1 RTOS | +[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS | `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD `aarch64-unknown-hermit` | ✓ | | ARM64 HermitCore `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) @@ -308,7 +310,7 @@ target | std | host | notes `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 `x86_64-apple-tvos` | * | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator -[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ? | | x86 64-bit QNX Neutrino 7.1 RTOS | +[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS | [`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | `x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support `x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md index 29e70129a..4d97b8c6c 100644 --- a/src/doc/rustc/src/platform-support/fuchsia.md +++ b/src/doc/rustc/src/platform-support/fuchsia.md @@ -12,7 +12,6 @@ The [Fuchsia team]: - Tyler Mandry ([@tmandry](https://github.com/tmandry)) - Dan Johnson ([@computerdruid](https://github.com/computerdruid)) - David Koloski ([@djkoloski](https://github.com/djkoloski)) -- Andrew Pollack ([@andrewpollack](https://github.com/andrewpollack)) - Joseph Ryan ([@P1n3appl3](https://github.com/P1n3appl3)) As the team evolves over time, the specific members listed here may differ from @@ -687,7 +686,9 @@ Rust compiler locally. See "[Targeting Fuchsia with a compiler built from source for the steps to build locally. You'll also need to download a copy of the Fuchsia SDK. The current minimum -supported SDK version is [9.20220726.1.1](https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core/linux-amd64/+/version:9.20220726.1.1). +supported SDK version is [10.20221207.2.89][minimum_supported_sdk_version]. + +[minimum_supported_sdk_version]: https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core/linux-amd64/+/version:10.20221207.2.89 Fuchsia's test runner interacts with the Fuchsia emulator and is located at `src/ci/docker/scripts/fuchsia-test-runner.py`. We can use it to start our @@ -697,7 +698,7 @@ test environment with: src/ci/docker/scripts/fuchsia-test-runner.py start --rust ${RUST_SRC_PATH}/install --sdk ${SDK_PATH} - --target-arch {x64,arm64} + --target {x86_64-unknown-fuchsia|aarch64-unknown-fuchsia} ``` Where `${RUST_SRC_PATH}/install` is the `prefix` set in `config.toml` and @@ -715,28 +716,30 @@ run the full `tests/ui` test suite: --stage=2 \ test tests/ui \ --target x86_64-unknown-fuchsia \ - --run=always --jobs 1 \ - --test-args --target-rustcflags \ - --test-args -L \ - --test-args --target-rustcflags \ - --test-args ${SDK_PATH}/arch/{x64|arm64}/sysroot/lib \ + --run=always \ --test-args --target-rustcflags \ - --test-args -L \ + --test-args -Lnative=${SDK_PATH}/arch/{x64|arm64}/sysroot/lib \ --test-args --target-rustcflags \ - --test-args ${SDK_PATH}/arch/{x64|arm64}/lib \ + --test-args -Lnative=${SDK_PATH}/arch/{x64|arm64}/lib \ --test-args --target-rustcflags \ - --test-args -Cpanic=abort \ - --test-args --target-rustcflags \ - --test-args -Zpanic_abort_tests \ + --test-args -Clink-arg=--undefined-version \ --test-args --remote-test-client \ --test-args src/ci/docker/scripts/fuchsia-test-runner.py \ ) ``` -*Note: The test suite cannot be run in parallel at the moment, so `x.py` -must be run with `--jobs 1` to ensure only one test runs at a time.* +By default, `x.py` compiles test binaries with `panic=unwind`. If you built your +Rust toolchain with `-Cpanic=abort`, you need to tell `x.py` to compile test +binaries with `panic=abort` as well: -When finished, the test runner can be used to stop the test environment: +```sh + --test-args --target-rustcflags \ + --test-args -Cpanic=abort \ + --test-args --target-rustcflags \ + --test-args -Zpanic_abort_tests \ +``` + +When finished testing, the test runner can be used to stop the test environment: ```sh src/ci/docker/scripts/fuchsia-test-runner.py stop @@ -764,8 +767,9 @@ ${SDK_PATH}/tools/${ARCH}/ffx debug connect -- \ * `--symbol-path` gets required symbol paths, which are necessary for stepping through your program. -The "[displaying source code in `zxdb`](#displaying-source-code-in-zxdb)" section describes how you can -display Rust and/or Fuchsia source code in your debugging session. +The "[displaying source code in `zxdb`](#displaying-source-code-in-zxdb)" +section describes how you can display Rust and/or Fuchsia source code in your +debugging session. ### Using `zxdb` @@ -866,6 +870,64 @@ ${SDK_PATH}/tools/${ARCH}/ffx debug connect -- \ Linking to a Fuchsia checkout can help with debugging Fuchsia libraries, such as [fdio]. +### Debugging the compiler test suite + +Debugging the compiler test suite requires some special configuration: + +First, we have to properly configure zxdb so it will be able to find debug +symbols and source information for our test. The test runner can do this for us +with: + +```sh +src/ci/docker/scripts/fuchsia-test-runner.py debug \ + --rust-src ${RUST_SRC_PATH} \ + --fuchsia-src ${FUCHSIA_SRC_PATH} \ + --test ${TEST} +``` + +where `${TEST}` is relative to Rust's `tests` directory (e.g. `ui/abi/...`). + +This will start a zxdb session that is properly configured for the specific test +being run. All three arguments are optional, so you can omit `--fuchsia-src` if +you don't have it downloaded. Now is a good time to set any desired breakpoints, +like `b main`. + +Next, we have to tell `x.py` not to optimize or strip debug symbols from our +test suite binaries. We can do this by passing some new arguments to `rustc` +through our `x.py` invocation. The full invocation is: + +```sh +( \ + source config-env.sh && \ + ./x.py \ + --config config.toml \ + --stage=2 \ + test tests/${TEST} \ + --target x86_64-unknown-fuchsia \ + --run=always \ + --test-args --target-rustcflags \ + --test-args -Lnative=${SDK_PATH}/arch/{x64|arm64}/sysroot/lib \ + --test-args --target-rustcflags \ + --test-args -Lnative=${SDK_PATH}/arch/{x64|arm64}/lib \ + --test-args --target-rustcflags \ + --test-args -Clink-arg=--undefined-version \ + --test-args --target-rustcflags \ + --test-args -Cdebuginfo=2 \ + --test-args --target-rustcflags \ + --test-args -Copt-level=0 \ + --test-args --target-rustcflags \ + --test-args -Cstrip=none \ + --test-args --remote-test-client \ + --test-args src/ci/docker/scripts/fuchsia-test-runner.py \ +) +``` + +*If you built your Rust toolchain with `panic=abort`, make sure to include the +previous flags so your test binaries are also compiled with `panic=abort`.* + +Upon running this command, the test suite binary will be run and zxdb will +attach and load any relevant debug symbols. + [Fuchsia team]: https://team-api.infra.rust-lang.org/v1/teams/fuchsia.json [Fuchsia]: https://fuchsia.dev/ [source tree]: https://fuchsia.dev/fuchsia-src/get-started/learn/build diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 37d0c3197..38198fe6c 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -2,9 +2,9 @@ **Tier: 3** -[BlackBerry® QNX®][BlackBerry] Neutrino (nto) Real-time operating system. +[QNX®][BlackBerry] Neutrino (nto) Real-time operating system. The support has been implemented jointly by [Elektrobit Automotive GmbH][Elektrobit] -and [BlackBerry][BlackBerry]. +and [Blackberry QNX][BlackBerry]. [BlackBerry]: https://blackberry.qnx.com [Elektrobit]: https://www.elektrobit.com @@ -19,19 +19,24 @@ and [BlackBerry][BlackBerry]. Currently, only cross-compilation for QNX Neutrino on AArch64 and x86_64 are supported (little endian). Adding other architectures that are supported by QNX Neutrino is possible. -The standard library does not yet support QNX Neutrino. Therefore, only `no_std` code can -be compiled. +The standard library, including `core` and `alloc` (with default allocator) are supported. -`core` and `alloc` (with default allocator) are supported. +For building or using the Rust toolchain for QNX Neutrino, the +[QNX Software Development Platform (SDP)](https://blackberry.qnx.com/en/products/foundation-software/qnx-software-development-platform) +must be installed and initialized. +Initialization is usually done by sourcing `qnxsdp-env.sh` (this will be installed as part of the SDP, see also installation instruction provided with the SDP). +Afterwards [`qcc`](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.utilities/topic/q/qcc.html) (QNX C/C++ compiler) +should be available (in the `$PATH` variable). +`qcc` will be called e.g. for linking executables. -Applications must link against `libc.so` (see example). This is required because applications -always link against the `crt` library and `crt` depends on `libc.so`. - -The correct version of `qcc` must be available by setting the `$PATH` variable (e.g. by sourcing `qnxsdp-env.sh` of the -QNX Neutrino toolchain). +When linking `no_std` applications, they must link against `libc.so` (see example). This is +required because applications always link against the `crt` library and `crt` depends on `libc.so`. +This is done automatically when using the standard library. ### Small example application +Small `no_std` example is shown below. Applications using the standard library work as well. + ```rust,ignore (platform-specific) #![no_std] #![no_main] @@ -89,30 +94,150 @@ changelog-seen = 2 2. Compile the Rust toolchain for an `x86_64-unknown-linux-gnu` host (for both `aarch64` and `x86_64` targets) -Run the following: +Compiling the Rust toolchain requires the same environment variables used for compiling C binaries. +Refer to the [QNX developer manual](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.prog/topic/devel_OS_version.html). + +To compile for QNX Neutrino (aarch64 and x86_64) and Linux (x86_64): ```bash -env \ - CC_aarch64-unknown-nto-qnx710="qcc" \ - CFLAGS_aarch64-unknown-nto-qnx710="-Vgcc_ntoaarch64le_cxx" \ - CXX_aarch64-unknown-nto-qnx710="qcc" \ - AR_aarch64_unknown_nto_qnx710="ntoaarch64-ar" \ - CC_x86_64-pc-nto-qnx710="qcc" \ - CFLAGS_x86_64-pc-nto-qnx710="-Vgcc_ntox86_64_cxx" \ - CXX_x86_64-pc-nto-qnx710="qcc" \ - AR_x86_64_pc_nto_qnx710="ntox86_64-ar" \ - ./x.py build --target aarch64-unknown-nto-qnx710 --target x86_64-pc-nto-qnx710 --target x86_64-unknown-linux-gnu rustc library/core library/alloc/ +export build_env=' + CC_aarch64-unknown-nto-qnx710=qcc + CFLAGS_aarch64-unknown-nto-qnx710=-Vgcc_ntoaarch64le_cxx + CXX_aarch64-unknown-nto-qnx710=qcc + AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar + CC_x86_64-pc-nto-qnx710=qcc + CFLAGS_x86_64-pc-nto-qnx710=-Vgcc_ntox86_64_cxx + CXX_x86_64-pc-nto-qnx710=qcc + AR_x86_64_pc_nto_qnx710=ntox86_64-ar' + +env $build_env \ + ./x.py build \ + --target aarch64-unknown-nto-qnx710 \ + --target x86_64-pc-nto-qnx710 \ + --target x86_64-unknown-linux-gnu \ + rustc library/core library/alloc ``` +## Running the Rust test suite + +The test suites of the Rust compiler and standard library can be executed much like other Rust targets. +The environment for testing should match the one used during compiler compilation (refer to `build_env` and `qcc`/`PATH` above) with the +addition of the TEST_DEVICE_ADDR environment variable. +The TEST_DEVICE_ADDR variable controls the remote runner and should point to the target, despite localhost being shown in the following example. +Note that some tests are failing which is why they are currently excluded by the target maintainers which can be seen in the following example. + +To run all tests on a x86_64 QNX Neutrino target: + +```bash +export TEST_DEVICE_ADDR="localhost:12345" # must address the test target, can be a SSH tunnel +export build_env=' + CC_aarch64-unknown-nto-qnx710=qcc + CFLAGS_aarch64-unknown-nto-qnx710=-Vgcc_ntoaarch64le_cxx + CXX_aarch64-unknown-nto-qnx710=qcc + AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar + CC_x86_64-pc-nto-qnx710=qcc + CFLAGS_x86_64-pc-nto-qnx710=-Vgcc_ntox86_64_cxx + CXX_x86_64-pc-nto-qnx710=qcc + AR_x86_64_pc_nto_qnx710=ntox86_64-ar' + +# Disable tests that only work on the host or don't make sense for this target. +# See also: +# - src/ci/docker/host-x86_64/i686-gnu/Dockerfile +# - https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Running.20tests.20on.20remote.20target +# - .github/workflows/ci.yml +export exclude_tests=' + --exclude src/bootstrap + --exclude src/tools/error_index_generator + --exclude src/tools/linkchecker + --exclude tests/ui-fulldeps + --exclude rustc + --exclude rustdoc + --exclude tests/run-make-fulldeps' + +env $build_env \ + ./x.py test -j 1 \ + $exclude_tests \ + --stage 1 \ + --target x86_64-pc-nto-qnx710 +``` + +Currently, only one thread can be used when testing due to limitations in `libc::fork` and `libc::posix_spawnp`. +See [fork documentation](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html) +(error section) for more information. +This can be achieved by using the `-j 1` parameter in the `x.py` call. +This issue is being researched and we will try to allow parallelism in the future. + ## Building Rust programs -Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you must either build Rust with the target enabled (see "Building the target" above), or build your own copy of `core` by using -`build-std` or similar. +Rust does not yet ship pre-compiled artifacts for this target. +To compile for this target, you must either build Rust with the target enabled (see "Building the target" above), +or build your own copy of `core` by using `build-std` or similar. ## Testing -Compiled executables can directly be run on QNX Neutrino. +Compiled executables can run directly on QNX Neutrino. + +### Rust std library test suite + +The target needs sufficient resources to execute all tests. The commands below assume that a QEMU image +is used. + +* Ensure that the temporary directory used by `remote-test-server` has enough free space and inodes. + 5GB of free space and 40000 inodes are known to be sufficient (the test will create more than 32k files). + To create a QEMU image in an empty directory, run this command inside the directory: + + ```bash + mkqnximage --type=qemu --ssh-ident=$HOME/.ssh/id_ed25519.pub --data-size=5000 --data-inodes=40000 + ``` + + `/data` should have enough free resources. + Set the `TMPDIR` environment variable accordingly when running `remote-test-server`, e.g.: + ```bash + TMPDIR=/data/tmp/rust remote-test-server --bind 0.0.0.0:12345 + ``` + +* Ensure the TCP stack can handle enough parallel connections (default is 200, should be 300 or higher). + After creating an image (see above), edit the file `output/build/startup.sh`: + 1. Search for `io-pkt-v6-hc` + 2. Add the parameter `-ptcpip threads_max=300`, e.g.: + ```text + io-pkt-v6-hc -U 33:33 -d e1000 -ptcpip threads_max=300 + ``` + 3. Update the image by running `mkqnximage` again with the same parameters as above for creating it. + +* Running and stopping the virtual machine + + To start the virtual machine, run inside the directory of the VM: + + ```bash + mkqnximage --run=-h + ``` + + To stop the virtual machine, run inside the directory of the VM: + + ```bash + mkqnximage --stop + ``` + +* Ensure local networking + + Ensure that 'localhost' is getting resolved to 127.0.0.1. If you can't ping the localhost, some tests may fail. + Ensure it's appended to /etc/hosts (if first `ping` command fails). + Commands have to be executed inside the virtual machine! + + ```bash + $ ping localhost + ping: Cannot resolve "localhost" (Host name lookup failure) + + $ echo "127.0.0.1 localhost" >> /etc/hosts + + $ ping localhost + PING localhost (127.0.0.1): 56 data bytes + 64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=1 ms + ``` ## Cross-compilation toolchains and C code -Compiling C code requires the same environment variables to be set as compiling the Rust toolchain (see above), to ensure `qcc` is used with proper arguments. To ensure compatibility, do not specify any further arguments that for example change calling conventions or memory layout. +Compiling C code requires the same environment variables to be set as compiling the Rust toolchain (see above), +to ensure `qcc` is used with proper arguments. +To ensure compatibility, do not specify any further arguments that for example change calling conventions or memory layout. diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md index d666d54b3..28a004a92 100644 --- a/src/doc/rustdoc/src/how-to-read-rustdoc.md +++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md @@ -84,6 +84,9 @@ When typing in the search bar, you can prefix your search term with a type followed by a colon (such as `mod:`) to restrict the results to just that kind of item. (The available items are listed in the help popup.) +Searching for `println!` will search for a macro named `println`, just like +searching for `macro:println` does. + ### Changing displayed theme You can change the displayed theme by opening the settings menu (the gear diff --git a/src/doc/style-guide/src/cargo.md b/src/doc/style-guide/src/cargo.md index f4993ba06..13b96ca8c 100644 --- a/src/doc/style-guide/src/cargo.md +++ b/src/doc/style-guide/src/cargo.md @@ -17,8 +17,7 @@ followed by the `description` at the end of that section. Don't use quotes around any standard key names; use bare keys. Only use quoted keys for non-standard keys whose names require them, and avoid introducing such key names when possible. See the [TOML -specification](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md#table) -for details. +specification](https://toml.io/en/v1.0.0#keys) for details. Put a single space both before and after the `=` between a key and value. Do not indent any key names; start all key names at the start of a line. diff --git a/src/doc/style-guide/src/statements.md b/src/doc/style-guide/src/statements.md index 29b48bb1e..4ab1c36f9 100644 --- a/src/doc/style-guide/src/statements.md +++ b/src/doc/style-guide/src/statements.md @@ -99,6 +99,84 @@ let Foo { ); ``` +#### else blocks (let-else statements) + +If a let statement contains an `else` component, also known as a let-else statement, +then the `else` component should be formatted according to the same rules as the `else` block +in [control flow expressions (i.e. if-else, and if-let-else expressions)](./expressions.md#control-flow-expressions). +Apply the same formatting rules to the components preceding +the `else` block (i.e. the `let pattern: Type = initializer_expr ...` portion) +as described [above](#let-statements) + +Similarly to if-else expressions, if the initializer +expression is multi-lined, then the `else` keyword and opening brace of the block (i.e. `else {`) +should be put on the same line as the end of the initializer +expression with a preceding space if all the following are true: + +* The initializer expression ends with one or more closing + parentheses, square brackets, and/or braces +* There is nothing else on that line +* That line is not indented beyond the indent of the first line containing the `let` keyword + +For example: + +```rust +let Some(x) = y.foo( + "abc", + fairly_long_identifier, + "def", + "123456", + "string", + "cheese", +) else { + bar() +} +``` + +Otherwise, the `else` keyword and opening brace should be placed on the next line after the end of the initializer expression, and should not be indented (the `else` keyword should be aligned with the `let` keyword). + +For example: + +```rust +let Some(x) = abcdef() + .foo( + "abc", + some_really_really_really_long_ident, + "ident", + "123456", + ) + .bar() + .baz() + .qux("fffffffffffffffff") +else { + foo_bar() +} +``` + +##### Single line let-else statements + +The entire let-else statement may be formatted on a single line if all the following are true: + +* the entire statement is *short* +* the `else` block contains a single-line expression and no statements +* the `else` block contains no comments +* the let statement components preceding the `else` block can be formatted on a single line + +```rust +let Some(1) = opt else { return }; + +let Some(1) = opt else { + return; +}; + +let Some(1) = opt else { + // nope + return +}; +``` + +Formatters may allow users to configure the value of the threshold +used to determine whether a let-else statement is *short*. ### Macros in statement position diff --git a/src/doc/unstable-book/src/compiler-flags/export-executable-symbols.md b/src/doc/unstable-book/src/compiler-flags/export-executable-symbols.md new file mode 100644 index 000000000..c7f10afac --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/export-executable-symbols.md @@ -0,0 +1,43 @@ +# `export-executable-symbols` + +The tracking issue for this feature is: [#84161](https://github.com/rust-lang/rust/issues/84161). + +------------------------ + +The `-Zexport-executable-symbols` compiler flag makes `rustc` export symbols from executables. The resulting binary is runnable, but can also be used as a dynamic library. This is useful for interoperating with programs written in other languages, in particular languages with a runtime like Java or Lua. + +For example on windows: +```rust +#[no_mangle] +fn my_function() -> usize { + return 42; +} + +fn main() { + println!("Hello, world!"); +} +``` + +A standard `cargo build` will produce a `.exe` without an export directory. When the `export-executable-symbols` flag is added + +```Bash +export RUSTFLAGS="-Zexport-executable-symbols" +cargo build +``` + +the binary has an export directory with the functions: + +```plain +The Export Tables (interpreted .edata section contents) + +... + +[Ordinal/Name Pointer] Table + [ 0] my_function + [ 1] main +``` +(the output of `objdump -x` on the binary) + +Please note that the `#[no_mangle]` attribute is required. Without it, the symbol is not exported. + +The equivalent of this flag in C and C++ compilers is the `__declspec(dllexport)` annotation or the `-rdynamic` linker flag. diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md new file mode 100644 index 000000000..7fb33cd68 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md @@ -0,0 +1,39 @@ +# `instrument-xray` + +The tracking issue for this feature is: [#102921](https://github.com/rust-lang/rust/issues/102921). + +------------------------ + +Enable generation of NOP sleds for XRay function tracing instrumentation. +For more information on XRay, +read [LLVM documentation](https://llvm.org/docs/XRay.html), +and/or the [XRay whitepaper](http://research.google.com/pubs/pub45287.html). + +Set the `-Z instrument-xray` compiler flag in order to enable XRay instrumentation. + + - `-Z instrument-xray` – use the default settings + - `-Z instrument-xray=skip-exit` – configure a custom setting + - `-Z instrument-xray=ignore-loops,instruction-threshold=300` – + multiple settings separated by commas + +Supported options: + + - `always` – force instrumentation of all functions + - `never` – do no instrument any functions + - `ignore-loops` – ignore presence of loops, + instrument functions based only on instruction count + - `instruction-threshold=10` – set a different instruction threshold for instrumentation + - `skip-entry` – do no instrument function entry + - `skip-exit` – do no instrument function exit + +The default settings are: + + - instrument both entry & exit from functions + - instrument functions with at least 200 instructions, + or containing a non-trivial loop + +Note that `-Z instrument-xray` only enables generation of NOP sleds +which on their own don't do anything useful. +In order to actually trace the functions, +you will need to link a separate runtime library of your choice, +such as Clang's [XRay Runtime Library](https://www.llvm.org/docs/XRay.html#xray-runtime-library). diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 70c3a445b..262cef345 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -531,6 +531,24 @@ LLVM KCFI is supported on the following targets: See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more details. +# KernelAddressSanitizer + +KernelAddressSanitizer (KASAN) is a freestanding version of AddressSanitizer +which is suitable for detecting memory errors in programs which do not have a +runtime environment, such as operating system kernels. KernelAddressSanitizer +requires manual implementation of the underlying functions used for tracking +KernelAddressSanitizer state. + +KernelAddressSanitizer is supported on the following targets: + +* `aarch64-unknown-none` +* `riscv64gc-unknown-none-elf` +* `riscv64imac-unknown-none-elf` +* `x86_64-unknown-none` + +See the [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan] for +more details. + # LeakSanitizer LeakSanitizer is run-time memory leak detector. @@ -714,6 +732,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT * [AddressSanitizer in Clang][clang-asan] * [ControlFlowIntegrity in Clang][clang-cfi] * [HWAddressSanitizer in Clang][clang-hwasan] +* [Linux Kernel's KernelAddressSanitizer documentation][linux-kasan] * [LeakSanitizer in Clang][clang-lsan] * [MemorySanitizer in Clang][clang-msan] * [MemTagSanitizer in LLVM][llvm-memtag] @@ -727,4 +746,5 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html [clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html +[linux-kasan]: https://www.kernel.org/doc/html/latest/dev-tools/kasan.html [llvm-memtag]: https://llvm.org/docs/MemTagSanitizer.html diff --git a/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md b/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md new file mode 100644 index 000000000..51c5fd69c --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md @@ -0,0 +1,6 @@ +# `tiny-const-eval-limit` + +-------------------- + +The `-Ztiny-const-eval-limit` compiler flag sets a tiny, non-configurable limit for const eval. +This flag should only be used by const eval tests in the rustc test suite. diff --git a/src/doc/unstable-book/src/library-features/char-error-internals.md b/src/doc/unstable-book/src/library-features/char-error-internals.md deleted file mode 100644 index 8013b4988..000000000 --- a/src/doc/unstable-book/src/library-features/char-error-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `char_error_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/src/doc/unstable-book/src/library-features/int-error-internals.md b/src/doc/unstable-book/src/library-features/int-error-internals.md deleted file mode 100644 index 402e4fa5e..000000000 --- a/src/doc/unstable-book/src/library-features/int-error-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `int_error_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh index 7a846d44a..ff1793111 100755 --- a/src/etc/pre-push.sh +++ b/src/etc/pre-push.sh @@ -14,4 +14,4 @@ ROOT_DIR="$(git rev-parse --show-toplevel)" echo "Running pre-push script $ROOT_DIR/x test tidy" cd "$ROOT_DIR" -./x test tidy +CARGOFLAGS="--locked" ./x test tidy diff --git a/src/etc/vscode_settings.json b/src/etc/vscode_settings.json new file mode 100644 index 000000000..dd01bfaa7 --- /dev/null +++ b/src/etc/vscode_settings.json @@ -0,0 +1,28 @@ +{ + "rust-analyzer.check.invocationLocation": "root", + "rust-analyzer.check.invocationStrategy": "once", + "rust-analyzer.check.overrideCommand": [ + "python3", + "x.py", + "check", + "--json-output" + ], + "rust-analyzer.linkedProjects": ["src/bootstrap/Cargo.toml", "Cargo.toml"], + "rust-analyzer.rustfmt.overrideCommand": [ + "./build/host/rustfmt/bin/rustfmt", + "--edition=2021" + ], + "rust-analyzer.procMacro.server": "./build/host/stage0/libexec/rust-analyzer-proc-macro-srv", + "rust-analyzer.procMacro.enable": true, + "rust-analyzer.cargo.buildScripts.enable": true, + "rust-analyzer.cargo.buildScripts.invocationLocation": "root", + "rust-analyzer.cargo.buildScripts.invocationStrategy": "once", + "rust-analyzer.cargo.buildScripts.overrideCommand": [ + "python3", + "x.py", + "check", + "--json-output" + ], + "rust-analyzer.cargo.sysrootSrc": "./library", + "rust-analyzer.rustc.source": "./Cargo.toml" +} diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 0271c27b4..c48f7998c 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -12,14 +12,12 @@ askama = { version = "0.11", default-features = false, features = ["config"] } itertools = "0.10.1" minifier = "0.2.2" once_cell = "1.10.0" -pulldown-cmark = { version = "0.9.2", default-features = false } regex = "1" rustdoc-json-types = { path = "../rustdoc-json-types" } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } smallvec = "1.8.1" tempfile = "3" -thin-vec = "0.2.9" tracing = "0.1" tracing-tree = "0.2.0" diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index a302750aa..9479b3ee0 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -137,7 +137,7 @@ where pub(crate) fn get_auto_trait_impls(&mut self, item_def_id: DefId) -> Vec { let tcx = self.cx.tcx; let param_env = tcx.param_env(item_def_id); - let ty = tcx.type_of(item_def_id); + let ty = tcx.type_of(item_def_id).subst_identity(); let f = auto_trait::AutoTraitFinder::new(tcx); debug!("get_auto_trait_impls({:?})", ty); @@ -734,8 +734,8 @@ struct RegionReplacer<'a, 'tcx> { tcx: TyCtxt<'tcx>, } -impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { +impl<'a, 'tcx> TypeFolder> for RegionReplacer<'a, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index e6b2b2349..bcdbbcacc 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -15,7 +15,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { pub(crate) fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec { let cx = &mut self.cx; let param_env = cx.tcx.param_env(item_def_id); - let ty = cx.tcx.bound_type_of(item_def_id); + let ty = cx.tcx.type_of(item_def_id); trace!("get_blanket_impls({:?})", ty); let mut impls = Vec::new(); diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index f1853f369..dd58a5b51 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -164,10 +164,10 @@ impl Cfg { /// Renders the configuration for human display, as a short HTML description. pub(crate) fn render_short_html(&self) -> String { let mut msg = Display(self, Format::ShortHtml).to_string(); - if self.should_capitalize_first_letter() { - if let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) { - msg[i..i + 1].make_ascii_uppercase(); - } + if self.should_capitalize_first_letter() && + let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) + { + msg[i..i + 1].make_ascii_uppercase(); } msg } diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 81f676724..bb62660e1 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -4,6 +4,7 @@ use rustc_ast::{LitKind, MetaItemLit, Path, StrStyle}; use rustc_span::create_default_session_globals_then; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::DUMMY_SP; +use thin_vec::thin_vec; fn word_cfg(s: &str) -> Cfg { Cfg::Cfg(Symbol::intern(s), None) @@ -34,7 +35,7 @@ macro_rules! dummy_meta_item_list { ($name:ident, [$($list:ident),* $(,)?]) => { MetaItem { path: Path::from_ident(Ident::from_str(stringify!($name))), - kind: MetaItemKind::List(vec![ + kind: MetaItemKind::List(thin_vec![ $( NestedMetaItem::MetaItem( dummy_meta_item_word(stringify!($list)), @@ -48,7 +49,7 @@ macro_rules! dummy_meta_item_list { ($name:ident, [$($list:expr),* $(,)?]) => { MetaItem { path: Path::from_ident(Ident::from_str(stringify!($name))), - kind: MetaItemKind::List(vec![ + kind: MetaItemKind::List(thin_vec![ $( NestedMetaItem::MetaItem($list), )* diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index b3b093312..148243683 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -9,7 +9,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId}; use rustc_hir::Mutability; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty::{self, TyCtxt}; @@ -45,7 +45,7 @@ pub(crate) fn try_inline( res: Res, name: Symbol, attrs: Option<&[ast::Attribute]>, - visited: &mut FxHashSet, + visited: &mut DefIdSet, ) -> Option> { let did = res.opt_def_id()?; if did.is_local() { @@ -163,7 +163,7 @@ pub(crate) fn try_inline_glob( cx: &mut DocContext<'_>, res: Res, current_mod: LocalDefId, - visited: &mut FxHashSet, + visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, ) -> Option> { let did = res.opt_def_id()?; @@ -251,7 +251,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean } fn build_external_function<'tcx>(cx: &mut DocContext<'tcx>, did: DefId) -> Box { - let sig = cx.tcx.fn_sig(did); + let sig = cx.tcx.fn_sig(did).subst_identity(); let late_bound_regions = sig.bound_vars().into_iter().filter_map(|var| match var { ty::BoundVariableKind::Region(ty::BrNamed(_, name)) if name != kw::UnderscoreLifetime => { @@ -303,7 +303,8 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box { let predicates = cx.tcx.explicit_predicates_of(did); - let type_ = clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did)), cx, Some(did)); + let type_ = + clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), cx, Some(did)); Box::new(clean::Typedef { type_, @@ -390,18 +391,17 @@ pub(crate) fn build_impl( // Only inline impl if the implemented trait is // reachable in rustdoc generated documentation - if !did.is_local() { - if let Some(traitref) = associated_trait { - let did = traitref.def_id; - if !cx.cache.effective_visibilities.is_directly_public(tcx, did) { - return; - } + if !did.is_local() && let Some(traitref) = associated_trait { + let did = traitref.def_id; + if !cx.cache.effective_visibilities.is_directly_public(tcx, did) { + return; + } - if let Some(stab) = tcx.lookup_stability(did) { - if stab.is_unstable() && stab.feature == sym::rustc_private { - return; - } - } + if let Some(stab) = tcx.lookup_stability(did) && + stab.is_unstable() && + stab.feature == sym::rustc_private + { + return; } } @@ -415,7 +415,9 @@ pub(crate) fn build_impl( let for_ = match &impl_item { Some(impl_) => clean_ty(impl_.self_ty, cx), - None => clean_middle_ty(ty::Binder::dummy(tcx.type_of(did)), cx, Some(did)), + None => { + clean_middle_ty(ty::Binder::dummy(tcx.type_of(did).subst_identity()), cx, Some(did)) + } }; // Only inline impl if the implementing type is @@ -525,10 +527,8 @@ pub(crate) fn build_impl( } while let Some(ty) = stack.pop() { - if let Some(did) = ty.def_id(&cx.cache) { - if tcx.is_doc_hidden(did) { - return; - } + if let Some(did) = ty.def_id(&cx.cache) && tcx.is_doc_hidden(did) { + return; } if let Some(generics) = ty.generics() { stack.extend(generics); @@ -568,11 +568,7 @@ pub(crate) fn build_impl( )); } -fn build_module( - cx: &mut DocContext<'_>, - did: DefId, - visited: &mut FxHashSet, -) -> clean::Module { +fn build_module(cx: &mut DocContext<'_>, did: DefId, visited: &mut DefIdSet) -> clean::Module { let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None); let span = clean::Span::new(cx.tcx.def_span(did)); @@ -582,9 +578,9 @@ fn build_module( fn build_module_items( cx: &mut DocContext<'_>, did: DefId, - visited: &mut FxHashSet, + visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, - allowed_def_ids: Option<&FxHashSet>, + allowed_def_ids: Option<&DefIdSet>, ) -> Vec { let mut items = Vec::new(); @@ -659,14 +655,22 @@ pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String { fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { clean::Constant { - type_: clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(def_id)), cx, Some(def_id)), + type_: clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), + cx, + Some(def_id), + ), kind: clean::ConstantKind::Extern { def_id }, } } fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::Static { clean::Static { - type_: clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did)), cx, Some(did)), + type_: clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), + cx, + Some(did), + ), mutability: if mutable { Mutability::Mut } else { Mutability::Not }, expr: None, } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0f0e16265..3edc2cd2e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -11,18 +11,20 @@ pub(crate) mod types; pub(crate) mod utils; use rustc_ast as ast; +use rustc_ast::token::{Token, TokenKind}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; -use rustc_middle::middle::resolve_lifetime as rl; +use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::InternalSubsts; -use rustc_middle::ty::TypeVisitable; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; @@ -37,6 +39,7 @@ use std::hash::Hash; use std::mem; use thin_vec::ThinVec; +use crate::clean::inline::merge_attrs; use crate::core::{self, DocContext, ImplTraitParam}; use crate::formats::item_type::ItemType; use crate::visit_ast::Module as DocModule; @@ -75,7 +78,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< // This covers the case where somebody does an import which should pull in an item, // but there's already an item with the same namespace and same name. Rust gives // priority to the not-imported one, so we should, too. - items.extend(doc.items.iter().flat_map(|(item, renamed, import_id)| { + items.extend(doc.items.values().flat_map(|(item, renamed, import_id)| { // First, lower everything other than imports. if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { return Vec::new(); @@ -88,7 +91,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< } v })); - items.extend(doc.items.iter().flat_map(|(item, renamed, _)| { + items.extend(doc.items.values().flat_map(|(item, renamed, _)| { // Now we actually lower the imports, skipping everything else. if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id())); @@ -116,7 +119,8 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< } }); - Item::from_hir_id_and_parts(doc.id, Some(doc.name), ModuleItem(Module { items, span }), cx) + let kind = ModuleItem(Module { items, span }); + Item::from_def_id_and_parts(doc.def_id.to_def_id(), Some(doc.name), kind, cx) } fn clean_generic_bound<'tcx>( @@ -197,11 +201,11 @@ fn clean_poly_trait_ref_with_bindings<'tcx>( } fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime { - let def = cx.tcx.named_region(lifetime.hir_id); + let def = cx.tcx.named_bound_var(lifetime.hir_id); if let Some( - rl::Region::EarlyBound(node_id) - | rl::Region::LateBound(_, _, node_id) - | rl::Region::Free(_, node_id), + rbv::ResolvedArg::EarlyBound(node_id) + | rbv::ResolvedArg::LateBound(_, _, node_id) + | rbv::ResolvedArg::Free(_, node_id), ) = def { if let Some(lt) = cx.substs.get(&node_id).and_then(|p| p.as_lt()).cloned() { @@ -214,7 +218,11 @@ fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'tcx>) -> Constant { let def_id = cx.tcx.hir().body_owner_def_id(constant.value.body).to_def_id(); Constant { - type_: clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(def_id)), cx, Some(def_id)), + type_: clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), + cx, + Some(def_id), + ), kind: ConstantKind::Anonymous { body: constant.value.body }, } } @@ -241,6 +249,7 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option { debug!("cannot clean region {:?}", region); @@ -309,10 +318,13 @@ pub(crate) fn clean_predicate<'tcx>( ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { Some(clean_projection_predicate(bound_predicate.rebind(pred), cx)) } + // FIXME(generic_const_exprs): should this do something? ty::PredicateKind::ConstEvaluatable(..) => None, ty::PredicateKind::WellFormed(..) => None, + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => None, ty::PredicateKind::Subtype(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) @@ -382,13 +394,10 @@ fn clean_middle_term<'tcx>( fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { match term { hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), - hir::Term::Const(c) => { - let def_id = cx.tcx.hir().local_def_id(c.hir_id); - Term::Constant(clean_middle_const( - ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, def_id)), - cx, - )) - } + hir::Term::Const(c) => Term::Constant(clean_middle_const( + ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, c.def_id)), + cx, + )), } } @@ -479,7 +488,7 @@ fn clean_generic_param_def<'tcx>( ty::GenericParamDefKind::Type { has_default, synthetic, .. } => { let default = if has_default { Some(clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(def.def_id)), + ty::Binder::dummy(cx.tcx.type_of(def.def_id).subst_identity()), cx, Some(def.def_id), )) @@ -501,7 +510,12 @@ fn clean_generic_param_def<'tcx>( GenericParamDefKind::Const { did: def.def_id, ty: Box::new(clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(def.def_id)), + ty::Binder::dummy( + cx.tcx + .type_of(def.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + ), cx, Some(def.def_id), )), @@ -523,12 +537,11 @@ fn clean_generic_param<'tcx>( generics: Option<&hir::Generics<'tcx>>, param: &hir::GenericParam<'tcx>, ) -> GenericParamDef { - let did = cx.tcx.hir().local_def_id(param.hir_id); let (name, kind) = match param.kind { hir::GenericParamKind::Lifetime { .. } => { let outlives = if let Some(generics) = generics { generics - .outlives_for_param(did) + .outlives_for_param(param.def_id) .filter(|bp| !bp.in_where_clause) .flat_map(|bp| bp.bounds) .map(|bound| match bound { @@ -544,7 +557,7 @@ fn clean_generic_param<'tcx>( hir::GenericParamKind::Type { ref default, synthetic } => { let bounds = if let Some(generics) = generics { generics - .bounds_for_param(did) + .bounds_for_param(param.def_id) .filter(|bp| bp.origin != PredicateOrigin::WhereClause) .flat_map(|bp| bp.bounds) .filter_map(|x| clean_generic_bound(x, cx)) @@ -555,7 +568,7 @@ fn clean_generic_param<'tcx>( ( param.name.ident().name, GenericParamDefKind::Type { - did: did.to_def_id(), + did: param.def_id.to_def_id(), bounds, default: default.map(|t| clean_ty(t, cx)).map(Box::new), synthetic, @@ -565,12 +578,10 @@ fn clean_generic_param<'tcx>( hir::GenericParamKind::Const { ty, default } => ( param.name.ident().name, GenericParamDefKind::Const { - did: did.to_def_id(), + did: param.def_id.to_def_id(), ty: Box::new(clean_ty(ty, cx)), - default: default.map(|ct| { - let def_id = cx.tcx.hir().local_def_id(ct.hir_id); - Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string()) - }), + default: default + .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())), }, ), }; @@ -789,43 +800,43 @@ fn clean_ty_generics<'tcx>( None })(); - if let Some(param_idx) = param_idx { - if let Some(b) = impl_trait.get_mut(¶m_idx.into()) { - let p: WherePredicate = clean_predicate(*p, cx)?; + if let Some(param_idx) = param_idx + && let Some(b) = impl_trait.get_mut(¶m_idx.into()) + { + let p: WherePredicate = clean_predicate(*p, cx)?; + + b.extend( + p.get_bounds() + .into_iter() + .flatten() + .cloned() + .filter(|b| !b.is_sized_bound(cx)), + ); - b.extend( - p.get_bounds() + let proj = projection.map(|p| { + ( + clean_projection(p.map_bound(|p| p.projection_ty), cx, None), + p.map_bound(|p| p.term), + ) + }); + if let Some(((_, trait_did, name), rhs)) = proj + .as_ref() + .and_then(|(lhs, rhs): &(Type, _)| Some((lhs.projection()?, rhs))) + { + // FIXME(...): Remove this unwrap() + impl_trait_proj.entry(param_idx).or_default().push(( + trait_did, + name, + rhs.map_bound(|rhs| rhs.ty().unwrap()), + p.get_bound_params() .into_iter() .flatten() - .cloned() - .filter(|b| !b.is_sized_bound(cx)), - ); - - let proj = projection.map(|p| { - ( - clean_projection(p.map_bound(|p| p.projection_ty), cx, None), - p.map_bound(|p| p.term), - ) - }); - if let Some(((_, trait_did, name), rhs)) = proj - .as_ref() - .and_then(|(lhs, rhs): &(Type, _)| Some((lhs.projection()?, rhs))) - { - // FIXME(...): Remove this unwrap() - impl_trait_proj.entry(param_idx).or_default().push(( - trait_did, - name, - rhs.map_bound(|rhs| rhs.ty().unwrap()), - p.get_bound_params() - .into_iter() - .flatten() - .map(|param| GenericParamDef::lifetime(param.0)) - .collect(), - )); - } - - return None; + .map(|param| GenericParamDef::lifetime(param.0)) + .collect(), + )); } + + return None; } Some(p) @@ -888,7 +899,7 @@ fn clean_ty_generics<'tcx>( // `?Sized` bound for each one we didn't find to be `Sized`. for tp in &stripped_params { if let types::GenericParamDefKind::Type { .. } = tp.kind - && !sized_params.contains(&tp.name) + && !sized_params.contains(&tp.name) { where_predicates.push(WherePredicate::BoundPredicate { ty: Type::Generic(tp.name), @@ -1214,7 +1225,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let kind = match assoc_item.kind { ty::AssocKind::Const => { let ty = clean_middle_ty( - ty::Binder::dummy(tcx.type_of(assoc_item.def_id)), + ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), ); @@ -1230,7 +1241,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( } } ty::AssocKind::Fn => { - let sig = tcx.fn_sig(assoc_item.def_id); + let sig = tcx.fn_sig(assoc_item.def_id).subst_identity(); let late_bound_regions = sig.bound_vars().into_iter().filter_map(|var| match var { ty::BoundVariableKind::Region(ty::BrNamed(_, name)) @@ -1253,7 +1264,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( if assoc_item.fn_has_self_parameter { let self_ty = match assoc_item.container { - ty::ImplContainer => tcx.type_of(assoc_item.container_id(tcx)), + ty::ImplContainer => tcx.type_of(assoc_item.container_id(tcx)).subst_identity(), ty::TraitContainer => tcx.types.self_param, }; let self_arg_ty = sig.input(0).skip_binder(); @@ -1400,7 +1411,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( AssocTypeItem( Box::new(Typedef { type_: clean_middle_ty( - ty::Binder::dummy(tcx.type_of(assoc_item.def_id)), + ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), ), @@ -1418,7 +1429,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( AssocTypeItem( Box::new(Typedef { type_: clean_middle_ty( - ty::Binder::dummy(tcx.type_of(assoc_item.def_id)), + ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), ), @@ -1463,10 +1474,10 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type // Try to normalize `::T` to a type let ty = hir_ty_to_ty(cx.tcx, hir_ty); // `hir_to_ty` can return projection types with escaping vars for GATs, e.g. `<() as Trait>::Gat<'_>` - if !ty.has_escaping_bound_vars() { - if let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) { - return clean_middle_ty(normalized_value, cx, None); - } + if !ty.has_escaping_bound_vars() + && let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) + { + return clean_middle_ty(normalized_value, cx, None); } let trait_segments = &p.segments[..p.segments.len() - 1]; @@ -1528,7 +1539,7 @@ fn maybe_expand_private_type_alias<'tcx>( let hir::ItemKind::TyAlias(ty, generics) = alias else { return None }; let provided_params = &path.segments.last().expect("segments were empty"); - let mut substs = FxHashMap::default(); + let mut substs = DefIdMap::default(); let generic_args = provided_params.args(); let mut indices: hir::GenericParamCount = Default::default(); @@ -1547,18 +1558,16 @@ fn maybe_expand_private_type_alias<'tcx>( _ => None, }); if let Some(lt) = lifetime { - let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id); let cleaned = if !lt.is_anonymous() { clean_lifetime(lt, cx) } else { Lifetime::elided() }; - substs.insert(lt_def_id.to_def_id(), SubstParam::Lifetime(cleaned)); + substs.insert(param.def_id.to_def_id(), SubstParam::Lifetime(cleaned)); } indices.lifetimes += 1; } hir::GenericParamKind::Type { ref default, .. } => { - let ty_param_def_id = cx.tcx.hir().local_def_id(param.hir_id); let mut j = 0; let type_ = generic_args.args.iter().find_map(|arg| match arg { hir::GenericArg::Type(ty) => { @@ -1571,17 +1580,14 @@ fn maybe_expand_private_type_alias<'tcx>( _ => None, }); if let Some(ty) = type_ { - substs.insert(ty_param_def_id.to_def_id(), SubstParam::Type(clean_ty(ty, cx))); + substs.insert(param.def_id.to_def_id(), SubstParam::Type(clean_ty(ty, cx))); } else if let Some(default) = *default { - substs.insert( - ty_param_def_id.to_def_id(), - SubstParam::Type(clean_ty(default, cx)), - ); + substs + .insert(param.def_id.to_def_id(), SubstParam::Type(clean_ty(default, cx))); } indices.types += 1; } hir::GenericParamKind::Const { .. } => { - let const_param_def_id = cx.tcx.hir().local_def_id(param.hir_id); let mut j = 0; let const_ = generic_args.args.iter().find_map(|arg| match arg { hir::GenericArg::Const(ct) => { @@ -1595,7 +1601,7 @@ fn maybe_expand_private_type_alias<'tcx>( }); if let Some(ct) = const_ { substs.insert( - const_param_def_id.to_def_id(), + param.def_id.to_def_id(), SubstParam::Constant(clean_const(ct, cx)), ); } @@ -1623,7 +1629,6 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T let length = match length { hir::ArrayLen::Infer(_, _) => "_".to_string(), hir::ArrayLen::Body(anon_const) => { - let def_id = cx.tcx.hir().local_def_id(anon_const.hir_id); // NOTE(min_const_generics): We can't use `const_eval_poly` for constants // as we currently do not supply the parent generics to anonymous constants // but do allow `ConstKind::Param`. @@ -1631,8 +1636,8 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T // `const_eval_poly` tries to first substitute generic parameters which // results in an ICE while manually constructing the constant and using `eval` // does nothing for `ConstKind::Param`. - let ct = ty::Const::from_anon_const(cx.tcx, def_id); - let param_env = cx.tcx.param_env(def_id); + let ct = ty::Const::from_anon_const(cx.tcx, anon_const.def_id); + let param_env = cx.tcx.param_env(anon_const.def_id); print_const(cx, ct.eval(cx.tcx, param_env)) } }; @@ -1657,7 +1662,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))), // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. - TyKind::Infer | TyKind::Err | TyKind::Typeof(..) => Infer, + TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) => Infer, } } @@ -1854,6 +1859,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Bound(..) => panic!("Bound"), ty::Placeholder(..) => panic!("Placeholder"), ty::GeneratorWitness(..) => panic!("GeneratorWitness"), + ty::GeneratorWitnessMIR(..) => panic!("GeneratorWitnessMIR"), ty::Infer(..) => panic!("Infer"), ty::Error(_) => rustc_errors::FatalError.raise(), } @@ -1885,11 +1891,9 @@ fn clean_middle_opaque_bounds<'tcx>( _ => return None, }; - if let Some(sized) = cx.tcx.lang_items().sized_trait() { - if trait_ref.def_id() == sized { - has_sized = true; - return None; - } + if let Some(sized) = cx.tcx.lang_items().sized_trait() && trait_ref.def_id() == sized { + has_sized = true; + return None; } let bindings: ThinVec<_> = bounds @@ -1928,15 +1932,18 @@ fn clean_middle_opaque_bounds<'tcx>( } pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { - let def_id = cx.tcx.hir().local_def_id(field.hir_id).to_def_id(); - clean_field_with_def_id(def_id, field.ident.name, clean_ty(field.ty, cx), cx) + clean_field_with_def_id(field.def_id.to_def_id(), field.ident.name, clean_ty(field.ty, cx), cx) } pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext<'tcx>) -> Item { clean_field_with_def_id( field.did, field.name, - clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(field.did)), cx, Some(field.did)), + clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(field.did).subst_identity()), + cx, + Some(field.did), + ), cx, ) } @@ -1979,10 +1986,8 @@ fn clean_variant_data<'tcx>( disr_expr: &Option, cx: &mut DocContext<'tcx>, ) -> Variant { - let discriminant = disr_expr.map(|disr| Discriminant { - expr: Some(disr.body), - value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(), - }); + let discriminant = disr_expr + .map(|disr| Discriminant { expr: Some(disr.body), value: disr.def_id.to_def_id() }); let kind = match variant { hir::VariantData::Struct(..) => VariantKind::Struct(VariantStruct { @@ -2067,12 +2072,12 @@ struct OneLevelVisitor<'hir> { map: rustc_middle::hir::map::Map<'hir>, item: Option<&'hir hir::Item<'hir>>, looking_for: Ident, - target_hir_id: hir::HirId, + target_def_id: LocalDefId, } impl<'hir> OneLevelVisitor<'hir> { - fn new(map: rustc_middle::hir::map::Map<'hir>, target_hir_id: hir::HirId) -> Self { - Self { map, item: None, looking_for: Ident::empty(), target_hir_id } + fn new(map: rustc_middle::hir::map::Map<'hir>, target_def_id: LocalDefId) -> Self { + Self { map, item: None, looking_for: Ident::empty(), target_def_id } } fn reset(&mut self, looking_for: Ident) { @@ -2091,8 +2096,8 @@ impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> { fn visit_item(&mut self, item: &'hir hir::Item<'hir>) { if self.item.is_none() && item.ident == self.looking_for - && matches!(item.kind, hir::ItemKind::Use(_, _)) - || item.hir_id() == self.target_hir_id + && (matches!(item.kind, hir::ItemKind::Use(_, _)) + || item.owner_id.def_id == self.target_def_id) { self.item = Some(item); } @@ -2106,41 +2111,168 @@ impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> { fn get_all_import_attributes<'hir>( mut item: &hir::Item<'hir>, tcx: TyCtxt<'hir>, - target_hir_id: hir::HirId, + target_def_id: LocalDefId, attributes: &mut Vec, + is_inline: bool, ) { + let mut first = true; let hir_map = tcx.hir(); - let mut visitor = OneLevelVisitor::new(hir_map, target_hir_id); + let mut visitor = OneLevelVisitor::new(hir_map, target_def_id); let mut visited = FxHashSet::default(); + // If the item is an import and has at least a path with two parts, we go into it. - while let hir::ItemKind::Use(path, _) = item.kind && - path.segments.len() > 1 && - let hir::def::Res::Def(_, def_id) = path.segments[path.segments.len() - 2].res && - visited.insert(def_id) - { - if let Some(hir::Node::Item(parent_item)) = hir_map.get_if_local(def_id) { - // We add the attributes from this import into the list. + while let hir::ItemKind::Use(path, _) = item.kind && visited.insert(item.hir_id()) { + if first { + // This is the "original" reexport so we get all its attributes without filtering them. attributes.extend_from_slice(hir_map.attrs(item.hir_id())); - // We get the `Ident` we will be looking for into `item`. - let looking_for = path.segments[path.segments.len() - 1].ident; - visitor.reset(looking_for); - hir::intravisit::walk_item(&mut visitor, parent_item); - if let Some(i) = visitor.item { - item = i; - } else { - break; + first = false; + } else { + add_without_unwanted_attributes(attributes, hir_map.attrs(item.hir_id()), is_inline); + } + + let def_id = if let [.., parent_segment, _] = &path.segments { + match parent_segment.res { + hir::def::Res::Def(_, def_id) => def_id, + _ if parent_segment.ident.name == kw::Crate => { + // In case the "parent" is the crate, it'll give `Res::Err` so we need to + // circumvent it this way. + tcx.parent(item.owner_id.def_id.to_def_id()) + } + _ => break, } + } else { + // If the path doesn't have a parent, then the parent is the current module. + tcx.parent(item.owner_id.def_id.to_def_id()) + }; + + let Some(parent) = hir_map.get_if_local(def_id) else { break }; + + // We get the `Ident` we will be looking for into `item`. + let looking_for = path.segments[path.segments.len() - 1].ident; + visitor.reset(looking_for); + + match parent { + hir::Node::Item(parent_item) => { + hir::intravisit::walk_item(&mut visitor, parent_item); + } + hir::Node::Crate(m) => { + hir::intravisit::walk_mod( + &mut visitor, + m, + tcx.local_def_id_to_hir_id(def_id.as_local().unwrap()), + ); + } + _ => break, + } + if let Some(i) = visitor.item { + item = i; } else { break; } } } +fn filter_tokens_from_list( + args_tokens: TokenStream, + should_retain: impl Fn(&TokenTree) -> bool, +) -> Vec { + let mut tokens = Vec::with_capacity(args_tokens.len()); + let mut skip_next_comma = false; + for token in args_tokens.into_trees() { + match token { + TokenTree::Token(Token { kind: TokenKind::Comma, .. }, _) if skip_next_comma => { + skip_next_comma = false; + } + token if should_retain(&token) => { + skip_next_comma = false; + tokens.push(token); + } + _ => { + skip_next_comma = true; + } + } + } + tokens +} + +/// When inlining items, we merge its attributes (and all the reexports attributes too) with the +/// final reexport. For example: +/// +/// ```ignore (just an example) +/// #[doc(hidden, cfg(feature = "foo"))] +/// pub struct Foo; +/// +/// #[doc(cfg(feature = "bar"))] +/// #[doc(hidden, no_inline)] +/// pub use Foo as Foo1; +/// +/// #[doc(inline)] +/// pub use Foo2 as Bar; +/// ``` +/// +/// So `Bar` at the end will have both `cfg(feature = "...")`. However, we don't want to merge all +/// attributes so we filter out the following ones: +/// * `doc(inline)` +/// * `doc(no_inline)` +/// * `doc(hidden)` +fn add_without_unwanted_attributes( + attrs: &mut Vec, + new_attrs: &[ast::Attribute], + is_inline: bool, +) { + // If it's `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything. + if !is_inline { + attrs.extend_from_slice(new_attrs); + return; + } + for attr in new_attrs { + let mut attr = attr.clone(); + match attr.kind { + ast::AttrKind::Normal(ref mut normal) => { + if let [ident] = &*normal.item.path.segments && + let ident = ident.ident.name && + ident == sym::doc + { + match normal.item.args { + ast::AttrArgs::Delimited(ref mut args) => { + let tokens = + filter_tokens_from_list(args.tokens.clone(), |token| { + !matches!( + token, + TokenTree::Token( + Token { + kind: TokenKind::Ident( + sym::hidden | sym::inline | sym::no_inline, + _, + ), + .. + }, + _, + ), + ) + }); + args.tokens = TokenStream::new(tokens); + attrs.push(attr); + } + ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => { + attrs.push(attr); + continue; + } + } + } + } + ast::AttrKind::DocComment(..) => { + attrs.push(attr); + } + } + } +} + fn clean_maybe_renamed_item<'tcx>( cx: &mut DocContext<'tcx>, item: &hir::Item<'tcx>, renamed: Option, - import_id: Option, + import_id: Option, ) -> Vec { use hir::ItemKind; @@ -2185,7 +2317,7 @@ fn clean_maybe_renamed_item<'tcx>( generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), - ItemKind::Impl(impl_) => return clean_impl(impl_, item.hir_id(), cx), + ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), // proc macros can have a name set by attributes ItemKind::Fn(ref sig, generics, body_id) => { clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) @@ -2218,40 +2350,38 @@ fn clean_maybe_renamed_item<'tcx>( _ => unreachable!("not yet converted"), }; - let mut extra_attrs = Vec::new(); - if let Some(hir::Node::Item(use_node)) = - import_id.and_then(|hir_id| cx.tcx.hir().find(hir_id)) + let mut import_attrs = Vec::new(); + let mut target_attrs = Vec::new(); + if let Some(import_id) = import_id && + let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id) { - // We get all the various imports' attributes. - get_all_import_attributes(use_node, cx.tcx, item.hir_id(), &mut extra_attrs); - } - - if !extra_attrs.is_empty() { - extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id)); - let attrs = Attributes::from_ast(&extra_attrs); - let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); - - vec![Item::from_def_id_and_attrs_and_parts( - def_id, - Some(name), - kind, - Box::new(attrs), - cfg, - )] + let is_inline = inline::load_attrs(cx, import_id.to_def_id()).lists(sym::doc).get_word_attr(sym::inline).is_some(); + // Then we get all the various imports' attributes. + get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut import_attrs, is_inline); + add_without_unwanted_attributes(&mut target_attrs, inline::load_attrs(cx, def_id), is_inline); } else { - vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)] + // We only keep the item's attributes. + target_attrs.extend_from_slice(inline::load_attrs(cx, def_id)); } + + let import_parent = import_id.map(|import_id| cx.tcx.local_parent(import_id).to_def_id()); + let (attrs, cfg) = merge_attrs(cx, import_parent, &target_attrs, Some(&import_attrs)); + + let mut item = + Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg); + item.inline_stmt_id = import_id.map(|def_id| def_id.to_def_id()); + vec![item] }) } fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx)); - Item::from_hir_id_and_parts(variant.hir_id, Some(variant.ident.name), kind, cx) + Item::from_def_id_and_parts(variant.def_id.to_def_id(), Some(variant.ident.name), kind, cx) } fn clean_impl<'tcx>( impl_: &hir::Impl<'tcx>, - hir_id: hir::HirId, + def_id: LocalDefId, cx: &mut DocContext<'tcx>, ) -> Vec { let tcx = cx.tcx; @@ -2262,7 +2392,6 @@ fn clean_impl<'tcx>( .iter() .map(|ii| clean_impl_item(tcx.hir().impl_item(ii.id), cx)) .collect::>(); - let def_id = tcx.hir().local_def_id(hir_id); // If this impl block is an implementation of the Deref trait, then we // need to try inlining the target's inherent impl blocks as well. @@ -2272,9 +2401,11 @@ fn clean_impl<'tcx>( let for_ = clean_ty(impl_.self_ty, cx); let type_alias = for_.def_id(&cx.cache).and_then(|did| match tcx.def_kind(did) { - DefKind::TyAlias => { - Some(clean_middle_ty(ty::Binder::dummy(tcx.type_of(did)), cx, Some(did))) - } + DefKind::TyAlias => Some(clean_middle_ty( + ty::Binder::dummy(tcx.type_of(did).subst_identity()), + cx, + Some(did), + )), _ => None, }); let mut make_item = |trait_: Option, for_: Type, items: Vec| { @@ -2291,7 +2422,7 @@ fn clean_impl<'tcx>( ImplKind::Normal }, })); - Item::from_hir_id_and_parts(hir_id, None, kind, cx) + Item::from_def_id_and_parts(def_id.to_def_id(), None, kind, cx) }; if let Some(type_alias) = type_alias { ret.push(make_item(trait_.clone(), type_alias, items.clone())); @@ -2323,7 +2454,7 @@ fn clean_extern_crate<'tcx>( let krate_owner_def_id = krate.owner_id.to_def_id(); if please_inline { - let mut visited = FxHashSet::default(); + let mut visited = DefIdSet::default(); let res = Res::Def(DefKind::Mod, crate_def_id); @@ -2405,17 +2536,15 @@ fn clean_use_statement_inner<'tcx>( let is_visible_from_parent_mod = visibility.is_accessible_from(parent_mod, cx.tcx) && !current_mod.is_top_level_module(); - if pub_underscore { - if let Some(ref inline) = inline_attr { - rustc_errors::struct_span_err!( - cx.tcx.sess, - inline.span(), - E0780, - "anonymous imports cannot be inlined" - ) - .span_label(import.span, "anonymous import") - .emit(); - } + if pub_underscore && let Some(ref inline) = inline_attr { + rustc_errors::struct_span_err!( + cx.tcx.sess, + inline.span(), + E0780, + "anonymous imports cannot be inlined" + ) + .span_label(import.span, "anonymous import") + .emit(); } // We consider inlining the documentation of `pub use` statements, but we @@ -2442,7 +2571,7 @@ fn clean_use_statement_inner<'tcx>( let path = clean_path(path, cx); let inner = if kind == hir::UseKind::Glob { if !denied { - let mut visited = FxHashSet::default(); + let mut visited = DefIdSet::default(); if let Some(items) = inline::try_inline_glob(cx, path.res, current_mod, &mut visited, inlined_names) { @@ -2451,17 +2580,16 @@ fn clean_use_statement_inner<'tcx>( } Import::new_glob(resolve_use_source(cx, path), true) } else { - if inline_attr.is_none() { - if let Res::Def(DefKind::Mod, did) = path.res { - if !did.is_local() && did.is_crate_root() { - // if we're `pub use`ing an extern crate root, don't inline it unless we - // were specifically asked for it - denied = true; - } - } + if inline_attr.is_none() + && let Res::Def(DefKind::Mod, did) = path.res + && !did.is_local() && did.is_crate_root() + { + // if we're `pub use`ing an extern crate root, don't inline it unless we + // were specifically asked for it + denied = true; } if !denied { - let mut visited = FxHashSet::default(); + let mut visited = DefIdSet::default(); let import_def_id = import.owner_id.to_def_id(); if let Some(mut items) = inline::try_inline( @@ -2512,8 +2640,8 @@ fn clean_maybe_renamed_foreign_item<'tcx>( hir::ForeignItemKind::Type => ForeignTypeItem, }; - Item::from_hir_id_and_parts( - item.hir_id(), + Item::from_def_id_and_parts( + item.owner_id.def_id.to_def_id(), Some(renamed.unwrap_or(item.ident.name)), kind, cx, diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index ed7683e36..ef38ca3c1 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -63,7 +63,8 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option) -> bool { self.item_id .as_def_id() - .map(|did| tcx.get_attrs_unchecked(did).inner_docs()) + .map(|did| inner_docs(tcx.get_attrs_unchecked(did))) .unwrap_or(false) } @@ -439,17 +437,6 @@ impl Item { self.attrs.doc_value() } - /// Convenience wrapper around [`Self::from_def_id_and_parts`] which converts - /// `hir_id` to a [`DefId`] - pub(crate) fn from_hir_id_and_parts( - hir_id: hir::HirId, - name: Option, - kind: ItemKind, - cx: &mut DocContext<'_>, - ) -> Item { - Item::from_def_id_and_parts(cx.tcx.hir().local_def_id(hir_id).to_def_id(), name, kind, cx) - } - pub(crate) fn from_def_id_and_parts( def_id: DefId, name: Option, @@ -493,16 +480,16 @@ impl Item { } pub(crate) fn links(&self, cx: &Context<'_>) -> Vec { - use crate::html::format::href; + use crate::html::format::{href, link_tooltip}; cx.cache() .intra_doc_links .get(&self.item_id) .map_or(&[][..], |v| v.as_slice()) .iter() - .filter_map(|ItemLink { link: s, link_text, page_id: did, ref fragment }| { - debug!(?did); - if let Ok((mut href, ..)) = href(*did, cx) { + .filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| { + debug!(?id); + if let Ok((mut href, ..)) = href(*id, cx) { debug!(?href); if let Some(ref fragment) = *fragment { fragment.render(&mut href, cx.tcx()) @@ -510,6 +497,7 @@ impl Item { Some(RenderedLink { original_text: s.clone(), new_text: link_text.clone(), + tooltip: link_tooltip(*id, fragment, cx), href, }) } else { @@ -534,6 +522,7 @@ impl Item { original_text: s.clone(), new_text: link_text.clone(), href: String::new(), + tooltip: String::new(), }) .collect() } @@ -665,7 +654,7 @@ impl Item { tcx: TyCtxt<'_>, asyncness: hir::IsAsync, ) -> hir::FnHeader { - let sig = tcx.fn_sig(def_id); + let sig = tcx.fn_sig(def_id).skip_binder(); let constness = if tcx.is_const_fn(def_id) && is_unstable_const_fn(tcx, def_id).is_none() { hir::Constness::Const @@ -677,7 +666,7 @@ impl Item { let header = match *self.kind { ItemKind::ForeignFunctionItem(_) => { let def_id = self.item_id.as_def_id().unwrap(); - let abi = tcx.fn_sig(def_id).abi(); + let abi = tcx.fn_sig(def_id).skip_binder().abi(); hir::FnHeader { unsafety: if abi == Abi::RustIntrinsic { intrinsic_operation_unsafety(tcx, self.item_id.as_def_id().unwrap()) @@ -885,8 +874,6 @@ pub(crate) trait AttributesExt { fn span(&self) -> Option; - fn inner_docs(&self) -> bool; - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option>; } @@ -905,14 +892,6 @@ impl AttributesExt for [ast::Attribute] { self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span) } - /// Returns whether the first doc-comment is an inner attribute. - /// - //// If there are no doc-comments, return true. - /// FIXME(#78591): Support both inner and outer attributes on the same item. - fn inner_docs(&self) -> bool { - self.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == AttrStyle::Inner) - } - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option> { let sess = tcx.sess; let doc_cfg_active = tcx.features().doc_cfg; @@ -1021,58 +1000,6 @@ impl> NestedAttributesExt for I { } } -/// A portion of documentation, extracted from a `#[doc]` attribute. -/// -/// Each variant contains the line number within the complete doc-comment where the fragment -/// starts, as well as the Span where the corresponding doc comment or attribute is located. -/// -/// Included files are kept separate from inline doc comments so that proper line-number -/// information can be given when a doctest fails. Sugared doc comments and "raw" doc comments are -/// kept separate because of issue #42760. -#[derive(Clone, PartialEq, Eq, Debug)] -pub(crate) struct DocFragment { - pub(crate) span: rustc_span::Span, - /// The module this doc-comment came from. - /// - /// This allows distinguishing between the original documentation and a pub re-export. - /// If it is `None`, the item was not re-exported. - pub(crate) parent_module: Option, - pub(crate) doc: Symbol, - pub(crate) kind: DocFragmentKind, - pub(crate) indent: usize, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub(crate) enum DocFragmentKind { - /// A doc fragment created from a `///` or `//!` doc comment. - SugaredDoc, - /// A doc fragment created from a "raw" `#[doc=""]` attribute. - RawDoc, -} - -/// The goal of this function is to apply the `DocFragment` transformation that is required when -/// transforming into the final Markdown, which is applying the computed indent to each line in -/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`). -/// -/// Note: remove the trailing newline where appropriate -fn add_doc_fragment(out: &mut String, frag: &DocFragment) { - let s = frag.doc.as_str(); - let mut iter = s.lines(); - if s.is_empty() { - out.push('\n'); - return; - } - while let Some(line) = iter.next() { - if line.chars().any(|c| !c.is_whitespace()) { - assert!(line.len() >= frag.indent); - out.push_str(&line[frag.indent..]); - } else { - out.push_str(line); - } - out.push('\n'); - } -} - /// Collapse a collection of [`DocFragment`]s into one string, /// handling indentation and newlines as needed. pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { @@ -1084,98 +1011,18 @@ pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { acc } -/// Removes excess indentation on comments in order for the Markdown -/// to be parsed correctly. This is necessary because the convention for -/// writing documentation is to provide a space between the /// or //! marker -/// and the doc text, but Markdown is whitespace-sensitive. For example, -/// a block of text with four-space indentation is parsed as a code block, -/// so if we didn't unindent comments, these list items -/// -/// /// A list: -/// /// -/// /// - Foo -/// /// - Bar -/// -/// would be parsed as if they were in a code block, which is likely not what the user intended. -fn unindent_doc_fragments(docs: &mut Vec) { - // `add` is used in case the most common sugared doc syntax is used ("/// "). The other - // fragments kind's lines are never starting with a whitespace unless they are using some - // markdown formatting requiring it. Therefore, if the doc block have a mix between the two, - // we need to take into account the fact that the minimum indent minus one (to take this - // whitespace into account). - // - // For example: - // - // /// hello! - // #[doc = "another"] - // - // In this case, you want "hello! another" and not "hello! another". - let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind) - && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc) - { - // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to - // "decide" how much the minimum indent will be. - 1 - } else { - 0 - }; - - // `min_indent` is used to know how much whitespaces from the start of each lines must be - // removed. Example: - // - // /// hello! - // #[doc = "another"] - // - // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum - // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4 - // (5 - 1) whitespaces. - let Some(min_indent) = docs - .iter() - .map(|fragment| { - fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { - if line.chars().all(|c| c.is_whitespace()) { - min_indent - } else { - // Compare against either space or tab, ignoring whether they are - // mixed or not. - let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count(); - cmp::min(min_indent, whitespace) - + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add } - } - }) - }) - .min() - else { - return; - }; - - for fragment in docs { - if fragment.doc == kw::Empty { - continue; - } - - let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { - min_indent - add - } else { - min_indent - }; - - fragment.indent = min_indent; - } -} - /// A link that has not yet been rendered. /// /// This link will be turned into a rendered link by [`Item::links`]. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct ItemLink { /// The original link written in the markdown - pub(crate) link: String, + pub(crate) link: Box, /// The link text displayed in the HTML. /// /// This may not be the same as `link` if there was a disambiguator /// in an intra-doc link (e.g. \[`fn@f`\]) - pub(crate) link_text: String, + pub(crate) link_text: Box, /// The `DefId` of the Item whose **HTML Page** contains the item being /// linked to. This will be different to `item_id` on item's that don't /// have their own page, such as struct fields and enum variants. @@ -1188,11 +1035,13 @@ pub struct RenderedLink { /// The text the link was original written as. /// /// This could potentially include disambiguators and backticks. - pub(crate) original_text: String, + pub(crate) original_text: Box, /// The text to display in the HTML - pub(crate) new_text: String, + pub(crate) new_text: Box, /// The URL to put in the `href` pub(crate) href: String, + /// The tooltip. + pub(crate) tooltip: String, } /// The attributes on an [`Item`], including attributes like `#[derive(...)]` and `#[inline]`, @@ -1242,26 +1091,7 @@ impl Attributes { attrs: impl Iterator)>, doc_only: bool, ) -> Attributes { - let mut doc_strings = Vec::new(); - let mut other_attrs = ast::AttrVec::new(); - for (attr, parent_module) in attrs { - if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { - trace!("got doc_str={doc_str:?}"); - let doc = beautify_doc_string(doc_str, comment_kind); - let kind = if attr.is_doc_comment() { - DocFragmentKind::SugaredDoc - } else { - DocFragmentKind::RawDoc - }; - let fragment = DocFragment { span: attr.span, doc, kind, parent_module, indent: 0 }; - doc_strings.push(fragment); - } else if !doc_only { - other_attrs.push(attr.clone()); - } - } - - unindent_doc_fragments(&mut doc_strings); - + let (doc_strings, other_attrs) = attrs_to_doc_fragments(attrs, doc_only); Attributes { doc_strings, other_attrs } } @@ -1280,20 +1110,6 @@ impl Attributes { if out.is_empty() { None } else { Some(out) } } - /// Return the doc-comments on this item, grouped by the module they came from. - /// The module can be different if this is a re-export with added documentation. - /// - /// The last newline is not trimmed so the produced strings are reusable between - /// early and late doc link resolution regardless of their position. - pub(crate) fn prepare_to_doc_link_resolution(&self) -> FxHashMap, String> { - let mut res = FxHashMap::default(); - for fragment in &self.doc_strings { - let out_str = res.entry(fragment.parent_module).or_default(); - add_doc_fragment(out_str, fragment); - } - res - } - /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined /// with newlines. pub(crate) fn collapsed_doc_value(&self) -> Option { @@ -1358,10 +1174,10 @@ impl GenericBound { pub(crate) fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool { use rustc_hir::TraitBoundModifier as TBM; - if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self { - if Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait() { - return true; - } + if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self && + Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait() + { + return true; } false } @@ -2416,10 +2232,7 @@ impl ConstantKind { pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool { match *self { - ConstantKind::TyConst { .. } => false, - ConstantKind::Extern { def_id } => def_id.as_local().map_or(false, |def_id| { - is_literal_expr(tcx, tcx.hir().local_def_id_to_hir_id(def_id)) - }), + ConstantKind::TyConst { .. } | ConstantKind::Extern { .. } => false, ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { is_literal_expr(tcx, body.hir_id) } @@ -2498,14 +2311,7 @@ impl Import { } pub(crate) fn imported_item_is_doc_hidden(&self, tcx: TyCtxt<'_>) -> bool { - match self.source.did { - Some(did) => tcx - .get_attrs(did, sym::doc) - .filter_map(ast::Attribute::meta_item_list) - .flatten() - .has_word(sym::hidden), - None => false, - } + self.source.did.map_or(false, |did| tcx.is_doc_hidden(did)) } } diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index 71eddf434..20627c2cf 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -2,6 +2,7 @@ use super::*; use crate::clean::collapse_doc_fragments; +use rustc_resolve::rustdoc::{unindent_doc_fragments, DocFragment, DocFragmentKind}; use rustc_span::create_default_session_globals_then; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::Symbol; diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index a12f764fa..c9c1c2c45 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -29,11 +29,6 @@ mod tests; pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { let module = crate::visit_ast::RustdocVisitor::new(cx).visit(); - for &cnum in cx.tcx.crates(()) { - // Analyze doc-reachability for extern items - crate::visit_lib::lib_embargo_visit_item(cx, cnum.as_def_id()); - } - // Clean the crate, translating the entire librustc_ast AST to one that is // understood by rustdoc. let mut module = clean_doc_module(&module, cx); @@ -134,7 +129,7 @@ fn external_generic_args<'tcx>( }); GenericArgs::Parenthesized { inputs, output } } else { - GenericArgs::AngleBracketed { args: args.into(), bindings: bindings.into() } + GenericArgs::AngleBracketed { args: args.into(), bindings } } } @@ -271,7 +266,7 @@ pub(crate) fn print_evaluated_const( underscores_and_type: bool, ) -> Option { tcx.const_eval_poly(def_id).ok().and_then(|val| { - let ty = tcx.type_of(def_id); + let ty = tcx.type_of(def_id).subst_identity(); match (val, ty.kind()) { (_, &ty::Ref(..)) => None, (ConstValue::Scalar(_), &ty::Adt(_, _)) => None, @@ -350,10 +345,10 @@ pub(crate) fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { return true; } - if let hir::ExprKind::Unary(hir::UnOp::Neg, expr) = &expr.kind { - if let hir::ExprKind::Lit(_) = &expr.kind { - return true; - } + if let hir::ExprKind::Unary(hir::UnOp::Neg, expr) = &expr.kind && + let hir::ExprKind::Lit(_) = &expr.kind + { + return true; } } @@ -475,6 +470,12 @@ pub(crate) fn get_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, ) -> impl Iterator { + // FIXME: To be removed once `parallel_compiler` bugs are fixed! + // More information in . + if cfg!(parallel_compiler) { + return vec![].into_iter().chain(vec![].into_iter()); + } + let auto_impls = cx .sess() .prof diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 56b40d8c6..2c514a0c8 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -509,7 +509,7 @@ impl Options { // these values up both in `dataset` and in the storage API, so it needs to be able // to convert the names back and forth. Despite doing this kebab-case to // StudlyCaps transformation automatically, the JS DOM API does not provide a - // mechanism for doing the just transformation on a string. So we want to avoid + // mechanism for doing just the transformation on a string. So we want to avoid // the StudlyCaps representation in the `dataset` property. // // We solve this by replacing all the `-`s with `_`s. We do that here, when we diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 2153e7d8c..fbfc58a43 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,23 +1,22 @@ -use rustc_ast::NodeId; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; use rustc_data_structures::unord::UnordSet; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::json::JsonEmitter; +use rustc_errors::TerminalUrl; use rustc_feature::UnstableFeatures; -use rustc_hir::def::{Namespace, Res}; -use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; +use rustc_hir::def::Res; +use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{HirId, Path, TraitCandidate}; +use rustc_hir::{HirId, Path}; use rustc_interface::interface; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; -use rustc_resolve as resolve; -use rustc_session::config::{self, CrateType, ErrorOutputType}; +use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks}; use rustc_session::lint; use rustc_session::Session; use rustc_span::symbol::sym; -use rustc_span::{source_map, Span, Symbol}; +use rustc_span::{source_map, Span}; use std::cell::RefCell; use std::mem; @@ -28,30 +27,12 @@ use crate::clean::inline::build_external_trait; use crate::clean::{self, ItemId}; use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions}; use crate::formats::cache::Cache; -use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink; use crate::passes::{self, Condition::*}; pub(crate) use rustc_session::config::{Input, Options, UnstableOptions}; -pub(crate) struct ResolverCaches { - pub(crate) markdown_links: Option>>, - pub(crate) doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option>>, - /// Traits in scope for a given module. - /// See `collect_intra_doc_links::traits_implemented_by` for more details. - pub(crate) traits_in_scope: DefIdMap>, - pub(crate) all_trait_impls: Option>, - pub(crate) all_macro_rules: FxHashMap>, -} - pub(crate) struct DocContext<'tcx> { pub(crate) tcx: TyCtxt<'tcx>, - /// Name resolver. Used for intra-doc links. - /// - /// The `Rc>` wrapping is needed because that is what's returned by - /// [`rustc_interface::Queries::expansion()`]. - // FIXME: see if we can get rid of this RefCell somehow - pub(crate) resolver: Rc>, - pub(crate) resolver_caches: ResolverCaches, /// Used for normalization. /// /// Most of this logic is copied from rustc_lint::late. @@ -60,11 +41,11 @@ pub(crate) struct DocContext<'tcx> { pub(crate) external_traits: Rc>>, /// Used while populating `external_traits` to ensure we don't process the same trait twice at /// the same time. - pub(crate) active_extern_traits: FxHashSet, + pub(crate) active_extern_traits: DefIdSet, // The current set of parameter substitutions, // for expanding type aliases at the HIR level: /// Table `DefId` of type, lifetime, or const parameter -> substituted type, lifetime, or const - pub(crate) substs: FxHashMap, + pub(crate) substs: DefIdMap, /// Table synthetic type parameter for `impl Trait` in argument position -> bounds pub(crate) impl_trait_bounds: FxHashMap>, /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`. @@ -99,20 +80,9 @@ impl<'tcx> DocContext<'tcx> { ret } - pub(crate) fn enter_resolver(&self, f: F) -> R - where - F: FnOnce(&mut resolve::Resolver<'_>) -> R, - { - self.resolver.borrow_mut().access(f) - } - /// Call the closure with the given parameters set as /// the substitutions for a type alias' RHS. - pub(crate) fn enter_alias( - &mut self, - substs: FxHashMap, - f: F, - ) -> R + pub(crate) fn enter_alias(&mut self, substs: DefIdMap, f: F) -> R where F: FnOnce(&mut Self) -> R, { @@ -133,12 +103,6 @@ impl<'tcx> DocContext<'tcx> { _ => None, } } - - pub(crate) fn with_all_trait_impls(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { - let all_trait_impls = self.resolver_caches.all_trait_impls.take(); - f(self, all_trait_impls.as_ref().expect("`all_trait_impls` are already borrowed")); - self.resolver_caches.all_trait_impls = all_trait_impls; - } } /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors. @@ -151,8 +115,10 @@ pub(crate) fn new_handler( diagnostic_width: Option, unstable_opts: &UnstableOptions, ) -> rustc_errors::Handler { - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); let emitter: Box = match error_format { ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); @@ -167,6 +133,7 @@ pub(crate) fn new_handler( diagnostic_width, false, unstable_opts.track_diagnostics, + TerminalUrl::No, ) .ui_testing(unstable_opts.ui_testing), ) @@ -186,6 +153,7 @@ pub(crate) fn new_handler( diagnostic_width, false, unstable_opts.track_diagnostics, + TerminalUrl::No, ) .ui_testing(unstable_opts.ui_testing), ) @@ -221,6 +189,7 @@ pub(crate) fn create_config( scrape_examples_options, .. }: RustdocOptions, + RenderOptions { document_private, .. }: &RenderOptions, ) -> rustc_interface::Config { // Add the doc cfg into the doc build. cfgs.push("doc".to_string()); @@ -248,6 +217,13 @@ pub(crate) fn create_config( let crate_types = if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; + let resolve_doc_links = if *document_private { + ResolveDocLinks::All + } else { + // Should be `ResolveDocLinks::Exported` in theory, but for some reason rustdoc + // still tries to request resolutions for links on private items. + ResolveDocLinks::All + }; let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false); // plays with error output here! let sessopts = config::Options { @@ -261,6 +237,7 @@ pub(crate) fn create_config( target_triple: target, unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()), actually_rustdoc: true, + resolve_doc_links, unstable_opts, error_format, diagnostic_width, @@ -279,6 +256,7 @@ pub(crate) fn create_config( output_file: None, output_dir: None, file_loader: None, + locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES, lint_caps, parse_sess_created: None, register_lints: Some(Box::new(crate::lint::register_lints)), @@ -316,8 +294,6 @@ pub(crate) fn create_config( pub(crate) fn run_global_ctxt( tcx: TyCtxt<'_>, - resolver: Rc>, - resolver_caches: ResolverCaches, show_coverage: bool, render_options: RenderOptions, output_format: OutputFormat, @@ -351,8 +327,6 @@ pub(crate) fn run_global_ctxt( let mut ctxt = DocContext { tcx, - resolver, - resolver_caches, param_env: ParamEnv::empty(), external_traits: Default::default(), active_extern_traits: Default::default(), @@ -367,6 +341,10 @@ pub(crate) fn run_global_ctxt( show_coverage, }; + for cnum in tcx.crates(()) { + crate::visit_lib::lib_embargo_visit_item(&mut ctxt, cnum.as_def_id()); + } + // Small hack to force the Sized trait to be present. // // Note that in case of `#![no_core]`, the trait is not available. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c1a652c75..9cf84acc7 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,11 +1,9 @@ use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError}; -use rustc_hir as hir; -use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::intravisit; -use rustc_hir::{HirId, CRATE_HIR_ID}; +use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError, TerminalUrl}; +use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID}; use rustc_interface::interface; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; @@ -98,6 +96,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { output_file: None, output_dir: None, file_loader: None, + locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES, lint_caps, parse_sess_created: None, register_lints: Some(Box::new(crate::lint::register_lints)), @@ -140,7 +139,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { }; hir_collector.visit_testable( "".to_string(), - CRATE_HIR_ID, + CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| tcx.hir().walk_toplevel_module(this), ); @@ -231,11 +230,11 @@ fn scrape_test_config(attrs: &[ast::Attribute]) -> GlobalTestOptions { if attr.has_name(sym::no_crate_inject) { opts.no_crate_inject = true; } - if attr.has_name(sym::attr) { - if let Some(l) = attr.meta_item_list() { - for item in l { - opts.attrs.push(pprust::meta_list_item_to_string(item)); - } + if attr.has_name(sym::attr) + && let Some(l) = attr.meta_item_list() + { + for item in l { + opts.attrs.push(pprust::meta_list_item_to_string(item)); } } } @@ -547,8 +546,10 @@ pub(crate) fn make_test( // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); supports_color = EmitterWriter::stderr( ColorConfig::Auto, None, @@ -559,6 +560,7 @@ pub(crate) fn make_test( Some(80), false, false, + TerminalUrl::No, ) .supports_color(); @@ -573,6 +575,7 @@ pub(crate) fn make_test( None, false, false, + TerminalUrl::No, ); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser @@ -594,31 +597,28 @@ pub(crate) fn make_test( loop { match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => { - if !found_main { - if let ast::ItemKind::Fn(..) = item.kind { - if item.ident.name == sym::main { - found_main = true; - } - } + if !found_main && + let ast::ItemKind::Fn(..) = item.kind && + item.ident.name == sym::main + { + found_main = true; } - if !found_extern_crate { - if let ast::ItemKind::ExternCrate(original) = item.kind { - // This code will never be reached if `crate_name` is none because - // `found_extern_crate` is initialized to `true` if it is none. - let crate_name = crate_name.unwrap(); + if !found_extern_crate && + let ast::ItemKind::ExternCrate(original) = item.kind + { + // This code will never be reached if `crate_name` is none because + // `found_extern_crate` is initialized to `true` if it is none. + let crate_name = crate_name.unwrap(); - match original { - Some(name) => found_extern_crate = name.as_str() == crate_name, - None => found_extern_crate = item.ident.as_str() == crate_name, - } + match original { + Some(name) => found_extern_crate = name.as_str() == crate_name, + None => found_extern_crate = item.ident.as_str() == crate_name, } } - if !found_macro { - if let ast::ItemKind::MacCall(..) = item.kind { - found_macro = true; - } + if !found_macro && let ast::ItemKind::MacCall(..) = item.kind { + found_macro = true; } if found_main && found_extern_crate { @@ -744,8 +744,10 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); let emitter = EmitterWriter::new( Box::new(io::sink()), @@ -758,6 +760,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { None, false, false, + TerminalUrl::No, ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); @@ -766,8 +769,8 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) { Ok(p) => p, Err(_) => { - debug!("Cannot build a parser to check mod attr so skipping..."); - return true; + // If there is an unclosed delimiter, an error will be returned by the tokentrees. + return false; } }; // If a parsing error happened, it's very likely that the attribute is incomplete. @@ -775,15 +778,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { e.cancel(); return false; } - // We now check if there is an unclosed delimiter for the attribute. To do so, we look at - // the `unclosed_delims` and see if the opening square bracket was closed. - parser - .unclosed_delims() - .get(0) - .map(|unclosed| { - unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2) - }) - .unwrap_or(true) + true }) }) .unwrap_or(false) @@ -971,14 +966,12 @@ impl Collector { fn get_filename(&self) -> FileName { if let Some(ref source_map) = self.source_map { let filename = source_map.span_to_filename(self.position); - if let FileName::Real(ref filename) = filename { - if let Ok(cur_dir) = env::current_dir() { - if let Some(local_path) = filename.local_path() { - if let Ok(path) = local_path.strip_prefix(&cur_dir) { - return path.to_owned().into(); - } - } - } + if let FileName::Real(ref filename) = filename && + let Ok(cur_dir) = env::current_dir() && + let Some(local_path) = filename.local_path() && + let Ok(path) = local_path.strip_prefix(&cur_dir) + { + return path.to_owned().into(); } filename } else if let Some(ref filename) = self.filename { @@ -1214,11 +1207,11 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { fn visit_testable( &mut self, name: String, - hir_id: HirId, + def_id: LocalDefId, sp: Span, nested: F, ) { - let ast_attrs = self.tcx.hir().attrs(hir_id); + let ast_attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id)); if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { if !cfg.matches(&self.sess.parse_sess, Some(self.tcx.features())) { return; @@ -1247,7 +1240,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { self.collector.enable_per_target_ignores, Some(&crate::html::markdown::ExtraInfo::new( self.tcx, - hir_id, + def_id.to_def_id(), span_of_attrs(&attrs).unwrap_or(sp), )), ); @@ -1276,37 +1269,37 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> _ => item.ident.to_string(), }; - self.visit_testable(name, item.hir_id(), item.span, |this| { + self.visit_testable(name, item.owner_id.def_id, item.span, |this| { intravisit::walk_item(this, item); }); } fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) { - self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { intravisit::walk_trait_item(this, item); }); } fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) { - self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { intravisit::walk_impl_item(this, item); }); } fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) { - self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { intravisit::walk_foreign_item(this, item); }); } fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) { - self.visit_testable(v.ident.to_string(), v.hir_id, v.span, |this| { + self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| { intravisit::walk_variant(this, v); }); } fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) { - self.visit_testable(f.ident.to_string(), f.hir_id, f.span, |this| { + self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| { intravisit::walk_field_def(this, f); }); } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 1c78c5b8d..8dbfaf4bb 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,7 +1,7 @@ use std::mem; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; @@ -33,7 +33,7 @@ pub(crate) struct Cache { /// /// The values of the map are a list of implementations and documentation /// found on that implementation. - pub(crate) impls: FxHashMap>, + pub(crate) impls: DefIdMap>, /// Maintains a mapping of local crate `DefId`s to the fully qualified name /// and "short type description" of that node. This is used when generating @@ -56,7 +56,7 @@ pub(crate) struct Cache { /// to the path used if the corresponding type is inlined. By /// doing this, we can detect duplicate impls on a trait page, and only display /// the impl for the inlined type. - pub(crate) exact_paths: FxHashMap>, + pub(crate) exact_paths: DefIdMap>, /// This map contains information about all known traits of this crate. /// Implementations of a crate should inherit the documentation of the @@ -127,7 +127,7 @@ pub(crate) struct Cache { struct CacheBuilder<'a, 'tcx> { cache: &'a mut Cache, /// This field is used to prevent duplicated impl blocks. - impl_ids: FxHashMap>, + impl_ids: DefIdMap, tcx: TyCtxt<'tcx>, } @@ -173,7 +173,7 @@ impl Cache { let (krate, mut impl_ids) = { let mut cache_builder = - CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: FxHashMap::default() }; + CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: Default::default() }; krate = cache_builder.fold_crate(krate); (krate, cache_builder.impl_ids) }; @@ -229,16 +229,15 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } // Collect all the implementors of traits. - if let clean::ImplItem(ref i) = *item.kind { - if let Some(trait_) = &i.trait_ { - if !i.kind.is_blanket() { - self.cache - .implementors - .entry(trait_.def_id()) - .or_default() - .push(Impl { impl_item: item.clone() }); - } - } + if let clean::ImplItem(ref i) = *item.kind && + let Some(trait_) = &i.trait_ && + !i.kind.is_blanket() + { + self.cache + .implementors + .entry(trait_.def_id()) + .or_default() + .push(Impl { impl_item: item.clone() }); } // Index this method for searching later on. @@ -288,6 +287,16 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } else { let last = self.cache.parent_stack.last().expect("parent_stack is empty 2"); let did = match &*last { + ParentStackItem::Impl { + // impl Trait for &T { fn method(self); } + // + // When generating a function index with the above shape, we want it + // associated with `T`, not with the primitive reference type. It should + // show up as `T::method`, rather than `reference::method`, in the search + // results page. + for_: clean::Type::BorrowedRef { type_, .. }, + .. + } => type_.def_id(&self.cache), ParentStackItem::Impl { for_, .. } => for_.def_id(&self.cache), ParentStackItem::Type(item_id) => item_id.as_def_id(), }; @@ -454,7 +463,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } => { dids.insert(path.def_id()); if let Some(generics) = path.generics() && - let ty::Adt(adt, _) = self.tcx.type_of(path.def_id()).kind() && + let ty::Adt(adt, _) = self.tcx.type_of(path.def_id()).subst_identity().kind() && adt.is_fundamental() { for ty in generics { if let Some(did) = ty.def_id(self.cache) { diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index 2f1f4cbf3..452e14918 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -21,6 +21,7 @@ use crate::clean; /// a heading, edit the listing in `html/render.rs`, function `sidebar_module`. This uses an /// ordering based on a helper function inside `item_module`, in the same file. #[derive(Copy, PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)] +#[repr(u8)] pub(crate) enum ItemType { Module = 0, ExternCrate = 1, @@ -139,7 +140,7 @@ impl From for ItemType { | DefKind::Field | DefKind::LifetimeParam | DefKind::GlobalAsm - | DefKind::Impl + | DefKind::Impl { .. } | DefKind::Closure | DefKind::Generator => Self::ForeignType, } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index d3dc4065d..0e4c5ed68 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -34,6 +34,7 @@ use crate::clean::{ use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::render::Context; +use crate::passes::collect_intra_doc_links::UrlFragment; use super::url_parts_builder::estimate_item_path_byte_length; use super::url_parts_builder::UrlPartsBuilder; @@ -208,7 +209,7 @@ impl clean::GenericParamDef { if f.alternate() { write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; } else { - write!(f, ": {}", print_generic_bounds(bounds, cx))?; + write!(f, ": {}", print_generic_bounds(bounds, cx))?; } } @@ -216,7 +217,7 @@ impl clean::GenericParamDef { if f.alternate() { write!(f, " = {:#}", ty.print(cx))?; } else { - write!(f, " = {}", ty.print(cx))?; + write!(f, " = {}", ty.print(cx))?; } } @@ -226,14 +227,14 @@ impl clean::GenericParamDef { if f.alternate() { write!(f, "const {}: {:#}", self.name, ty.print(cx))?; } else { - write!(f, "const {}: {}", self.name, ty.print(cx))?; + write!(f, "const {}: {}", self.name, ty.print(cx))?; } if let Some(default) = default { if f.alternate() { write!(f, " = {:#}", default)?; } else { - write!(f, " = {}", default)?; + write!(f, " = {}", default)?; } } @@ -289,7 +290,7 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( if f.alternate() { f.write_str(" ")?; } else { - f.write_str("
")?; + f.write_str("\n")?; } match pred { @@ -352,23 +353,31 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( } } else { let mut br_with_padding = String::with_capacity(6 * indent + 28); - br_with_padding.push_str("
"); - for _ in 0..indent + 4 { - br_with_padding.push_str(" "); + br_with_padding.push_str("\n"); + + let padding_amout = + if ending == Ending::Newline { indent + 4 } else { indent + "fn where ".len() }; + + for _ in 0..padding_amout { + br_with_padding.push_str(" "); } - let where_preds = where_preds.to_string().replace("
", &br_with_padding); + let where_preds = where_preds.to_string().replace('\n', &br_with_padding); if ending == Ending::Newline { - let mut clause = " ".repeat(indent.saturating_sub(1)); + let mut clause = " ".repeat(indent.saturating_sub(1)); write!(clause, "where{where_preds},")?; clause } else { - // insert a
tag after a single space but before multiple spaces at the start + // insert a newline after a single space but before multiple spaces at the start if indent == 0 { - format!("
where{where_preds}") + format!("\nwhere{where_preds}") } else { + // put the first one on the same line as the 'where' keyword + let where_preds = where_preds.replacen(&br_with_padding, " ", 1); + let mut clause = br_with_padding; - clause.truncate(clause.len() - 4 * " ".len()); + clause.truncate(clause.len() - "where ".len()); + write!(clause, "where{where_preds}")?; clause } @@ -701,11 +710,9 @@ pub(crate) fn href_with_root_path( } } }; - if !is_remote { - if let Some(root_path) = root_path { - let root = root_path.trim_end_matches('/'); - url_parts.push_front(root); - } + if !is_remote && let Some(root_path) = root_path { + let root = root_path.trim_end_matches('/'); + url_parts.push_front(root); } debug!(?url_parts); match shortty { @@ -760,6 +767,28 @@ pub(crate) fn href_relative_parts<'fqp>( } } +pub(crate) fn link_tooltip(did: DefId, fragment: &Option, cx: &Context<'_>) -> String { + let cache = cx.cache(); + let Some((fqp, shortty)) = cache.paths.get(&did) + .or_else(|| cache.external_paths.get(&did)) + else { return String::new() }; + let mut buf = Buffer::new(); + if let &Some(UrlFragment::Item(id)) = fragment { + write!(buf, "{} ", cx.tcx().def_descr(id)); + for component in fqp { + write!(buf, "{component}::"); + } + write!(buf, "{}", cx.tcx().item_name(id)); + } else if !fqp.is_empty() { + let mut fqp_it = fqp.into_iter(); + write!(buf, "{shortty} {}", fqp_it.next().unwrap()); + for component in fqp_it { + write!(buf, "::{component}"); + } + } + buf.into_inner() +} + /// Used to render a [`clean::Path`]. fn resolved_path<'cx>( w: &mut fmt::Formatter<'_>, @@ -1064,14 +1093,8 @@ fn fmt_type<'cx>( fmt_type(ty, f, use_absolute, cx)?; write!(f, ")") } - clean::Generic(..) => { - primitive_link( - f, - PrimitiveType::Reference, - &format!("{}{}{}", amp, lt, m), - cx, - )?; - fmt_type(ty, f, use_absolute, cx) + clean::Generic(name) => { + primitive_link(f, PrimitiveType::Reference, &format!("{amp}{lt}{m}{name}"), cx) } _ => { write!(f, "{}{}{}", amp, lt, m)?; @@ -1313,7 +1336,8 @@ impl clean::FnDecl { /// * `header_len`: The length of the function header and name. In other words, the number of /// characters in the function declaration up to but not including the parentheses. - ///
Used to determine line-wrapping. + /// This is expected to go into a `
`/`code-header` block, so indentation and newlines
+    ///   are preserved.
     /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
     ///   necessary.
     pub(crate) fn full_print<'a, 'tcx: 'a>(
@@ -1361,7 +1385,7 @@ impl clean::FnDecl {
                 }
             } else {
                 if i > 0 {
-                    args.push_str("
"); + args.push_str("\n"); } if input.is_const { args.push_str("const "); @@ -1387,7 +1411,7 @@ impl clean::FnDecl { let mut args = args.into_inner(); if self.c_variadic { - args.push_str(",
..."); + args.push_str(",\n ..."); args_plain.push_str(", ..."); } @@ -1397,24 +1421,20 @@ impl clean::FnDecl { let declaration_len = header_len + args_plain.len() + arrow_plain.len(); let output = if declaration_len > 80 { - let full_pad = format!("
{}", " ".repeat(indent + 4)); - let close_pad = format!("
{}", " ".repeat(indent)); + let full_pad = format!("\n{}", " ".repeat(indent + 4)); + let close_pad = format!("\n{}", " ".repeat(indent)); format!( "({pad}{args}{close}){arrow}", pad = if self.inputs.values.is_empty() { "" } else { &full_pad }, - args = args.replace("
", &full_pad), + args = args.replace('\n', &full_pad), close = close_pad, arrow = arrow ) } else { - format!("({args}){arrow}", args = args.replace("
", " "), arrow = arrow) + format!("({args}){arrow}", args = args.replace('\n', " "), arrow = arrow) }; - if f.alternate() { - write!(f, "{}", output.replace("
", "\n")) - } else { - write!(f, "{}", output) - } + write!(f, "{}", output) } } @@ -1617,7 +1637,7 @@ impl clean::TypeBinding { if f.alternate() { write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; } else { - write!(f, ": {}", print_generic_bounds(bounds, cx))?; + write!(f, ": {}", print_generic_bounds(bounds, cx))?; } } } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 8a9e6caf6..2c9fc4e3c 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -58,11 +58,11 @@ pub(crate) fn render_example_with_highlighting( write_footer(out, playground_button); } -/// Highlights `src` as a macro, returning the HTML output. -pub(crate) fn render_macro_with_highlighting(src: &str, out: &mut Buffer) { - write_header(out, "macro", None, Tooltip::None); +/// Highlights `src` as an item-decl, returning the HTML output. +pub(crate) fn render_item_decl_with_highlighting(src: &str, out: &mut Buffer) { + write!(out, "
");
     write_code(out, src, None, None);
-    write_footer(out, None);
+    write!(out, "
"); } /// Highlights `src` as a source code page, returning the HTML output. @@ -96,13 +96,19 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option, to ); if tooltip != Tooltip::None { + let edition_code; write!( out, - "
", - if let Tooltip::Edition(edition_info) = tooltip { - format!(" data-edition=\"{}\"", edition_info) - } else { - String::new() + "
", + match tooltip { + Tooltip::Ignore => "This example is not tested", + Tooltip::CompileFail => "This example deliberately fails to compile", + Tooltip::ShouldPanic => "This example panics", + Tooltip::Edition(edition) => { + edition_code = format!("This example runs with edition {edition}"); + &edition_code + } + Tooltip::None => unreachable!(), }, ); } @@ -460,10 +466,8 @@ impl<'a> PeekIter<'a> { } /// Returns the next item after the current one. It doesn't interfere with `peek_next` output. fn peek(&mut self) -> Option<&(TokenKind, &'a str)> { - if self.stored.is_empty() { - if let Some(next) = self.iter.next() { - self.stored.push_back(next); - } + if self.stored.is_empty() && let Some(next) = self.iter.next() { + self.stored.push_back(next); } self.stored.front() } diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index a60e7cb10..6ab849c92 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -30,7 +30,6 @@ pub(crate) struct Page<'a> { pub(crate) root_path: &'a str, pub(crate) static_root_path: Option<&'a str>, pub(crate) description: &'a str, - pub(crate) keywords: &'a str, pub(crate) resource_suffix: &'a str, } diff --git a/src/librustdoc/html/length_limit.rs b/src/librustdoc/html/length_limit.rs index bbdc91c8d..4c8db2c67 100644 --- a/src/librustdoc/html/length_limit.rs +++ b/src/librustdoc/html/length_limit.rs @@ -61,14 +61,14 @@ impl HtmlWithLimit { /// and returns [`ControlFlow::Break`]. pub(super) fn push(&mut self, text: &str) -> ControlFlow<(), ()> { if self.len + text.len() > self.limit { - return ControlFlow::BREAK; + return ControlFlow::Break(()); } self.flush_queue(); write!(self.buf, "{}", Escape(text)).unwrap(); self.len += text.len(); - ControlFlow::CONTINUE + ControlFlow::Continue(()) } /// Open an HTML tag. diff --git a/src/librustdoc/html/length_limit/tests.rs b/src/librustdoc/html/length_limit/tests.rs index 2d02b8a16..2185c0348 100644 --- a/src/librustdoc/html/length_limit/tests.rs +++ b/src/librustdoc/html/length_limit/tests.rs @@ -83,7 +83,7 @@ fn past_the_limit() { buf.push("word#")?; buf.push(&n.to_string())?; buf.close_tag(); - ControlFlow::CONTINUE + ControlFlow::Continue(()) }); buf.close_tag(); assert_eq!( diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 4ff67fe15..fe446ae3c 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -27,14 +27,14 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::HirId; use rustc_middle::ty::TyCtxt; +pub(crate) use rustc_resolve::rustdoc::main_body_opts; +use rustc_resolve::rustdoc::may_be_doc_link; use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; use once_cell::sync::Lazy; use std::borrow::Cow; -use std::cell::RefCell; use std::collections::VecDeque; use std::default::Default; use std::fmt::Write; @@ -47,6 +47,7 @@ use crate::html::escape::Escape; use crate::html::format::Buffer; use crate::html::highlight; use crate::html::length_limit::HtmlWithLimit; +use crate::html::render::small_url_encode; use crate::html::toc::TocBuilder; use pulldown_cmark::{ @@ -58,15 +59,6 @@ mod tests; const MAX_HEADER_LEVEL: u32 = 6; -/// Options for rendering Markdown in the main body of documentation. -pub(crate) fn main_body_opts() -> Options { - Options::ENABLE_TABLES - | Options::ENABLE_FOOTNOTES - | Options::ENABLE_STRIKETHROUGH - | Options::ENABLE_TASKLISTS - | Options::ENABLE_SMART_PUNCTUATION -} - /// Options for rendering Markdown in summaries (e.g., in search results). pub(crate) fn summary_opts() -> Options { Options::ENABLE_TABLES @@ -103,14 +95,14 @@ pub struct Markdown<'a> { /// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `

`. pub heading_offset: HeadingOffset, } -/// A tuple struct like `Markdown` that renders the markdown with a table of contents. -pub(crate) struct MarkdownWithToc<'a>( - pub(crate) &'a str, - pub(crate) &'a mut IdMap, - pub(crate) ErrorCodes, - pub(crate) Edition, - pub(crate) &'a Option, -); +/// A struct like `Markdown` that renders the markdown with a table of contents. +pub(crate) struct MarkdownWithToc<'a> { + pub(crate) content: &'a str, + pub(crate) ids: &'a mut IdMap, + pub(crate) error_codes: ErrorCodes, + pub(crate) edition: Edition, + pub(crate) playground: &'a Option, +} /// A tuple struct like `Markdown` that renders the markdown escaping HTML tags /// and includes no paragraph tags. pub(crate) struct MarkdownItemInfo<'a>(pub(crate) &'a str, pub(crate) &'a mut IdMap); @@ -295,30 +287,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { doctest::make_test(&test, krate, false, &Default::default(), edition, None); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; - // These characters don't need to be escaped in a URI. - // FIXME: use a library function for percent encoding. - fn dont_escape(c: u8) -> bool { - (b'a' <= c && c <= b'z') - || (b'A' <= c && c <= b'Z') - || (b'0' <= c && c <= b'9') - || c == b'-' - || c == b'_' - || c == b'.' - || c == b'~' - || c == b'!' - || c == b'\'' - || c == b'(' - || c == b')' - || c == b'*' - } - let mut test_escaped = String::new(); - for b in test.bytes() { - if dont_escape(b) { - test_escaped.push(char::from(b)); - } else { - write!(test_escaped, "%{:02X}", b).unwrap(); - } - } + let test_escaped = small_url_encode(test); Some(format!( r#"Run"#, url, test_escaped, channel, edition, @@ -391,6 +360,9 @@ impl<'a, I: Iterator>> Iterator for LinkReplacer<'a, I> { trace!("it matched"); assert!(self.shortcut_link.is_none(), "shortcut links cannot be nested"); self.shortcut_link = Some(link); + if title.is_empty() && !link.tooltip.is_empty() { + *title = CowStr::Borrowed(link.tooltip.as_ref()); + } } } // Now that we're done with the shortcut link, don't replace any more text. @@ -441,9 +413,12 @@ impl<'a, I: Iterator>> Iterator for LinkReplacer<'a, I> { } // If this is a link, but not a shortcut link, // replace the URL, since the broken_link_callback was not called. - Some(Event::Start(Tag::Link(_, dest, _))) => { + Some(Event::Start(Tag::Link(_, dest, title))) => { if let Some(link) = self.links.iter().find(|&link| *link.original_text == **dest) { *dest = CowStr::Borrowed(link.href.as_ref()); + if title.is_empty() && !link.tooltip.is_empty() { + *title = CowStr::Borrowed(link.tooltip.as_ref()); + } } } // Anything else couldn't have been a valid Rust path, so no need to replace the text. @@ -577,10 +552,7 @@ impl<'a, I: Iterator>> SummaryLine<'a, I> { } fn check_if_allowed_tag(t: &Tag<'_>) -> bool { - matches!( - t, - Tag::Paragraph | Tag::Item | Tag::Emphasis | Tag::Strong | Tag::Link(..) | Tag::BlockQuote - ) + matches!(t, Tag::Paragraph | Tag::Emphasis | Tag::Strong | Tag::Link(..) | Tag::BlockQuote) } fn is_forbidden_tag(t: &Tag<'_>) -> bool { @@ -771,11 +743,7 @@ pub(crate) fn find_testable_code( } Event::Text(ref s) if register_header.is_some() => { let level = register_header.unwrap(); - if s.is_empty() { - tests.register_header("", level); - } else { - tests.register_header(s, level); - } + tests.register_header(s, level); register_header = None; } _ => {} @@ -784,45 +752,26 @@ pub(crate) fn find_testable_code( } pub(crate) struct ExtraInfo<'tcx> { - id: ExtraInfoId, + def_id: DefId, sp: Span, tcx: TyCtxt<'tcx>, } -enum ExtraInfoId { - Hir(HirId), - Def(DefId), -} - impl<'tcx> ExtraInfo<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, hir_id: HirId, sp: Span) -> ExtraInfo<'tcx> { - ExtraInfo { id: ExtraInfoId::Hir(hir_id), sp, tcx } - } - - pub(crate) fn new_did(tcx: TyCtxt<'tcx>, did: DefId, sp: Span) -> ExtraInfo<'tcx> { - ExtraInfo { id: ExtraInfoId::Def(did), sp, tcx } + pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: DefId, sp: Span) -> ExtraInfo<'tcx> { + ExtraInfo { def_id, sp, tcx } } fn error_invalid_codeblock_attr(&self, msg: &str, help: &str) { - let hir_id = match self.id { - ExtraInfoId::Hir(hir_id) => hir_id, - ExtraInfoId::Def(item_did) => { - match item_did.as_local() { - Some(item_did) => self.tcx.hir().local_def_id_to_hir_id(item_did), - None => { - // If non-local, no need to check anything. - return; - } - } - } - }; - self.tcx.struct_span_lint_hir( - crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, - hir_id, - self.sp, - msg, - |lint| lint.help(help), - ); + if let Some(def_id) = self.def_id.as_local() { + self.tcx.struct_span_lint_hir( + crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, + self.tcx.hir().local_def_id_to_hir_id(def_id), + self.sp, + msg, + |lint| lint.help(help), + ); + } } } @@ -1029,8 +978,8 @@ impl Markdown<'_> { let mut replacer = |broken_link: BrokenLink<'_>| { links .iter() - .find(|link| link.original_text.as_str() == &*broken_link.reference) - .map(|link| (link.href.as_str().into(), link.new_text.as_str().into())) + .find(|link| &*link.original_text == &*broken_link.reference) + .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) }; let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut replacer)); @@ -1051,7 +1000,7 @@ impl Markdown<'_> { impl MarkdownWithToc<'_> { pub(crate) fn into_string(self) -> String { - let MarkdownWithToc(md, ids, codes, edition, playground) = self; + let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self; let p = Parser::new_ext(md, main_body_opts()).into_offset_iter(); @@ -1112,8 +1061,8 @@ impl MarkdownSummaryLine<'_> { let mut replacer = |broken_link: BrokenLink<'_>| { links .iter() - .find(|link| link.original_text.as_str() == &*broken_link.reference) - .map(|link| (link.href.as_str().into(), link.new_text.as_str().into())) + .find(|link| &*link.original_text == &*broken_link.reference) + .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) }; let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)) @@ -1159,8 +1108,8 @@ fn markdown_summary_with_limit( let mut replacer = |broken_link: BrokenLink<'_>| { link_names .iter() - .find(|link| link.original_text.as_str() == &*broken_link.reference) - .map(|link| (link.href.as_str().into(), link.new_text.as_str().into())) + .find(|link| &*link.original_text == &*broken_link.reference) + .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) }; let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)); @@ -1191,18 +1140,18 @@ fn markdown_summary_with_limit( Event::Start(tag) => match tag { Tag::Emphasis => buf.open_tag("em"), Tag::Strong => buf.open_tag("strong"), - Tag::CodeBlock(..) => return ControlFlow::BREAK, + Tag::CodeBlock(..) => return ControlFlow::Break(()), _ => {} }, Event::End(tag) => match tag { Tag::Emphasis | Tag::Strong => buf.close_tag(), - Tag::Paragraph | Tag::Heading(..) => return ControlFlow::BREAK, + Tag::Paragraph | Tag::Heading(..) => return ControlFlow::Break(()), _ => {} }, Event::HardBreak | Event::SoftBreak => buf.push(" ")?, _ => {} }; - ControlFlow::CONTINUE + ControlFlow::Continue(()) }); (buf.finish(), stopped_early) @@ -1230,14 +1179,23 @@ pub(crate) fn short_markdown_summary(markdown: &str, link_names: &[RenderedLink] /// - Headings, links, and formatting are stripped. /// - Inline code is rendered as-is, surrounded by backticks. /// - HTML and code blocks are ignored. -pub(crate) fn plain_text_summary(md: &str) -> String { +pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> String { if md.is_empty() { return String::new(); } let mut s = String::with_capacity(md.len() * 3 / 2); - for event in Parser::new_ext(md, summary_opts()) { + let mut replacer = |broken_link: BrokenLink<'_>| { + link_names + .iter() + .find(|link| &*link.original_text == &*broken_link.reference) + .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) + }; + + let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)); + + for event in p { match &event { Event::Text(text) => s.push_str(text), Event::Code(code) => { @@ -1265,14 +1223,12 @@ pub(crate) struct MarkdownLink { pub(crate) fn markdown_links( md: &str, - filter_map: impl Fn(MarkdownLink) -> Option, + preprocess_link: impl Fn(MarkdownLink) -> Option, ) -> Vec { if md.is_empty() { return vec![]; } - let links = RefCell::new(vec![]); - // FIXME: remove this function once pulldown_cmark can provide spans for link definitions. let locate = |s: &str, fallback: Range| unsafe { let s_start = s.as_ptr(); @@ -1304,46 +1260,23 @@ pub(crate) fn markdown_links( } }; - let mut push = |link: BrokenLink<'_>| { - let span = span_for_link(&link.reference, link.span); - filter_map(MarkdownLink { - kind: LinkType::ShortcutUnknown, - link: link.reference.to_string(), - range: span, - }) - .map(|link| links.borrow_mut().push(link)); - None - }; - let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut push)) - .into_offset_iter(); - - // There's no need to thread an IdMap through to here because - // the IDs generated aren't going to be emitted anywhere. - let mut ids = IdMap::new(); - let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids, HeadingOffset::H1)); - - for ev in iter { - if let Event::Start(Tag::Link( - // `<>` links cannot be intra-doc links so we skip them. - kind @ (LinkType::Inline - | LinkType::Reference - | LinkType::ReferenceUnknown - | LinkType::Collapsed - | LinkType::CollapsedUnknown - | LinkType::Shortcut - | LinkType::ShortcutUnknown), - dest, - _, - )) = ev.0 - { - debug!("found link: {dest}"); - let span = span_for_link(&dest, ev.1); - filter_map(MarkdownLink { kind, link: dest.into_string(), range: span }) - .map(|link| links.borrow_mut().push(link)); + Parser::new_with_broken_link_callback( + md, + main_body_opts(), + Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), + ) + .into_offset_iter() + .filter_map(|(event, span)| match event { + Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { + preprocess_link(MarkdownLink { + kind: link_type, + range: span_for_link(&dest, span), + link: dest.into_string(), + }) } - } - - links.into_inner() + _ => None, + }) + .collect() } #[derive(Debug)] diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 5878c5826..e05635a02 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -249,7 +249,7 @@ fn test_short_markdown_summary() { #[test] fn test_plain_text_summary() { fn t(input: &str, expect: &str) { - let output = plain_text_summary(input); + let output = plain_text_summary(input, &[]); assert_eq!(output, expect, "original: {}", input); } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 5cefe9475..5e4a59562 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use std::sync::mpsc::{channel, Receiver}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::edition::Edition; @@ -18,7 +18,7 @@ use super::search_index::build_index; use super::write_shared::write_shared; use super::{ collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes, - LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS, + LinkFromSrc, StylePath, }; use crate::clean::{self, types::ExternalLocation, ExternalCrate}; @@ -56,7 +56,7 @@ pub(crate) struct Context<'tcx> { pub(super) render_redirect_pages: bool, /// Tracks section IDs for `Deref` targets so they match in both the main /// body and the sidebar. - pub(super) deref_id_map: FxHashMap, + pub(super) deref_id_map: DefIdMap, /// The map used to ensure all generated 'id=' attributes are unique. pub(super) id_map: IdMap, /// Shared mutable state. @@ -182,7 +182,10 @@ impl<'tcx> Context<'tcx> { }; title.push_str(" - Rust"); let tyname = it.type_(); - let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(doc)); + let desc = it + .doc_value() + .as_ref() + .map(|doc| plain_text_summary(doc, &it.link_names(&self.cache()))); let desc = if let Some(desc) = desc { desc } else if it.is_crate() { @@ -195,7 +198,6 @@ impl<'tcx> Context<'tcx> { self.shared.layout.krate ) }; - let keywords = make_item_keywords(it); let name; let tyname_s = if it.is_crate() { name = format!("{} crate", tyname); @@ -212,7 +214,6 @@ impl<'tcx> Context<'tcx> { static_root_path: clone_shared.static_root_path.as_deref(), title: &title, description: &desc, - keywords: &keywords, resource_suffix: &clone_shared.resource_suffix, }; let mut page_buffer = Buffer::html(); @@ -258,7 +259,7 @@ impl<'tcx> Context<'tcx> { } /// Construct a map of items shown in the sidebar to a plain-text summary of their docs. - fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { + fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { // BTreeMap instead of HashMap to get a sorted output let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); let mut inserted: FxHashMap> = FxHashMap::default(); @@ -276,10 +277,7 @@ impl<'tcx> Context<'tcx> { if inserted.entry(short).or_default().insert(myname) { let short = short.to_string(); let myname = myname.to_string(); - map.entry(short).or_default().push(( - myname, - Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))), - )); + map.entry(short).or_default().push(myname); } } @@ -544,7 +542,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { dst, render_redirect_pages: false, id_map, - deref_id_map: FxHashMap::default(), + deref_id_map: Default::default(), shared: Rc::new(scx), include_sources, types_with_notable_traits: FxHashSet::default(), @@ -572,7 +570,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { current: self.current.clone(), dst: self.dst.clone(), render_redirect_pages: self.render_redirect_pages, - deref_id_map: FxHashMap::default(), + deref_id_map: Default::default(), id_map: IdMap::new(), shared: Rc::clone(&self.shared), include_sources: self.include_sources, @@ -598,7 +596,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { root_path: "../", static_root_path: shared.static_root_path.as_deref(), description: "List of all items in this crate", - keywords: BASIC_KEYWORDS, resource_suffix: &shared.resource_suffix, }; let all = shared.all.replace(AllTypes::new()); @@ -708,14 +705,12 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { shared.fs.write(scrape_examples_help_file, v)?; } - if let Some(ref redirections) = shared.redirections { - if !redirections.borrow().is_empty() { - let redirect_map_path = - self.dst.join(crate_name.as_str()).join("redirect-map.json"); - let paths = serde_json::to_string(&*redirections.borrow()).unwrap(); - shared.ensure_dir(&self.dst.join(crate_name.as_str()))?; - shared.fs.write(redirect_map_path, paths)?; - } + if let Some(ref redirections) = shared.redirections && !redirections.borrow().is_empty() { + let redirect_map_path = + self.dst.join(crate_name.as_str()).join("redirect-map.json"); + let paths = serde_json::to_string(&*redirections.borrow()).unwrap(); + shared.ensure_dir(&self.dst.join(crate_name.as_str()))?; + shared.fs.write(redirect_map_path, paths)?; } // No need for it anymore. @@ -828,7 +823,3 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { &self.shared.cache } } - -fn make_item_keywords(it: &clean::Item) -> String { - format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) -} diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 4fa33e890..e6a040d02 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -38,7 +38,7 @@ pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc}; use std::collections::VecDeque; use std::default::Default; -use std::fmt; +use std::fmt::{self, Write}; use std::fs; use std::iter::Peekable; use std::path::PathBuf; @@ -50,7 +50,7 @@ use rustc_ast_pretty::pprust; use rustc_attr::{ConstStability, Deprecation, StabilityLevel}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::CtorKind; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::Mutability; use rustc_middle::middle::stability; use rustc_middle::ty; @@ -83,9 +83,6 @@ use crate::scrape_examples::{CallData, CallLocation}; use crate::try_none; use crate::DOC_RUST_LANG_ORG_CHANNEL; -/// A pair of name and its optional document. -pub(crate) type NameDoc = (String, Option); - pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { crate::html::format::display_fn(move |f| { if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) } @@ -1115,7 +1112,7 @@ fn render_assoc_items( it: DefId, what: AssocItemRender<'_>, ) { - let mut derefs = FxHashSet::default(); + let mut derefs = DefIdSet::default(); derefs.insert(it); render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs) } @@ -1126,7 +1123,7 @@ fn render_assoc_items_inner( containing_item: &clean::Item, it: DefId, what: AssocItemRender<'_>, - derefs: &mut FxHashSet, + derefs: &mut DefIdSet, ) { info!("Documenting associated items of {:?}", containing_item.name); let shared = Rc::clone(&cx.shared); @@ -1215,7 +1212,7 @@ fn render_deref_methods( impl_: &Impl, container_item: &clean::Item, deref_mut: bool, - derefs: &mut FxHashSet, + derefs: &mut DefIdSet, ) { let cache = cx.cache(); let deref_type = impl_.inner_impl().trait_.as_ref().unwrap(); @@ -1313,7 +1310,7 @@ pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> O if has_notable_trait { cx.types_with_notable_traits.insert(ty.clone()); Some(format!( - " ", + " ", ty = Escape(&format!("{:#}", ty.print(cx))), )) } else { @@ -1528,11 +1525,7 @@ fn render_impl( }) }) .map(|item| format!("{}.{}", item.type_(), name)); - write!( - w, - "
", - id, item_type, in_trait_class, - ); + write!(w, "
", id, item_type, in_trait_class,); render_rightside(w, cx, item, containing_item, render_mode); if trait_.is_some() { // Anchors are only used on trait impls. @@ -1554,11 +1547,7 @@ fn render_impl( kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => { let source_id = format!("{}.{}", item_type, name); let id = cx.derive_id(source_id.clone()); - write!( - w, - "
", - id, item_type, in_trait_class - ); + write!(w, "
", id, item_type, in_trait_class); render_rightside(w, cx, item, containing_item, render_mode); if trait_.is_some() { // Anchors are only used on trait impls. @@ -1606,11 +1595,7 @@ fn render_impl( clean::AssocTypeItem(tydef, _bounds) => { let source_id = format!("{}.{}", item_type, name); let id = cx.derive_id(source_id.clone()); - write!( - w, - "
", - id, item_type, in_trait_class - ); + write!(w, "
", id, item_type, in_trait_class); if trait_.is_some() { // Anchors are only used on trait impls. write!(w, "§", id); @@ -1844,7 +1829,7 @@ pub(crate) fn render_impl_summary( } else { format!(" data-aliases=\"{}\"", aliases.join(",")) }; - write!(w, "
", id, aliases); + write!(w, "
", id, aliases); render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal); write!(w, "§", id); write!(w, "

"); @@ -2032,31 +2017,60 @@ fn get_associated_constants( .collect::>() } -// The point is to url encode any potential character from a type with genericity. -fn small_url_encode(s: String) -> String { +pub(crate) fn small_url_encode(s: String) -> String { + // These characters don't need to be escaped in a URI. + // See https://url.spec.whatwg.org/#query-percent-encode-set + // and https://url.spec.whatwg.org/#urlencoded-parsing + // and https://url.spec.whatwg.org/#url-code-points + fn dont_escape(c: u8) -> bool { + (b'a' <= c && c <= b'z') + || (b'A' <= c && c <= b'Z') + || (b'0' <= c && c <= b'9') + || c == b'-' + || c == b'_' + || c == b'.' + || c == b',' + || c == b'~' + || c == b'!' + || c == b'\'' + || c == b'(' + || c == b')' + || c == b'*' + || c == b'/' + || c == b';' + || c == b':' + || c == b'?' + // As described in urlencoded-parsing, the + // first `=` is the one that separates key from + // value. Following `=`s are part of the value. + || c == b'=' + } let mut st = String::new(); let mut last_match = 0; - for (idx, c) in s.char_indices() { - let escaped = match c { - '<' => "%3C", - '>' => "%3E", - ' ' => "%20", - '?' => "%3F", - '\'' => "%27", - '&' => "%26", - ',' => "%2C", - ':' => "%3A", - ';' => "%3B", - '[' => "%5B", - ']' => "%5D", - '"' => "%22", - _ => continue, - }; + for (idx, b) in s.bytes().enumerate() { + if dont_escape(b) { + continue; + } - st += &s[last_match..idx]; - st += escaped; - // NOTE: we only expect single byte characters here - which is fine as long as we - // only match single byte characters + if last_match != idx { + // Invariant: `idx` must be the first byte in a character at this point. + st += &s[last_match..idx]; + } + if b == b' ' { + // URL queries are decoded with + replaced with SP. + // While the same is not true for hashes, rustdoc only needs to be + // consistent with itself when encoding them. + st += "+"; + } else if b == b'%' { + st += "%%"; + } else { + write!(st, "%{:02X}", b).unwrap(); + } + // Invariant: if the current byte is not at the start of a multi-byte character, + // we need to get down here so that when the next turn of the loop comes around, + // last_match winds up equalling idx. + // + // In other words, dont_escape must always return `false` in multi-byte character. last_match = idx + 1; } @@ -2175,7 +2189,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { if let Some(impl_) = v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) { - let mut derefs = FxHashSet::default(); + let mut derefs = DefIdSet::default(); derefs.insert(did); sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links); } @@ -2195,7 +2209,7 @@ fn sidebar_deref_methods( out: &mut Buffer, impl_: &Impl, v: &[Impl], - derefs: &mut FxHashSet, + derefs: &mut DefIdSet, used_links: &mut FxHashSet, ) { let c = cx.cache(); @@ -2211,14 +2225,13 @@ fn sidebar_deref_methods( }) { debug!("found target, real_target: {:?} {:?}", target, real_target); - if let Some(did) = target.def_id(c) { - if let Some(type_did) = impl_.inner_impl().for_.def_id(c) { - // `impl Deref for S` - if did == type_did || !derefs.insert(did) { - // Avoid infinite cycles - return; - } - } + if let Some(did) = target.def_id(c) && + let Some(type_did) = impl_.inner_impl().for_.def_id(c) && + // `impl Deref for S` + (did == type_did || !derefs.insert(did)) + { + // Avoid infinite cycles + return; } let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); let inner_impl = target @@ -2252,25 +2265,24 @@ fn sidebar_deref_methods( } // Recurse into any further impls that might exist for `target` - if let Some(target_did) = target.def_id(c) { - if let Some(target_impls) = c.impls.get(&target_did) { - if let Some(target_deref_impl) = target_impls.iter().find(|i| { - i.inner_impl() - .trait_ - .as_ref() - .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) - .unwrap_or(false) - }) { - sidebar_deref_methods( - cx, - out, - target_deref_impl, - target_impls, - derefs, - used_links, - ); - } - } + if let Some(target_did) = target.def_id(c) && + let Some(target_impls) = c.impls.get(&target_did) && + let Some(target_deref_impl) = target_impls.iter().find(|i| { + i.inner_impl() + .trait_ + .as_ref() + .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) + .unwrap_or(false) + }) + { + sidebar_deref_methods( + cx, + out, + target_deref_impl, + target_impls, + derefs, + used_links, + ); } } } @@ -2755,8 +2767,6 @@ fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { } } -pub(crate) const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; - /// Returns a list of all paths used in the type. /// This is used to help deduplicate imported impls /// for reexported types. If any of the contained diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index f824c9e3a..2869a3961 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::{self, Adt, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_target::abi::{LayoutS, Primitive, TagEncoding, VariantIdx, Variants}; +use rustc_target::abi::{LayoutS, Primitive, TagEncoding, Variants}; use std::cmp::Ordering; use std::fmt; use std::rc::Rc; @@ -39,10 +39,10 @@ use crate::html::{highlight, static_files}; use askama::Template; use itertools::Itertools; -const ITEM_TABLE_OPEN: &str = "
"; -const ITEM_TABLE_CLOSE: &str = "
"; -const ITEM_TABLE_ROW_OPEN: &str = "
"; -const ITEM_TABLE_ROW_CLOSE: &str = "
"; +const ITEM_TABLE_OPEN: &str = "
    "; +const ITEM_TABLE_CLOSE: &str = "
"; +const ITEM_TABLE_ROW_OPEN: &str = "
  • "; +const ITEM_TABLE_ROW_CLOSE: &str = "
  • "; // A component in a `use` path, like `string` in std::string::ToString struct PathComponent { @@ -338,14 +338,14 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: match *src { Some(src) => write!( w, - "
    {}extern crate {} as {};", + "
    {}extern crate {} as {};", visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx), anchor(myitem.item_id.expect_def_id(), src, cx), myitem.name.unwrap(), ), None => write!( w, - "
    {}extern crate {};", + "
    {}extern crate {};", visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx), anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx), ), @@ -355,7 +355,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: } clean::ImportItem(ref import) => { - let (stab, stab_tags) = if let Some(import_def_id) = import.source.did { + let stab_tags = if let Some(import_def_id) = import.source.did { let ast_attrs = cx.tcx().get_attrs_unchecked(import_def_id); let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs)); @@ -367,15 +367,12 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: ..myitem.clone() }; - let stab = import_item.stability_class(cx.tcx()); let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx())); - (stab, stab_tags) + stab_tags } else { - (None, None) + None }; - let add = if stab.is_some() { " " } else { "" }; - w.write_str(ITEM_TABLE_ROW_OPEN); let id = match import.kind { clean::ImportKind::Simple(s) => { @@ -387,15 +384,14 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() { ("", "") } else { - ("
    ", "
    ") + ("
    ", "
    ") }; write!( w, - "
    \ + "
    \ {vis}{imp}\
    \ {stab_tags_before}{stab_tags}{stab_tags_after}", - stab = stab.unwrap_or_default(), vis = visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx), imp = import.print(cx), ); @@ -417,9 +413,6 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: _ => "", }; - let stab = myitem.stability_class(cx.tcx()); - let add = if stab.is_some() { " " } else { "" }; - let visibility_emoji = match myitem.visibility(tcx) { Some(ty::Visibility::Restricted(_)) => { " 🔒 " @@ -433,11 +426,11 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: let (docs_before, docs_after) = if docs.is_empty() { ("", "") } else { - ("
    ", "
    ") + ("
    ", "
    ") }; write!( w, - "
    \ + "
    \ {name}\ {visibility_emoji}\ {unsafety_flag}\ @@ -448,11 +441,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: visibility_emoji = visibility_emoji, stab_tags = extra_info_tags(myitem, item, cx.tcx()), class = myitem.type_(), - add = add, - stab = stab.unwrap_or_default(), unsafety_flag = unsafety_flag, href = item_path(myitem.type_(), myitem.name.unwrap().as_str()), - title = [full_path(cx, myitem), myitem.type_().to_string()] + title = [myitem.type_().to_string(), full_path(cx, myitem)] .iter() .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None }) .collect::>() @@ -530,26 +521,24 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle let notable_traits = f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx)); - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - render_attributes_in_pre(w, it, ""); - w.reserve(header_len); - write!( - w, - "{vis}{constness}{asyncness}{unsafety}{abi}fn \ - {name}{generics}{decl}{notable_traits}{where_clause}", - vis = visibility, - constness = constness, - asyncness = asyncness, - unsafety = unsafety, - abi = abi, - name = name, - generics = f.generics.print(cx), - where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline), - decl = f.decl.full_print(header_len, 0, cx), - notable_traits = notable_traits.unwrap_or_default(), - ); - }); + wrap_item(w, |w| { + render_attributes_in_pre(w, it, ""); + w.reserve(header_len); + write!( + w, + "{vis}{constness}{asyncness}{unsafety}{abi}fn \ + {name}{generics}{decl}{notable_traits}{where_clause}", + vis = visibility, + constness = constness, + asyncness = asyncness, + unsafety = unsafety, + abi = abi, + name = name, + generics = f.generics.print(cx), + where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline), + decl = f.decl.full_print(header_len, 0, cx), + notable_traits = notable_traits.unwrap_or_default(), + ); }); document(w, cx, it, None, HeadingOffset::H2); } @@ -569,145 +558,140 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone(); // Output the trait definition - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - render_attributes_in_pre(w, it, ""); - write!( - w, - "{}{}{}trait {}{}{}", - visibility_print_with_space(it.visibility(tcx), it.item_id, cx), - t.unsafety(tcx).print_with_space(), - if t.is_auto(tcx) { "auto " } else { "" }, - it.name.unwrap(), - t.generics.print(cx), - bounds - ); + wrap_item(w, |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "{}{}{}trait {}{}{}", + visibility_print_with_space(it.visibility(tcx), it.item_id, cx), + t.unsafety(tcx).print_with_space(), + if t.is_auto(tcx) { "auto " } else { "" }, + it.name.unwrap(), + t.generics.print(cx), + bounds + ); - if !t.generics.where_predicates.is_empty() { - write!(w, "{}", print_where_clause(&t.generics, cx, 0, Ending::Newline)); - } else { - w.write_str(" "); - } + if !t.generics.where_predicates.is_empty() { + write!(w, "{}", print_where_clause(&t.generics, cx, 0, Ending::Newline)); + } else { + w.write_str(" "); + } - if t.items.is_empty() { - w.write_str("{ }"); - } else { - // FIXME: we should be using a derived_id for the Anchors here - w.write_str("{\n"); - let mut toggle = false; - - // If there are too many associated types, hide _everything_ - if should_hide_fields(count_types) { - toggle = true; - toggle_open( - w, - format_args!( - "{} associated items", - count_types + count_consts + count_methods - ), - ); - } - for types in [&required_types, &provided_types] { - for t in types { - render_assoc_item( - w, - t, - AssocItemLink::Anchor(None), - ItemType::Trait, - cx, - RenderMode::Normal, - ); - w.write_str(";\n"); - } - } - // If there are too many associated constants, hide everything after them - // We also do this if the types + consts is large because otherwise we could - // render a bunch of types and _then_ a bunch of consts just because both were - // _just_ under the limit - if !toggle && should_hide_fields(count_types + count_consts) { - toggle = true; - toggle_open( - w, - format_args!( - "{} associated constant{} and {} method{}", - count_consts, - pluralize(count_consts), - count_methods, - pluralize(count_methods), - ), - ); - } - if count_types != 0 && (count_consts != 0 || count_methods != 0) { - w.write_str("\n"); - } - for consts in [&required_consts, &provided_consts] { - for c in consts { - render_assoc_item( - w, - c, - AssocItemLink::Anchor(None), - ItemType::Trait, - cx, - RenderMode::Normal, - ); - w.write_str(";\n"); - } - } - if !toggle && should_hide_fields(count_methods) { - toggle = true; - toggle_open(w, format_args!("{} methods", count_methods)); - } - if count_consts != 0 && count_methods != 0 { - w.write_str("\n"); - } - for (pos, m) in required_methods.iter().enumerate() { + if t.items.is_empty() { + w.write_str("{ }"); + } else { + // FIXME: we should be using a derived_id for the Anchors here + w.write_str("{\n"); + let mut toggle = false; + + // If there are too many associated types, hide _everything_ + if should_hide_fields(count_types) { + toggle = true; + toggle_open( + w, + format_args!("{} associated items", count_types + count_consts + count_methods), + ); + } + for types in [&required_types, &provided_types] { + for t in types { render_assoc_item( w, - m, + t, AssocItemLink::Anchor(None), ItemType::Trait, cx, RenderMode::Normal, ); w.write_str(";\n"); - - if pos < required_methods.len() - 1 { - w.write_str(""); - } - } - if !required_methods.is_empty() && !provided_methods.is_empty() { - w.write_str("\n"); } - for (pos, m) in provided_methods.iter().enumerate() { + } + // If there are too many associated constants, hide everything after them + // We also do this if the types + consts is large because otherwise we could + // render a bunch of types and _then_ a bunch of consts just because both were + // _just_ under the limit + if !toggle && should_hide_fields(count_types + count_consts) { + toggle = true; + toggle_open( + w, + format_args!( + "{} associated constant{} and {} method{}", + count_consts, + pluralize(count_consts), + count_methods, + pluralize(count_methods), + ), + ); + } + if count_types != 0 && (count_consts != 0 || count_methods != 0) { + w.write_str("\n"); + } + for consts in [&required_consts, &provided_consts] { + for c in consts { render_assoc_item( w, - m, + c, AssocItemLink::Anchor(None), ItemType::Trait, cx, RenderMode::Normal, ); - match *m.kind { - clean::MethodItem(ref inner, _) - if !inner.generics.where_predicates.is_empty() => - { - w.write_str(",\n { ... }\n"); - } - _ => { - w.write_str(" { ... }\n"); - } - } + w.write_str(";\n"); + } + } + if !toggle && should_hide_fields(count_methods) { + toggle = true; + toggle_open(w, format_args!("{} methods", count_methods)); + } + if count_consts != 0 && count_methods != 0 { + w.write_str("\n"); + } - if pos < provided_methods.len() - 1 { - w.write_str(""); - } + if !required_methods.is_empty() { + write!(w, " // Required method{}\n", pluralize(required_methods.len())); + } + for (pos, m) in required_methods.iter().enumerate() { + render_assoc_item( + w, + m, + AssocItemLink::Anchor(None), + ItemType::Trait, + cx, + RenderMode::Normal, + ); + w.write_str(";\n"); + + if pos < required_methods.len() - 1 { + w.write_str(""); } - if toggle { - toggle_close(w); + } + if !required_methods.is_empty() && !provided_methods.is_empty() { + w.write_str("\n"); + } + + if !provided_methods.is_empty() { + write!(w, " // Provided method{}\n", pluralize(provided_methods.len())); + } + for (pos, m) in provided_methods.iter().enumerate() { + render_assoc_item( + w, + m, + AssocItemLink::Anchor(None), + ItemType::Trait, + cx, + RenderMode::Normal, + ); + + w.write_str(" { ... }\n"); + + if pos < provided_methods.len() - 1 { + w.write_str(""); } - w.write_str("}"); } - }); + if toggle { + toggle_close(w); + } + w.write_str("}"); + } }); // Trait documentation @@ -735,7 +719,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; write!(w, "
    "); } - write!(w, "
    ", id); + write!(w, "
    ", id); render_rightside(w, cx, m, t, RenderMode::Normal); write!(w, "

    "); render_assoc_item( @@ -1050,18 +1034,16 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - render_attributes_in_pre(w, it, ""); - write!( - w, - "trait {}{}{} = {};", - it.name.unwrap(), - t.generics.print(cx), - print_where_clause(&t.generics, cx, 0, Ending::Newline), - bounds(&t.bounds, true, cx) - ); - }); + wrap_item(w, |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "trait {}{}{} = {};", + it.name.unwrap(), + t.generics.print(cx), + print_where_clause(&t.generics, cx, 0, Ending::Newline), + bounds(&t.bounds, true, cx) + ); }); document(w, cx, it, None, HeadingOffset::H2); @@ -1074,18 +1056,16 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & } fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - render_attributes_in_pre(w, it, ""); - write!( - w, - "type {}{}{where_clause} = impl {bounds};", - it.name.unwrap(), - t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), - bounds = bounds(&t.bounds, false, cx), - ); - }); + wrap_item(w, |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "type {}{}{where_clause} = impl {bounds};", + it.name.unwrap(), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), + bounds = bounds(&t.bounds, false, cx), + ); }); document(w, cx, it, None, HeadingOffset::H2); @@ -1101,10 +1081,10 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); - write!(w, "{}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx)); write!( w, - "type {}{}{where_clause} = {type_};", + "{}type {}{}{where_clause} = {type_};", + visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), it.name.unwrap(), t.generics.print(cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), @@ -1113,7 +1093,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea }); } - wrap_into_item_decl(w, |w| write_content(w, cx, it, t)); + write_content(w, cx, it, t); document(w, cx, it, None, HeadingOffset::H2); @@ -1127,11 +1107,9 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea } fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - render_attributes_in_pre(w, it, ""); - render_union(w, it, Some(&s.generics), &s.fields, "", cx); - }); + wrap_item(w, |w| { + render_attributes_in_pre(w, it, ""); + render_union(w, it, Some(&s.generics), &s.fields, cx); }); document(w, cx, it, None, HeadingOffset::H2); @@ -1160,13 +1138,11 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: §\ {name}: {ty}\ ", - id = id, - name = name, shortty = ItemType::StructField, ty = ty.print(cx), ); if let Some(stability_class) = field.stability_class(cx.tcx()) { - write!(w, "", stab = stability_class); + write!(w, ""); } document(w, cx, field, Some(it), HeadingOffset::H3); } @@ -1179,7 +1155,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]) { for (i, ty) in s.iter().enumerate() { if i > 0 { - w.write_str(", "); + w.write_str(", "); } match *ty.kind { clean::StrippedItem(box clean::StructFieldItem(_)) => w.write_str("_"), @@ -1192,60 +1168,58 @@ fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item] fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) { let tcx = cx.tcx(); let count_variants = e.variants().count(); - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - render_attributes_in_pre(w, it, ""); - write!( - w, - "{}enum {}{}", - visibility_print_with_space(it.visibility(tcx), it.item_id, cx), - it.name.unwrap(), - e.generics.print(cx), - ); - if !print_where_clause_and_check(w, &e.generics, cx) { - // If there wasn't a `where` clause, we add a whitespace. - w.write_str(" "); - } + wrap_item(w, |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "{}enum {}{}", + visibility_print_with_space(it.visibility(tcx), it.item_id, cx), + it.name.unwrap(), + e.generics.print(cx), + ); + if !print_where_clause_and_check(w, &e.generics, cx) { + // If there wasn't a `where` clause, we add a whitespace. + w.write_str(" "); + } - let variants_stripped = e.has_stripped_entries(); - if count_variants == 0 && !variants_stripped { - w.write_str("{}"); - } else { - w.write_str("{\n"); - let toggle = should_hide_fields(count_variants); - if toggle { - toggle_open(w, format_args!("{} variants", count_variants)); - } - for v in e.variants() { - w.write_str(" "); - let name = v.name.unwrap(); - match *v.kind { - // FIXME(#101337): Show discriminant - clean::VariantItem(ref var) => match var.kind { - clean::VariantKind::CLike => write!(w, "{}", name), - clean::VariantKind::Tuple(ref s) => { - write!(w, "{}(", name); - print_tuple_struct_fields(w, cx, s); - w.write_str(")"); - } - clean::VariantKind::Struct(ref s) => { - render_struct(w, v, None, None, &s.fields, " ", false, cx); - } - }, - _ => unreachable!(), - } - w.write_str(",\n"); + let variants_stripped = e.has_stripped_entries(); + if count_variants == 0 && !variants_stripped { + w.write_str("{}"); + } else { + w.write_str("{\n"); + let toggle = should_hide_fields(count_variants); + if toggle { + toggle_open(w, format_args!("{} variants", count_variants)); + } + for v in e.variants() { + w.write_str(" "); + let name = v.name.unwrap(); + match *v.kind { + // FIXME(#101337): Show discriminant + clean::VariantItem(ref var) => match var.kind { + clean::VariantKind::CLike => write!(w, "{}", name), + clean::VariantKind::Tuple(ref s) => { + write!(w, "{}(", name); + print_tuple_struct_fields(w, cx, s); + w.write_str(")"); + } + clean::VariantKind::Struct(ref s) => { + render_struct(w, v, None, None, &s.fields, " ", false, cx); + } + }, + _ => unreachable!(), } + w.write_str(",\n"); + } - if variants_stripped { - w.write_str(" // some variants omitted\n"); - } - if toggle { - toggle_close(w); - } - w.write_str("}"); + if variants_stripped { + w.write_str(" // some variants omitted\n"); } - }); + if toggle { + toggle_close(w); + } + w.write_str("}"); + } }); document(w, cx, it, None, HeadingOffset::H2); @@ -1266,7 +1240,6 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w, "
    \ §", - id = id, ); render_stability_since_raw_with_extra( w, @@ -1304,8 +1277,11 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: if let Some((heading, fields)) = heading_and_fields { let variant_id = cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap())); - write!(w, "
    ", id = variant_id); - write!(w, "

    {heading}

    ", heading = heading); + write!( + w, + "
    \ +

    {heading}

    ", + ); document_non_exhaustive(w, variant); for field in fields { match *field.kind { @@ -1321,9 +1297,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: "
    \ \ §\ - {f}: {t}\ + {f}: {t}\ ", - id = id, f = field.name.unwrap(), t = ty.print(cx) ); @@ -1346,38 +1321,30 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: } fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { - wrap_into_item_decl(w, |w| { - highlight::render_macro_with_highlighting(&t.source, w); - }); + highlight::render_item_decl_with_highlighting(&t.source, w); document(w, cx, it, None, HeadingOffset::H2) } fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { - wrap_into_item_decl(w, |w| { + wrap_item(w, |w| { let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - wrap_item(w, |w| { - write!(w, "{}!() {{ /* proc-macro */ }}", name); - }); + write!(w, "{}!() {{ /* proc-macro */ }}", name); } MacroKind::Attr => { - wrap_item(w, |w| { - write!(w, "#[{}]", name); - }); + write!(w, "#[{}]", name); } MacroKind::Derive => { - wrap_item(w, |w| { - write!(w, "#[derive({})]", name); - if !m.helpers.is_empty() { - w.push_str("\n{\n"); - w.push_str(" // Attributes available to this derive:\n"); - for attr in &m.helpers { - writeln!(w, " #[{}]", attr); - } - w.push_str("}\n"); + write!(w, "#[derive({})]", name); + if !m.helpers.is_empty() { + w.push_str("\n{\n"); + w.push_str(" // Attributes available to this derive:\n"); + for attr in &m.helpers { + writeln!(w, " #[{}]", attr); } - }); + w.push_str("}\n"); + } } } }); @@ -1400,61 +1367,57 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { } fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) { - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - let tcx = cx.tcx(); - render_attributes_in_code(w, it); + wrap_item(w, |w| { + let tcx = cx.tcx(); + render_attributes_in_code(w, it); - write!( - w, - "{vis}const {name}: {typ}", - vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), - name = it.name.unwrap(), - typ = c.type_.print(cx), - ); + write!( + w, + "{vis}const {name}: {typ}", + vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), + name = it.name.unwrap(), + typ = c.type_.print(cx), + ); - // FIXME: The code below now prints - // ` = _; // 100i32` - // if the expression is - // `50 + 50` - // which looks just wrong. - // Should we print - // ` = 100i32;` - // instead? - - let value = c.value(tcx); - let is_literal = c.is_literal(tcx); - let expr = c.expr(tcx); - if value.is_some() || is_literal { - write!(w, " = {expr};", expr = Escape(&expr)); - } else { - w.write_str(";"); - } + // FIXME: The code below now prints + // ` = _; // 100i32` + // if the expression is + // `50 + 50` + // which looks just wrong. + // Should we print + // ` = 100i32;` + // instead? + + let value = c.value(tcx); + let is_literal = c.is_literal(tcx); + let expr = c.expr(tcx); + if value.is_some() || is_literal { + write!(w, " = {expr};", expr = Escape(&expr)); + } else { + w.write_str(";"); + } - if !is_literal { - if let Some(value) = &value { - let value_lowercase = value.to_lowercase(); - let expr_lowercase = expr.to_lowercase(); + if !is_literal { + if let Some(value) = &value { + let value_lowercase = value.to_lowercase(); + let expr_lowercase = expr.to_lowercase(); - if value_lowercase != expr_lowercase - && value_lowercase.trim_end_matches("i32") != expr_lowercase - { - write!(w, " // {value}", value = Escape(value)); - } + if value_lowercase != expr_lowercase + && value_lowercase.trim_end_matches("i32") != expr_lowercase + { + write!(w, " // {value}", value = Escape(value)); } } - }); + } }); document(w, cx, it, None, HeadingOffset::H2) } fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) { - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - render_attributes_in_code(w, it); - render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx); - }); + wrap_item(w, |w| { + render_attributes_in_code(w, it); + render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx); }); document(w, cx, it, None, HeadingOffset::H2); @@ -1486,11 +1449,9 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean w, "\ §\ - {name}: {ty}\ + {field_name}: {ty}\ ", item_type = ItemType::StructField, - id = id, - name = field_name, ty = ty.print(cx) ); document(w, cx, field, Some(it), HeadingOffset::H3); @@ -1503,34 +1464,30 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - render_attributes_in_code(w, it); - write!( - w, - "{vis}static {mutability}{name}: {typ}", - vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), - mutability = s.mutability.print_with_space(), - name = it.name.unwrap(), - typ = s.type_.print(cx) - ); - }); + wrap_item(w, |w| { + render_attributes_in_code(w, it); + write!( + w, + "{vis}static {mutability}{name}: {typ}", + vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), + mutability = s.mutability.print_with_space(), + name = it.name.unwrap(), + typ = s.type_.print(cx) + ); }); document(w, cx, it, None, HeadingOffset::H2) } fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { - wrap_into_item_decl(w, |w| { - wrap_item(w, |w| { - w.write_str("extern {\n"); - render_attributes_in_code(w, it); - write!( - w, - " {}type {};\n}}", - visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), - it.name.unwrap(), - ); - }); + wrap_item(w, |w| { + w.write_str("extern {\n"); + render_attributes_in_code(w, it); + write!( + w, + " {}type {};\n}}", + visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), + it.name.unwrap(), + ); }); document(w, cx, it, None, HeadingOffset::H2); @@ -1609,20 +1566,11 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) bounds } -fn wrap_into_item_decl(w: &mut Buffer, f: F) -where - F: FnOnce(&mut Buffer), -{ - w.write_str("
    "); - f(w); - w.write_str("
    ") -} - fn wrap_item(w: &mut Buffer, f: F) where F: FnOnce(&mut Buffer), { - w.write_str(r#"
    "#);
    +    w.write_str(r#"
    "#);
         f(w);
         w.write_str("
    "); } @@ -1677,7 +1625,6 @@ fn render_union( it: &clean::Item, g: Option<&clean::Generics>, fields: &[clean::Item], - tab: &str, cx: &Context<'_>, ) { let tcx = cx.tcx(); @@ -1700,7 +1647,7 @@ fn render_union( w.write_str(" "); } - write!(w, "{{\n{}", tab); + write!(w, "{{\n"); let count_fields = fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count(); let toggle = should_hide_fields(count_fields); @@ -1712,17 +1659,16 @@ fn render_union( if let clean::StructFieldItem(ref ty) = *field.kind { write!( w, - " {}{}: {},\n{}", + " {}{}: {},\n", visibility_print_with_space(field.visibility(tcx), field.item_id, cx), field.name.unwrap(), - ty.print(cx), - tab + ty.print(cx) ); } } if it.has_stripped_entries().unwrap() { - write!(w, " /* private fields */\n{}", tab); + write!(w, " /* private fields */\n"); } if toggle { toggle_close(w); @@ -1887,12 +1833,12 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { } fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { - fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS, tag_size: u64) { + fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS, tag_size: u64) { if layout.abi.is_unsized() { write!(w, "(unsized)"); } else { - let bytes = layout.size.bytes() - tag_size; - write!(w, "{size} byte{pl}", size = bytes, pl = if bytes == 1 { "" } else { "s" },); + let size = layout.size.bytes() - tag_size; + write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" },); } } @@ -1909,7 +1855,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { let tcx = cx.tcx(); let param_env = tcx.param_env(ty_def_id); - let ty = tcx.type_of(ty_def_id); + let ty = tcx.type_of(ty_def_id).subst_identity(); match tcx.layout_of(param_env.and(ty)) { Ok(ty_layout) => { writeln!( @@ -1947,7 +1893,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { for (index, layout) in variants.iter_enumerated() { let name = adt.variant(index).name; - write!(w, "
  • {name}: ", name = name); + write!(w, "
  • {name}: "); write_size_of_layout(w, layout, tag_size); writeln!(w, "
  • "); } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 5b0caac09..090ea2cb1 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -236,7 +236,16 @@ pub(crate) fn build_index<'tcx>( crate_data.serialize_field("doc", &self.doc)?; crate_data.serialize_field( "t", - &self.items.iter().map(|item| &item.ty).collect::>(), + &self + .items + .iter() + .map(|item| { + let n = item.ty as u8; + let c = char::try_from(n + b'A').expect("item types must fit in ASCII"); + assert!(c <= 'z', "item types must fit within ASCII printables"); + c + }) + .collect::(), )?; crate_data.serialize_field( "n", diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index bc8badad3..54749e9a3 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; -use super::{collect_paths_for_type, ensure_trailing_slash, Context, BASIC_KEYWORDS}; +use super::{collect_paths_for_type, ensure_trailing_slash, Context}; use crate::clean::Crate; use crate::config::{EmitType, RenderOptions}; use crate::docfs::PathError; @@ -138,7 +138,7 @@ pub(super) fn write_shared( Ok((ret, krates)) } - /// Read a file and return all lines that match the "{crate}":{data},\ format, + /// Read a file and return all lines that match the "{crate}":{data},\ format, /// and return a tuple `(Vec, Vec)`. /// /// This forms the payload of files that look like this: @@ -340,7 +340,6 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; root_path: "./", static_root_path: shared.static_root_path.as_deref(), description: "List of crates", - keywords: BASIC_KEYWORDS, resource_suffix: &shared.resource_suffix, }; diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 799c497d1..2c90bf4fa 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -4,7 +4,7 @@ use crate::error::Error; use crate::html::format::Buffer; use crate::html::highlight; use crate::html::layout; -use crate::html::render::{Context, BASIC_KEYWORDS}; +use crate::html::render::Context; use crate::visit::DocVisitor; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -228,7 +228,6 @@ impl SourceCollector<'_, '_> { root_path: &root_path, static_root_path: shared.static_root_path.as_deref(), description: &desc, - keywords: BASIC_KEYWORDS, resource_suffix: &shared.resource_suffix, }; let v = layout::render( diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index fcd925bb3..1a34530c2 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -90,7 +90,6 @@ module.exports = { "no-return-assign": "error", "no-script-url": "error", "no-sequences": "error", - "no-throw-literal": "error", "no-div-regex": "error", } }; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index a93f60da2..95528e70e 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1,3 +1,11 @@ +/* When static files are updated, their suffixes need to be updated. + 1. In the top directory run: + ./x.py doc --stage 1 library/core + 2. Find the directory containing files named with updated suffixes: + find build -path '*'/stage1-std/'*'/static.files + 3. Copy the filenames with updated suffixes from the directory. +*/ + /* See FiraSans-LICENSE.txt for the Fira Sans license. */ @font-face { font-family: 'Fira Sans'; @@ -22,7 +30,7 @@ font-style: normal; font-weight: 400; src: local('Source Serif 4'), - url("SourceSerif4-Regular-1f7d512b176f0f72.ttf.woff2") format("woff2"); + url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2"); font-display: swap; } @font-face { @@ -30,7 +38,7 @@ font-style: italic; font-weight: 400; src: local('Source Serif 4 Italic'), - url("SourceSerif4-It-d034fe4ef9d0fa00.ttf.woff2") format("woff2"); + url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2"); font-display: swap; } @font-face { @@ -38,7 +46,7 @@ font-style: normal; font-weight: 700; src: local('Source Serif 4 Bold'), - url("SourceSerif4-Bold-124a1ca42af929b6.ttf.woff2") format("woff2"); + url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2"); font-display: swap; } @@ -166,6 +174,14 @@ h1, h2, h3, h4 { .top-doc .docblock > h4 { border-bottom: 1px solid var(--headings-border-bottom-color); } +/* while line-height 1.5 is required for any "block of text", + which WCAG defines as more than one sentence, it looks weird for + very large main headers */ +h1, h2 { + line-height: 1.25; + padding-top: 3px; + padding-bottom: 9px; +} h3.code-header { font-size: 1.125rem; /* 18px */ } @@ -176,6 +192,7 @@ h4.code-header { font-weight: 600; margin: 0; padding: 0; + white-space: pre-wrap; } #crate-search, @@ -184,7 +201,7 @@ h1, h2, h3, h4, h5, h6, .mobile-topbar, .search-input, .search-results .result-name, -.item-left > a, +.item-name > a, .out-of-band, span.since, a.srclink, @@ -335,7 +352,7 @@ pre { padding: 14px; line-height: 1.5; /* https://github.com/rust-lang/rust/issues/105906 */ } -.item-decl pre { +pre.item-decl { overflow-x: auto; } /* This rule allows to have scrolling on the X axis. */ @@ -533,7 +550,7 @@ ul.block, .block li { .rustdoc .example-wrap > pre { margin: 0; flex-grow: 1; - overflow-x: auto; + overflow: auto hidden; } .rustdoc .example-wrap > pre.example-line-numbers, @@ -634,6 +651,7 @@ pre, .rustdoc.source .example-wrap { .fn .where, .where.fmt-newline { display: block; + white-space: pre-wrap; font-size: 0.875rem; } @@ -697,8 +715,8 @@ h2.small-section-header > .anchor { .main-heading a:hover, .example-wrap > pre.rust a:hover, .all-items a:hover, -.docblock a:not(.test-arrow):not(.scrape-help):hover, -.docblock-short a:not(.test-arrow):not(.scrape-help):hover, +.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover, +.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover, .item-info a { text-decoration: underline; } @@ -732,14 +750,16 @@ table, .item-table { display: table; + padding: 0; + margin: 0; } -.item-row { +.item-table > li { display: table-row; } -.item-left, .item-right { +.item-table > li > div { display: table-cell; } -.item-left { +.item-table > li > .item-name { padding-right: 1.25rem; } @@ -806,8 +826,11 @@ so that we can apply CSS-filters to change the arrow color in themes */ background-repeat: no-repeat; background-size: 20px; background-position: calc(100% - 2px) 56%; - /* image is black color */ - background-image: url("down-arrow-927217e04c7463ac.svg"); + /* down arrow (image is black color) */ + background-image: url('data:image/svg+xml, \ + '); /* changes the arrow image color */ filter: var(--crate-search-div-filter); } @@ -941,7 +964,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ padding: 3px; margin-bottom: 5px; } -.item-left .stab { +.item-name .stab { margin-left: 0.3125em; } .stab { @@ -977,11 +1000,6 @@ so that we can apply CSS-filters to change the arrow color in themes */ 0 -1px 0 black; } -.module-item.unstable, -.import-item.unstable { - opacity: 0.65; -} - .since { font-weight: normal; font-size: initial; @@ -1093,44 +1111,8 @@ pre.rust .doccomment { display: block; left: -25px; top: 5px; -} - -.example-wrap .tooltip:hover::after { - padding: 5px 3px 3px 3px; - border-radius: 6px; - margin-left: 5px; - font-size: 1rem; - border: 1px solid var(--border-color); - position: absolute; - width: max-content; - top: -2px; - z-index: 1; - background-color: var(--tooltip-background-color); - color: var(--tooltip-color); -} - -.example-wrap .tooltip:hover::before { - content: " "; - position: absolute; - top: 50%; - left: 16px; - margin-top: -5px; - z-index: 1; - border: 5px solid transparent; - border-right-color: var(--tooltip-background-color); -} - -.example-wrap.ignore .tooltip:hover::after { - content: "This example is not tested"; -} -.example-wrap.compile_fail .tooltip:hover::after { - content: "This example deliberately fails to compile"; -} -.example-wrap.should_panic .tooltip:hover::after { - content: "This example panics"; -} -.example-wrap.edition .tooltip:hover::after { - content: "This code runs with edition " attr(data-edition); + margin: 0; + line-height: 1; } .example-wrap.compile_fail .tooltip, @@ -1168,6 +1150,7 @@ a.test-arrow:hover { .item-spacer { width: 100%; height: 12px; + display: block; } .out-of-band > span.since { @@ -1196,7 +1179,7 @@ a.test-arrow:hover { border-right: 3px solid var(--target-border-color); } -.notable-traits { +.code-header a.tooltip { color: inherit; margin-right: 15px; position: relative; @@ -1205,7 +1188,7 @@ a.test-arrow:hover { /* placeholder thunk so that the mouse can easily travel from "(i)" to popover the resulting "hover tunnel" is a stepped triangle, approximating https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown */ -.notable-traits:hover::after { +a.tooltip:hover::after { position: absolute; top: calc(100% - 10px); left: -15px; @@ -1214,11 +1197,11 @@ a.test-arrow:hover { content: "\00a0"; } -.notable .content { +.popover.tooltip .content { margin: 0.25em 0.5em; } -.notable .content pre, .notable .content code { +.popover.tooltip .content pre, .popover.tooltip .content code { background: transparent; margin: 0; padding: 0; @@ -1226,7 +1209,7 @@ a.test-arrow:hover { white-space: pre-wrap; } -.notable .content > h3:first-child { +.popover.tooltip .content > h3:first-child { margin: 0 0 5px 0; } @@ -1263,12 +1246,25 @@ a.test-arrow:hover { line-height: 1.5; color: inherit; } +#search-tabs button:not(.selected) { + background-color: var(--search-tab-button-not-selected-background); + border-top-color: var(--search-tab-button-not-selected-border-top-color); +} +#search-tabs button:hover, #search-tabs button.selected { + background-color: var(--search-tab-button-selected-background); + border-top-color: var(--search-tab-button-selected-border-top-color); +} #search-tabs .count { font-size: 1rem; color: var(--search-tab-title-count-color); } +#search .error code { + border-radius: 3px; + background-color: var(--search-error-code-background-color); +} + #src-sidebar-toggle { position: sticky; top: 0; @@ -1433,7 +1429,10 @@ details.toggle > summary.hideme > span { } details.toggle > summary::before { - background: url("toggle-plus-1092eb4930d581b0.svg") no-repeat top left; + /* toggle plus */ + background: url('data:image/svg+xml,') no-repeat top left; content: ""; cursor: pointer; width: 16px; @@ -1511,7 +1510,10 @@ details.toggle[open] > summary.hideme > span { } details.toggle[open] > summary::before { - background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left; + /* toggle minus */ + background: url('data:image/svg+xml,') no-repeat top left; } details.toggle[open] > summary::after { @@ -1700,7 +1702,7 @@ in storage.js } /* Display an alternating layout on tablets and phones */ - .item-table, .item-row, .item-left, .item-right, + .item-table, .item-row, .item-table > li, .item-table > li > div, .search-results > a, .search-results > a > div { display: block; } @@ -1709,7 +1711,7 @@ in storage.js .search-results > a { padding: 5px 0px; } - .search-results > a > div.desc, .item-right { + .search-results > a > div.desc, .item-table > li > div.desc { padding-left: 2em; } @@ -1897,19 +1899,25 @@ in storage.js right: 0.25em; } -.scraped-example:not(.expanded) .code-wrapper:before, -.scraped-example:not(.expanded) .code-wrapper:after { +.scraped-example:not(.expanded) .code-wrapper::before, +.scraped-example:not(.expanded) .code-wrapper::after { content: " "; width: 100%; height: 5px; position: absolute; z-index: 1; } -.scraped-example:not(.expanded) .code-wrapper:before { +.scraped-example:not(.expanded) .code-wrapper::before { top: 0; + background: linear-gradient(to bottom, + var(--scrape-example-code-wrapper-background-start), + var(--scrape-example-code-wrapper-background-end)); } -.scraped-example:not(.expanded) .code-wrapper:after { +.scraped-example:not(.expanded) .code-wrapper::after { bottom: 0; + background: linear-gradient(to top, + var(--scrape-example-code-wrapper-background-start), + var(--scrape-example-code-wrapper-background-end)); } .scraped-example .code-wrapper .example-wrap { diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css index 4e9803fe2..920f45c4b 100644 --- a/src/librustdoc/html/static/css/settings.css +++ b/src/librustdoc/html/static/css/settings.css @@ -3,8 +3,7 @@ position: relative; } -.setting-line .radio-line input, -.setting-line .settings-toggle input { +.setting-radio input, .setting-check input { margin-right: 0.3em; height: 1.2rem; width: 1.2rem; @@ -14,21 +13,20 @@ -webkit-appearance: none; cursor: pointer; } -.setting-line .radio-line input { +.setting-radio input { border-radius: 50%; } -.setting-line .settings-toggle input:checked { +.setting-check input:checked { content: url('data:image/svg+xml,\ \ '); } -.setting-line .radio-line input + span, -.setting-line .settings-toggle span { +.setting-radio span, .setting-check span { padding-bottom: 1px; } -.radio-line .choice { +.setting-radio { margin-top: 0.1em; margin-bottom: 0.1em; min-width: 3.8em; @@ -37,36 +35,32 @@ align-items: center; cursor: pointer; } -.radio-line .choice + .choice { +.setting-radio + .setting-radio { margin-left: 0.5em; } -.settings-toggle { - position: relative; - width: 100%; +.setting-check { margin-right: 20px; display: flex; align-items: center; cursor: pointer; } -.setting-line .radio-line input:checked { +.setting-radio input:checked { box-shadow: inset 0 0 0 3px var(--main-background-color); background-color: var(--settings-input-color); } -.setting-line .settings-toggle input:checked { +.setting-check input:checked { background-color: var(--settings-input-color); } -.setting-line .radio-line input:focus, -.setting-line .settings-toggle input:focus { +.setting-radio input:focus, .setting-check input:focus { box-shadow: 0 0 1px 1px var(--settings-input-color); } /* In here we combine both `:focus` and `:checked` properties. */ -.setting-line .radio-line input:checked:focus { +.setting-radio input:checked:focus { box-shadow: inset 0 0 0 3px var(--main-background-color), 0 0 2px 2px var(--settings-input-color); } -.setting-line .radio-line input:hover, -.setting-line .settings-toggle input:hover { +.setting-radio input:hover, .setting-check input:hover { border-color: var(--settings-input-color) !important; } diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 979e7e0f9..90cf689ad 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -43,9 +43,14 @@ Original by Dempfi (https://github.com/dempfi/ayu) --search-result-link-focus-background-color: #3c3c3c; --search-result-border-color: #aaa3; --search-color: #fff; + --search-error-code-background-color: #4f4c4c; --search-results-alias-color: #c5c5c5; --search-results-grey-color: #999; --search-tab-title-count-color: #888; + --search-tab-button-not-selected-border-top-color: none; + --search-tab-button-not-selected-background: transparent !important; + --search-tab-button-selected-border-top-color: none; + --search-tab-button-selected-background: #141920 !important; --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ff7733; @@ -70,8 +75,6 @@ Original by Dempfi (https://github.com/dempfi/ayu) --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368); --target-background-color: rgba(255, 236, 164, 0.06); --target-border-color: rgba(255, 180, 76, 0.85); - --tooltip-background-color: #314559; - --tooltip-color: #c5c5c5; --kbd-color: #c5c5c5; --kbd-background: #314559; --kbd-box-shadow-color: #5c6773; @@ -97,12 +100,13 @@ Original by Dempfi (https://github.com/dempfi/ayu) --scrape-example-help-color: #eee; --scrape-example-help-hover-border-color: #fff; --scrape-example-help-hover-color: #fff; + --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1); + --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0); } -h1, h2, h3, h4 { - color: white; -} -h1 a { +h1, h2, h3, h4, +h1 a, .sidebar h2 a, .sidebar h3 a, +#source-sidebar > .title { color: #fff; } h4 { @@ -112,24 +116,22 @@ h4 { .docblock code { color: #ffb454; } -.code-header { - color: #e6e1cf; -} -.docblock pre > code, pre > code { - color: #e6e1cf; -} -.item-info code { - color: #e6e1cf; -} .docblock a > code { color: #39AFD7 !important; } -pre, .rustdoc.source .example-wrap { +.code-header, +.docblock pre > code, +pre, pre > code, +.item-info code, +.rustdoc.source .example-wrap { color: #e6e1cf; } .sidebar .current, -.sidebar a:hover { +.sidebar a:hover, +#source-sidebar div.files > a:hover, details.dir-entry summary:hover, +#source-sidebar div.files > a:focus, details.dir-entry summary:focus, +#source-sidebar div.files > a.selected { color: #ffb44c; } @@ -143,15 +145,12 @@ pre, .rustdoc.source .example-wrap { border-right: 1px solid #ffb44c; } -.search-results a:hover { - color: #fff !important; - background-color: #3c3c3c; -} - +.search-results a:hover, .search-results a:focus { color: #fff !important; background-color: #3c3c3c; } + .search-results a { color: #0096cf; } @@ -159,54 +158,22 @@ pre, .rustdoc.source .example-wrap { color: #c5c5c5; } -.sidebar h2 a, -.sidebar h3 a { - color: white; -} - .result-name .primitive > i, .result-name .keyword > i { color: #788797; } #search-tabs > button.selected { - background-color: #141920 !important; border-bottom: 1px solid #ffb44c !important; border-top: none; } - #search-tabs > button:not(.selected) { - background-color: transparent !important; border: none; + background-color: transparent !important; } - #search-tabs > button:hover { border-bottom: 1px solid rgba(242, 151, 24, 0.3); } -/* rules that this theme does not need to set, here to satisfy the rule checker */ -/* note that a lot of these are partially set in some way (meaning they are set -individually rather than as a group) */ -/* FIXME: these rules should be at the bottom of the file but currently must be -above the `@media (max-width: 700px)` rules due to a bug in the css checker */ -/* see https://github.com/rust-lang/rust/pull/71237#issuecomment-618170143 */ -#search-tabs > button:hover, #search-tabs > button.selected {} - #settings-menu > a img { filter: invert(100); } - -#source-sidebar > .title { - color: #fff; -} -#source-sidebar div.files > a:hover, details.dir-entry summary:hover, -#source-sidebar div.files > a:focus, details.dir-entry summary:focus, -#source-sidebar div.files > a.selected { - color: #ffb44c; -} - -.scraped-example:not(.expanded) .code-wrapper::before { - background: linear-gradient(to bottom, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0)); -} -.scraped-example:not(.expanded) .code-wrapper::after { - background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0)); -} diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index fb15863b0..e8cd06931 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -38,9 +38,14 @@ --search-result-link-focus-background-color: #616161; --search-result-border-color: #aaa3; --search-color: #111; + --search-error-code-background-color: #484848; --search-results-alias-color: #fff; --search-results-grey-color: #ccc; --search-tab-title-count-color: #888; + --search-tab-button-not-selected-border-top-color: #252525; + --search-tab-button-not-selected-background: #252525; + --search-tab-button-selected-border-top-color: #0089ff; + --search-tab-button-selected-background: #353535; --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ab8ac1; @@ -65,8 +70,6 @@ --test-arrow-hover-background-color: #4e8bca; --target-background-color: #494a3d; --target-border-color: #bb7410; - --tooltip-background-color: #000; - --tooltip-color: #fff; --kbd-color: #000; --kbd-background: #fafbfc; --kbd-box-shadow-color: #c6cbd1; @@ -92,21 +95,6 @@ --scrape-example-help-color: #eee; --scrape-example-help-hover-border-color: #fff; --scrape-example-help-hover-color: #fff; -} - -#search-tabs > button:not(.selected) { - background-color: #252525; - border-top-color: #252525; -} - -#search-tabs > button:hover, #search-tabs > button.selected { - border-top-color: #0089ff; - background-color: #353535; -} - -.scraped-example:not(.expanded) .code-wrapper::before { - background: linear-gradient(to bottom, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0)); -} -.scraped-example:not(.expanded) .code-wrapper::after { - background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0)); + --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1); + --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); } diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 053fa78d1..5e3f14e48 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -38,9 +38,14 @@ --search-result-link-focus-background-color: #ccc; --search-result-border-color: #aaa3; --search-color: #000; + --search-error-code-background-color: #d0cccc; --search-results-alias-color: #000; --search-results-grey-color: #999; --search-tab-title-count-color: #888; + --search-tab-button-not-selected-border-top-color: #e6e6e6; + --search-tab-button-not-selected-background: #e6e6e6; + --search-tab-button-selected-border-top-color: #0089ff; + --search-tab-button-selected-background: #ffffff; --stab-background-color: #fff5d6; --stab-code-color: #000; --code-highlight-kw-color: #8959a8; @@ -65,8 +70,6 @@ --test-arrow-hover-background-color: #4e8bca; --target-background-color: #fdffd3; --target-border-color: #ad7c37; - --tooltip-background-color: #000; - --tooltip-color: #fff; --kbd-color: #000; --kbd-background: #fafbfc; --kbd-box-shadow-color: #c6cbd1; @@ -89,21 +92,6 @@ --scrape-example-help-color: #333; --scrape-example-help-hover-border-color: #000; --scrape-example-help-hover-color: #000; -} - -#search-tabs > button:not(.selected) { - background-color: #e6e6e6; - border-top-color: #e6e6e6; -} - -#search-tabs > button:hover, #search-tabs > button.selected { - background-color: #ffffff; - border-top-color: #0089ff; -} - -.scraped-example:not(.expanded) .code-wrapper::before { - background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); -} -.scraped-example:not(.expanded) .code-wrapper::after { - background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); + --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1); + --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); } diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff2 index db57d2145..181a07f63 100644 Binary files a/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff2 and b/src/librustdoc/html/static/fonts/SourceSerif4-Bold.ttf.woff2 differ diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff2 index 1cbc021a3..2ae08a7be 100644 Binary files a/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff2 and b/src/librustdoc/html/static/fonts/SourceSerif4-It.ttf.woff2 differ diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md index 68ea18924..5871e1f3d 100644 --- a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md +++ b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md @@ -1,4 +1,4 @@ -Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff2 index 2db73fe2b..0263fc304 100644 Binary files a/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff2 and b/src/librustdoc/html/static/fonts/SourceSerif4-Regular.ttf.woff2 differ diff --git a/src/librustdoc/html/static/images/down-arrow.svg b/src/librustdoc/html/static/images/down-arrow.svg deleted file mode 100644 index 5d76a64e9..000000000 --- a/src/librustdoc/html/static/images/down-arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/librustdoc/html/static/images/toggle-minus.svg b/src/librustdoc/html/static/images/toggle-minus.svg deleted file mode 100644 index 73154788a..000000000 --- a/src/librustdoc/html/static/images/toggle-minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/librustdoc/html/static/images/toggle-plus.svg b/src/librustdoc/html/static/images/toggle-plus.svg deleted file mode 100644 index 08b17033e..000000000 --- a/src/librustdoc/html/static/images/toggle-plus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/librustdoc/html/static/images/wheel.svg b/src/librustdoc/html/static/images/wheel.svg index 01da3b24c..83c07f63d 100644 --- a/src/librustdoc/html/static/images/wheel.svg +++ b/src/librustdoc/html/static/images/wheel.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 604ab147f..5e8c0e8d1 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -180,7 +180,6 @@ function browserSupportsHistoryApi() { return window.history && typeof window.history.pushState === "function"; } -// eslint-disable-next-line no-unused-vars function loadCss(cssUrl) { const link = document.createElement("link"); link.href = cssUrl; @@ -379,7 +378,7 @@ function loadCss(cssUrl) { } ev.preventDefault(); searchState.defocus(); - window.hideAllModals(true); // true = reset focus for notable traits + window.hideAllModals(true); // true = reset focus for tooltips } function handleShortcut(ev) { @@ -456,10 +455,7 @@ function loadCss(cssUrl) { const ul = document.createElement("ul"); ul.className = "block " + shortty; - for (const item of filtered) { - const name = item[0]; - const desc = item[1]; // can be null - + for (const name of filtered) { let path; if (shortty === "mod") { path = name + "/index.html"; @@ -469,7 +465,6 @@ function loadCss(cssUrl) { const current_page = document.location.href.split("/").pop(); const link = document.createElement("a"); link.href = path; - link.title = desc; if (path === current_page) { link.className = "current"; } @@ -789,17 +784,17 @@ function loadCss(cssUrl) { // we need to switch away from mobile mode and make the main content area scrollable. hideSidebar(); } - if (window.CURRENT_NOTABLE_ELEMENT) { - // As a workaround to the behavior of `contains: layout` used in doc togglers, the - // notable traits popup is positioned using javascript. + if (window.CURRENT_TOOLTIP_ELEMENT) { + // As a workaround to the behavior of `contains: layout` used in doc togglers, + // tooltip popovers are positioned using javascript. // // This means when the window is resized, we need to redo the layout. - const base = window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE; - const force_visible = base.NOTABLE_FORCE_VISIBLE; - hideNotable(false); + const base = window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE; + const force_visible = base.TOOLTIP_FORCE_VISIBLE; + hideTooltip(false); if (force_visible) { - showNotable(base); - base.NOTABLE_FORCE_VISIBLE = true; + showTooltip(base); + base.TOOLTIP_FORCE_VISIBLE = true; } } }); @@ -827,27 +822,35 @@ function loadCss(cssUrl) { }); }); - function showNotable(e) { - if (!window.NOTABLE_TRAITS) { + function showTooltip(e) { + const notable_ty = e.getAttribute("data-notable-ty"); + if (!window.NOTABLE_TRAITS && notable_ty) { const data = document.getElementById("notable-traits-data"); if (data) { window.NOTABLE_TRAITS = JSON.parse(data.innerText); } else { - throw new Error("showNotable() called on page without any notable traits!"); + throw new Error("showTooltip() called with notable without any notable traits!"); } } - if (window.CURRENT_NOTABLE_ELEMENT && window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE === e) { + if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) { // Make this function idempotent. return; } window.hideAllModals(false); - const ty = e.getAttribute("data-ty"); const wrapper = document.createElement("div"); - wrapper.innerHTML = "
    " + window.NOTABLE_TRAITS[ty] + "
    "; - wrapper.className = "notable popover"; + if (notable_ty) { + wrapper.innerHTML = "
    " + + window.NOTABLE_TRAITS[notable_ty] + "
    "; + } else if (e.getAttribute("title") !== undefined) { + const titleContent = document.createElement("div"); + titleContent.className = "content"; + titleContent.appendChild(document.createTextNode(e.getAttribute("title"))); + wrapper.appendChild(titleContent); + } + wrapper.className = "tooltip popover"; const focusCatcher = document.createElement("div"); focusCatcher.setAttribute("tabindex", "0"); - focusCatcher.onfocus = hideNotable; + focusCatcher.onfocus = hideTooltip; wrapper.appendChild(focusCatcher); const pos = e.getBoundingClientRect(); // 5px overlap so that the mouse can easily travel from place to place @@ -869,62 +872,62 @@ function loadCss(cssUrl) { ); } wrapper.style.visibility = ""; - window.CURRENT_NOTABLE_ELEMENT = wrapper; - window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE = e; + window.CURRENT_TOOLTIP_ELEMENT = wrapper; + window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e; wrapper.onpointerleave = function(ev) { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } - if (!e.NOTABLE_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) { - hideNotable(true); + if (!e.TOOLTIP_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) { + hideTooltip(true); } }; } - function notableBlurHandler(event) { - if (window.CURRENT_NOTABLE_ELEMENT && - !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT) && - !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT) && - !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE) && - !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE) + function tooltipBlurHandler(event) { + if (window.CURRENT_TOOLTIP_ELEMENT && + !elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT) && + !elemIsInParent(event.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT) && + !elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE) && + !elemIsInParent(event.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE) ) { // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari. - // When I click the button on an already-opened notable trait popover, Safari + // When I click the button on an already-opened tooltip popover, Safari // hides the popover and then immediately shows it again, while everyone else hides it // and it stays hidden. // // To work around this, make sure the click finishes being dispatched before - // hiding the popover. Since `hideNotable()` is idempotent, this makes Safari behave + // hiding the popover. Since `hideTooltip()` is idempotent, this makes Safari behave // consistently with the other two. - setTimeout(() => hideNotable(false), 0); + setTimeout(() => hideTooltip(false), 0); } } - function hideNotable(focus) { - if (window.CURRENT_NOTABLE_ELEMENT) { - if (window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE) { + function hideTooltip(focus) { + if (window.CURRENT_TOOLTIP_ELEMENT) { + if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) { if (focus) { - window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.focus(); + window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus(); } - window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE = false; + window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false; } const body = document.getElementsByTagName("body")[0]; - body.removeChild(window.CURRENT_NOTABLE_ELEMENT); - window.CURRENT_NOTABLE_ELEMENT = null; + body.removeChild(window.CURRENT_TOOLTIP_ELEMENT); + window.CURRENT_TOOLTIP_ELEMENT = null; } } - onEachLazy(document.getElementsByClassName("notable-traits"), e => { + onEachLazy(document.getElementsByClassName("tooltip"), e => { e.onclick = function() { - this.NOTABLE_FORCE_VISIBLE = this.NOTABLE_FORCE_VISIBLE ? false : true; - if (window.CURRENT_NOTABLE_ELEMENT && !this.NOTABLE_FORCE_VISIBLE) { - hideNotable(true); + this.TOOLTIP_FORCE_VISIBLE = this.TOOLTIP_FORCE_VISIBLE ? false : true; + if (window.CURRENT_TOOLTIP_ELEMENT && !this.TOOLTIP_FORCE_VISIBLE) { + hideTooltip(true); } else { - showNotable(this); - window.CURRENT_NOTABLE_ELEMENT.setAttribute("tabindex", "0"); - window.CURRENT_NOTABLE_ELEMENT.focus(); - window.CURRENT_NOTABLE_ELEMENT.onblur = notableBlurHandler; + showTooltip(this); + window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0"); + window.CURRENT_TOOLTIP_ELEMENT.focus(); + window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler; } return false; }; @@ -933,16 +936,16 @@ function loadCss(cssUrl) { if (ev.pointerType !== "mouse") { return; } - showNotable(this); + showTooltip(this); }; e.onpointerleave = function(ev) { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } - if (!this.NOTABLE_FORCE_VISIBLE && - !elemIsInParent(ev.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) { - hideNotable(true); + if (!this.TOOLTIP_FORCE_VISIBLE && + !elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) { + hideTooltip(true); } }; }); @@ -1044,14 +1047,14 @@ function loadCss(cssUrl) { } /** - * Hide popover menus, notable trait tooltips, and the sidebar (if applicable). + * Hide popover menus, clickable tooltips, and the sidebar (if applicable). * - * Pass "true" to reset focus for notable traits. + * Pass "true" to reset focus for tooltip popovers. */ window.hideAllModals = function(switchFocus) { hideSidebar(); window.hidePopoverMenus(); - hideNotable(switchFocus); + hideTooltip(switchFocus); }; /** @@ -1142,7 +1145,11 @@ function loadCss(cssUrl) { (function() { let reset_button_timeout = null; - window.copy_path = but => { + const but = document.getElementById("copy-path"); + if (!but) { + return; + } + but.onclick = () => { const parent = but.parentElement; const path = []; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 88592fa0c..b98bced41 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -112,7 +112,6 @@ function levenshtein(s1, s2) { } function initSearch(rawSearchIndex) { - const MAX_LEV_DISTANCE = 3; const MAX_RESULTS = 200; const NO_TYPE_FILTER = -1; /** @@ -143,13 +142,11 @@ function initSearch(rawSearchIndex) { } function itemTypeFromName(typename) { - for (let i = 0, len = itemTypes.length; i < len; ++i) { - if (itemTypes[i] === typename) { - return i; - } + const index = itemTypes.findIndex(i => i === typename); + if (index < 0) { + throw ["Unknown type filter ", typename]; } - - throw new Error("Unknown type filter `" + typename + "`"); + return index; } /** @@ -167,21 +164,21 @@ function initSearch(rawSearchIndex) { */ function getStringElem(query, parserState, isInGenerics) { if (isInGenerics) { - throw new Error("`\"` cannot be used in generics"); + throw ["Unexpected ", "\"", " in generics"]; } else if (query.literalSearch) { - throw new Error("Cannot have more than one literal search element"); + throw ["Cannot have more than one literal search element"]; } else if (parserState.totalElems - parserState.genericsElems > 0) { - throw new Error("Cannot use literal search when there is more than one element"); + throw ["Cannot use literal search when there is more than one element"]; } parserState.pos += 1; const start = parserState.pos; const end = getIdentEndPosition(parserState); if (parserState.pos >= parserState.length) { - throw new Error("Unclosed `\"`"); + throw ["Unclosed ", "\""]; } else if (parserState.userQuery[end] !== "\"") { - throw new Error(`Unexpected \`${parserState.userQuery[end]}\` in a string element`); + throw ["Unexpected ", parserState.userQuery[end], " in a string element"]; } else if (start === end) { - throw new Error("Cannot have empty string element"); + throw ["Cannot have empty string element"]; } // To skip the quote at the end. parserState.pos += 1; @@ -260,7 +257,7 @@ function initSearch(rawSearchIndex) { return; } if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) { - throw new Error("You cannot have more than one element if you use quotes"); + throw ["You cannot have more than one element if you use quotes"]; } const pathSegments = name.split("::"); if (pathSegments.length > 1) { @@ -269,17 +266,17 @@ function initSearch(rawSearchIndex) { if (pathSegment.length === 0) { if (i === 0) { - throw new Error("Paths cannot start with `::`"); + throw ["Paths cannot start with ", "::"]; } else if (i + 1 === len) { - throw new Error("Paths cannot end with `::`"); + throw ["Paths cannot end with ", "::"]; } - throw new Error("Unexpected `::::`"); + throw ["Unexpected ", "::::"]; } } } // In case we only have something like `

    `, there is no name. if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) { - throw new Error("Found generics without a path"); + throw ["Found generics without a path"]; } parserState.totalElems += 1; if (isInGenerics) { @@ -303,22 +300,23 @@ function initSearch(rawSearchIndex) { * @return {integer} */ function getIdentEndPosition(parserState) { + const start = parserState.pos; let end = parserState.pos; - let foundExclamation = false; + let foundExclamation = -1; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; if (!isIdentCharacter(c)) { if (c === "!") { - if (foundExclamation) { - throw new Error("Cannot have more than one `!` in an ident"); + if (foundExclamation !== -1) { + throw ["Cannot have more than one ", "!", " in an ident"]; } else if (parserState.pos + 1 < parserState.length && isIdentCharacter(parserState.userQuery[parserState.pos + 1]) ) { - throw new Error("`!` can only be at the end of an ident"); + throw ["Unexpected ", "!", ": it can only be at the end of an ident"]; } - foundExclamation = true; + foundExclamation = parserState.pos; } else if (isErrorCharacter(c)) { - throw new Error(`Unexpected \`${c}\``); + throw ["Unexpected ", c]; } else if ( isStopCharacter(c) || isSpecialStartCharacter(c) || @@ -329,16 +327,40 @@ function initSearch(rawSearchIndex) { if (!isPathStart(parserState)) { break; } + if (foundExclamation !== -1) { + if (start <= (end - 2)) { + throw ["Cannot have associated items in macros"]; + } else { + // if start == end - 1, we got the never type + // while the never type has no associated macros, we still + // can parse a path like that + foundExclamation = -1; + } + } // Skip current ":". parserState.pos += 1; - foundExclamation = false; } else { - throw new Error(`Unexpected \`${c}\``); + throw ["Unexpected ", c]; } } parserState.pos += 1; end = parserState.pos; } + // if start == end - 1, we got the never type + if (foundExclamation !== -1 && start <= (end - 2)) { + if (parserState.typeFilter === null) { + parserState.typeFilter = "macro"; + } else if (parserState.typeFilter !== "macro") { + throw [ + "Invalid search type: macro ", + "!", + " and ", + parserState.typeFilter, + " both specified", + ]; + } + end = foundExclamation; + } return end; } @@ -365,9 +387,9 @@ function initSearch(rawSearchIndex) { parserState.userQuery[parserState.pos] === "<" ) { if (isInGenerics) { - throw new Error("Unexpected `<` after `<`"); + throw ["Unexpected ", "<", " after ", "<"]; } else if (start >= end) { - throw new Error("Found generics without a path"); + throw ["Found generics without a path"]; } parserState.pos += 1; getItemsBefore(query, parserState, generics, ">"); @@ -411,24 +433,51 @@ function initSearch(rawSearchIndex) { foundStopChar = true; continue; } else if (c === ":" && isPathStart(parserState)) { - throw new Error("Unexpected `::`: paths cannot start with `::`"); + throw ["Unexpected ", "::", ": paths cannot start with ", "::"]; } else if (c === ":" || isEndCharacter(c)) { let extra = ""; if (endChar === ">") { - extra = "`<`"; + extra = "<"; } else if (endChar === "") { - extra = "`->`"; + extra = "->"; + } else { + extra = endChar; } - throw new Error("Unexpected `" + c + "` after " + extra); + throw ["Unexpected ", c, " after ", extra]; } if (!foundStopChar) { if (endChar !== "") { - throw new Error(`Expected \`,\`, \` \` or \`${endChar}\`, found \`${c}\``); + throw [ + "Expected ", + ",", // comma + ", ", + " ", // whitespace + " or ", + endChar, + ", found ", + c, + ]; } - throw new Error(`Expected \`,\` or \` \`, found \`${c}\``); + throw [ + "Expected ", + ",", // comma + " or ", + " ", // whitespace + ", found ", + c, + ]; } const posBefore = parserState.pos; getNextElem(query, parserState, elems, endChar === ">"); + if (endChar !== "") { + if (parserState.pos >= parserState.length) { + throw ["Unclosed ", "<"]; + } + const c2 = parserState.userQuery[parserState.pos]; + if (!isSeparatorCharacter(c2) && c2 !== endChar) { + throw ["Expected ", endChar, ", found ", c2]; + } + } // This case can be encountered if `getNextElem` encountered a "stop character" right // from the start. For example if you have `,,` or `<>`. In this case, we simply move up // the current position to continue the parsing. @@ -437,7 +486,10 @@ function initSearch(rawSearchIndex) { } foundStopChar = false; } - // We are either at the end of the string or on the `endChar`` character, let's move forward + if (parserState.pos >= parserState.length && endChar !== "") { + throw ["Unclosed ", "<"]; + } + // We are either at the end of the string or on the `endChar` character, let's move forward // in any case. parserState.pos += 1; } @@ -453,7 +505,7 @@ function initSearch(rawSearchIndex) { for (let pos = 0; pos < parserState.pos; ++pos) { if (!isIdentCharacter(query[pos]) && !isWhitespaceCharacter(query[pos])) { - throw new Error(`Unexpected \`${query[pos]}\` in type filter`); + throw ["Unexpected ", query[pos], " in type filter"]; } } } @@ -466,11 +518,10 @@ function initSearch(rawSearchIndex) { * @param {ParserState} parserState */ function parseInput(query, parserState) { - let c, before; let foundStopChar = true; while (parserState.pos < parserState.length) { - c = parserState.userQuery[parserState.pos]; + const c = parserState.userQuery[parserState.pos]; if (isStopCharacter(c)) { foundStopChar = true; if (isSeparatorCharacter(c)) { @@ -480,19 +531,19 @@ function initSearch(rawSearchIndex) { if (isReturnArrow(parserState)) { break; } - throw new Error(`Unexpected \`${c}\` (did you mean \`->\`?)`); + throw ["Unexpected ", c, " (did you mean ", "->", "?)"]; } - throw new Error(`Unexpected \`${c}\``); + throw ["Unexpected ", c]; } else if (c === ":" && !isPathStart(parserState)) { if (parserState.typeFilter !== null) { - throw new Error("Unexpected `:`"); + throw ["Unexpected ", ":"]; } if (query.elems.length === 0) { - throw new Error("Expected type filter before `:`"); + throw ["Expected type filter before ", ":"]; } else if (query.elems.length !== 1 || parserState.totalElems !== 1) { - throw new Error("Unexpected `:`"); + throw ["Unexpected ", ":"]; } else if (query.literalSearch) { - throw new Error("You cannot use quotes on type filter"); + throw ["You cannot use quotes on type filter"]; } checkExtraTypeFilterCharacters(parserState); // The type filter doesn't count as an element since it's a modifier. @@ -505,11 +556,31 @@ function initSearch(rawSearchIndex) { } if (!foundStopChar) { if (parserState.typeFilter !== null) { - throw new Error(`Expected \`,\`, \` \` or \`->\`, found \`${c}\``); + throw [ + "Expected ", + ",", // comma + ", ", + " ", // whitespace + " or ", + "->", // arrow + ", found ", + c, + ]; } - throw new Error(`Expected \`,\`, \` \`, \`:\` or \`->\`, found \`${c}\``); - } - before = query.elems.length; + throw [ + "Expected ", + ",", // comma + ", ", + " ", // whitespace + ", ", + ":", // colon + " or ", + "->", // arrow + ", found ", + c, + ]; + } + const before = query.elems.length; getNextElem(query, parserState, query.elems, false); if (query.elems.length === before) { // Nothing was added, weird... Let's increase the position to not remain stuck. @@ -518,14 +589,13 @@ function initSearch(rawSearchIndex) { foundStopChar = false; } while (parserState.pos < parserState.length) { - c = parserState.userQuery[parserState.pos]; if (isReturnArrow(parserState)) { parserState.pos += 2; // Get returned elements. getItemsBefore(query, parserState, query.returned, ""); // Nothing can come afterward! if (query.returned.length === 0) { - throw new Error("Expected at least one item after `->`"); + throw ["Expected at least one item after ", "->"]; } break; } else { @@ -594,8 +664,8 @@ function initSearch(rawSearchIndex) { * * The supported syntax by this parser is as follow: * - * ident = *(ALPHA / DIGIT / "_") [!] - * path = ident *(DOUBLE-COLON ident) + * ident = *(ALPHA / DIGIT / "_") + * path = ident *(DOUBLE-COLON ident) [!] * arg = path [generics] * arg-without-generic = path * type-sep = COMMA/WS *(COMMA/WS) @@ -679,7 +749,7 @@ function initSearch(rawSearchIndex) { } } catch (err) { query = newParsedQuery(userQuery); - query.error = err.message; + query.error = err; query.typeFilter = -1; return query; } @@ -897,13 +967,13 @@ function initSearch(rawSearchIndex) { * @param {QueryElement} elem - The element from the parsed query. * @param {integer} defaultLev - This is the value to return in case there are no generics. * - * @return {integer} - Returns the best match (if any) or `MAX_LEV_DISTANCE + 1`. + * @return {integer} - Returns the best match (if any) or `maxLevDistance + 1`. */ - function checkGenerics(row, elem, defaultLev) { + function checkGenerics(row, elem, defaultLev, maxLevDistance) { if (row.generics.length === 0) { - return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1; + return elem.generics.length === 0 ? defaultLev : maxLevDistance + 1; } else if (row.generics.length > 0 && row.generics[0].name === null) { - return checkGenerics(row.generics[0], elem, defaultLev); + return checkGenerics(row.generics[0], elem, defaultLev, maxLevDistance); } // The names match, but we need to be sure that all generics kinda // match as well. @@ -914,8 +984,8 @@ function initSearch(rawSearchIndex) { elem_name = entry.name; if (elem_name === "") { // Pure generic, needs to check into it. - if (checkGenerics(entry, elem, MAX_LEV_DISTANCE + 1) !== 0) { - return MAX_LEV_DISTANCE + 1; + if (checkGenerics(entry, elem, maxLevDistance + 1, maxLevDistance) !== 0) { + return maxLevDistance + 1; } continue; } @@ -942,7 +1012,7 @@ function initSearch(rawSearchIndex) { } } if (match === null) { - return MAX_LEV_DISTANCE + 1; + return maxLevDistance + 1; } elems[match] -= 1; if (elems[match] === 0) { @@ -951,7 +1021,7 @@ function initSearch(rawSearchIndex) { } return 0; } - return MAX_LEV_DISTANCE + 1; + return maxLevDistance + 1; } /** @@ -963,10 +1033,10 @@ function initSearch(rawSearchIndex) { * * @return {integer} - Returns a Levenshtein distance to the best match. */ - function checkIfInGenerics(row, elem) { - let lev = MAX_LEV_DISTANCE + 1; + function checkIfInGenerics(row, elem, maxLevDistance) { + let lev = maxLevDistance + 1; for (const entry of row.generics) { - lev = Math.min(checkType(entry, elem, true), lev); + lev = Math.min(checkType(entry, elem, true, maxLevDistance), lev); if (lev === 0) { break; } @@ -983,15 +1053,15 @@ function initSearch(rawSearchIndex) { * @param {boolean} literalSearch * * @return {integer} - Returns a Levenshtein distance to the best match. If there is - * no match, returns `MAX_LEV_DISTANCE + 1`. + * no match, returns `maxLevDistance + 1`. */ - function checkType(row, elem, literalSearch) { + function checkType(row, elem, literalSearch, maxLevDistance) { if (row.name === null) { // This is a pure "generic" search, no need to run other checks. if (row.generics.length > 0) { - return checkIfInGenerics(row, elem); + return checkIfInGenerics(row, elem, maxLevDistance); } - return MAX_LEV_DISTANCE + 1; + return maxLevDistance + 1; } let lev = levenshtein(row.name, elem.name); @@ -1005,9 +1075,9 @@ function initSearch(rawSearchIndex) { return 0; } } - return MAX_LEV_DISTANCE + 1; + return maxLevDistance + 1; } else if (elem.generics.length > 0) { - return checkGenerics(row, elem, MAX_LEV_DISTANCE + 1); + return checkGenerics(row, elem, maxLevDistance + 1, maxLevDistance); } return 0; } else if (row.generics.length > 0) { @@ -1017,22 +1087,20 @@ function initSearch(rawSearchIndex) { } // The name didn't match so we now check if the type we're looking for is inside // the generics! - lev = checkIfInGenerics(row, elem); - // Now whatever happens, the returned distance is "less good" so we should mark - // it as such, and so we add 0.5 to the distance to make it "less good". - return lev + 0.5; - } else if (lev > MAX_LEV_DISTANCE) { + lev = Math.min(lev, checkIfInGenerics(row, elem, maxLevDistance)); + return lev; + } else if (lev > maxLevDistance) { // So our item's name doesn't match at all and has generics. // // Maybe it's present in a sub generic? For example "f>>()", if we're // looking for "B", we'll need to go down. - return checkIfInGenerics(row, elem); + return checkIfInGenerics(row, elem, maxLevDistance); } else { // At this point, the name kinda match and we have generics to check, so // let's go! - const tmp_lev = checkGenerics(row, elem, lev); - if (tmp_lev > MAX_LEV_DISTANCE) { - return MAX_LEV_DISTANCE + 1; + const tmp_lev = checkGenerics(row, elem, lev, maxLevDistance); + if (tmp_lev > maxLevDistance) { + return maxLevDistance + 1; } // We compute the median value of both checks and return it. return (tmp_lev + lev) / 2; @@ -1040,7 +1108,7 @@ function initSearch(rawSearchIndex) { } else if (elem.generics.length > 0) { // In this case, we were expecting generics but there isn't so we simply reject this // one. - return MAX_LEV_DISTANCE + 1; + return maxLevDistance + 1; } // No generics on our query or on the target type so we can return without doing // anything else. @@ -1055,23 +1123,26 @@ function initSearch(rawSearchIndex) { * @param {integer} typeFilter * * @return {integer} - Returns a Levenshtein distance to the best match. If there is no - * match, returns `MAX_LEV_DISTANCE + 1`. + * match, returns `maxLevDistance + 1`. */ - function findArg(row, elem, typeFilter) { - let lev = MAX_LEV_DISTANCE + 1; + function findArg(row, elem, typeFilter, maxLevDistance) { + let lev = maxLevDistance + 1; if (row && row.type && row.type.inputs && row.type.inputs.length > 0) { for (const input of row.type.inputs) { if (!typePassesFilter(typeFilter, input.ty)) { continue; } - lev = Math.min(lev, checkType(input, elem, parsedQuery.literalSearch)); + lev = Math.min( + lev, + checkType(input, elem, parsedQuery.literalSearch, maxLevDistance) + ); if (lev === 0) { return 0; } } } - return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev; + return parsedQuery.literalSearch ? maxLevDistance + 1 : lev; } /** @@ -1082,10 +1153,10 @@ function initSearch(rawSearchIndex) { * @param {integer} typeFilter * * @return {integer} - Returns a Levenshtein distance to the best match. If there is no - * match, returns `MAX_LEV_DISTANCE + 1`. + * match, returns `maxLevDistance + 1`. */ - function checkReturned(row, elem, typeFilter) { - let lev = MAX_LEV_DISTANCE + 1; + function checkReturned(row, elem, typeFilter, maxLevDistance) { + let lev = maxLevDistance + 1; if (row && row.type && row.type.output.length > 0) { const ret = row.type.output; @@ -1093,20 +1164,23 @@ function initSearch(rawSearchIndex) { if (!typePassesFilter(typeFilter, ret_ty.ty)) { continue; } - lev = Math.min(lev, checkType(ret_ty, elem, parsedQuery.literalSearch)); + lev = Math.min( + lev, + checkType(ret_ty, elem, parsedQuery.literalSearch, maxLevDistance) + ); if (lev === 0) { return 0; } } } - return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev; + return parsedQuery.literalSearch ? maxLevDistance + 1 : lev; } - function checkPath(contains, ty) { + function checkPath(contains, ty, maxLevDistance) { if (contains.length === 0) { return 0; } - let ret_lev = MAX_LEV_DISTANCE + 1; + let ret_lev = maxLevDistance + 1; const path = ty.path.split("::"); if (ty.parent && ty.parent.name) { @@ -1116,7 +1190,7 @@ function initSearch(rawSearchIndex) { const length = path.length; const clength = contains.length; if (clength > length) { - return MAX_LEV_DISTANCE + 1; + return maxLevDistance + 1; } for (let i = 0; i < length; ++i) { if (i + clength > length) { @@ -1126,7 +1200,7 @@ function initSearch(rawSearchIndex) { let aborted = false; for (let x = 0; x < clength; ++x) { const lev = levenshtein(path[i + x], contains[x]); - if (lev > MAX_LEV_DISTANCE) { + if (lev > maxLevDistance) { aborted = true; break; } @@ -1231,7 +1305,7 @@ function initSearch(rawSearchIndex) { * following condition: * * * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0. - * * If it is not a "literal search", `lev` must be <= `MAX_LEV_DISTANCE`. + * * If it is not a "literal search", `lev` must be <= `maxLevDistance`. * * The `results` map contains information which will be used to sort the search results: * @@ -1249,8 +1323,8 @@ function initSearch(rawSearchIndex) { * @param {integer} lev * @param {integer} path_lev */ - function addIntoResults(results, fullId, id, index, lev, path_lev) { - const inBounds = lev <= MAX_LEV_DISTANCE || index !== -1; + function addIntoResults(results, fullId, id, index, lev, path_lev, maxLevDistance) { + const inBounds = lev <= maxLevDistance || index !== -1; if (lev === 0 || (!parsedQuery.literalSearch && inBounds)) { if (results[fullId] !== undefined) { const result = results[fullId]; @@ -1289,7 +1363,8 @@ function initSearch(rawSearchIndex) { elem, results_others, results_in_args, - results_returned + results_returned, + maxLevDistance ) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; @@ -1298,13 +1373,13 @@ function initSearch(rawSearchIndex) { const fullId = row.id; const searchWord = searchWords[pos]; - const in_args = findArg(row, elem, parsedQuery.typeFilter); - const returned = checkReturned(row, elem, parsedQuery.typeFilter); + const in_args = findArg(row, elem, parsedQuery.typeFilter, maxLevDistance); + const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxLevDistance); // path_lev is 0 because no parent path information is currently stored // in the search index - addIntoResults(results_in_args, fullId, pos, -1, in_args, 0); - addIntoResults(results_returned, fullId, pos, -1, returned, 0); + addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxLevDistance); + addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxLevDistance); if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) { return; @@ -1328,16 +1403,16 @@ function initSearch(rawSearchIndex) { // No need to check anything else if it's a "pure" generics search. if (elem.name.length === 0) { if (row.type !== null) { - lev = checkGenerics(row.type, elem, MAX_LEV_DISTANCE + 1); + lev = checkGenerics(row.type, elem, maxLevDistance + 1, maxLevDistance); // path_lev is 0 because we know it's empty - addIntoResults(results_others, fullId, pos, index, lev, 0); + addIntoResults(results_others, fullId, pos, index, lev, 0, maxLevDistance); } return; } if (elem.fullPath.length > 1) { - path_lev = checkPath(elem.pathWithoutLast, row); - if (path_lev > MAX_LEV_DISTANCE) { + path_lev = checkPath(elem.pathWithoutLast, row, maxLevDistance); + if (path_lev > maxLevDistance) { return; } } @@ -1351,11 +1426,11 @@ function initSearch(rawSearchIndex) { lev = levenshtein(searchWord, elem.pathLast); - if (index === -1 && lev + path_lev > MAX_LEV_DISTANCE) { + if (index === -1 && lev + path_lev > maxLevDistance) { return; } - addIntoResults(results_others, fullId, pos, index, lev, path_lev); + addIntoResults(results_others, fullId, pos, index, lev, path_lev, maxLevDistance); } /** @@ -1367,7 +1442,7 @@ function initSearch(rawSearchIndex) { * @param {integer} pos - Position in the `searchIndex`. * @param {Object} results */ - function handleArgs(row, pos, results) { + function handleArgs(row, pos, results, maxLevDistance) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; } @@ -1379,7 +1454,7 @@ function initSearch(rawSearchIndex) { function checkArgs(elems, callback) { for (const elem of elems) { // There is more than one parameter to the query so all checks should be "exact" - const lev = callback(row, elem, NO_TYPE_FILTER); + const lev = callback(row, elem, NO_TYPE_FILTER, maxLevDistance); if (lev <= 1) { nbLev += 1; totalLev += lev; @@ -1400,12 +1475,21 @@ function initSearch(rawSearchIndex) { return; } const lev = Math.round(totalLev / nbLev); - addIntoResults(results, row.id, pos, 0, lev, 0); + addIntoResults(results, row.id, pos, 0, lev, 0, maxLevDistance); } function innerRunQuery() { let elem, i, nSearchWords, in_returned, row; + let queryLen = 0; + for (const elem of parsedQuery.elems) { + queryLen += elem.name.length; + } + for (const elem of parsedQuery.returned) { + queryLen += elem.name.length; + } + const maxLevDistance = Math.floor(queryLen / 3); + if (parsedQuery.foundElems === 1) { if (parsedQuery.elems.length === 1) { elem = parsedQuery.elems[0]; @@ -1418,7 +1502,8 @@ function initSearch(rawSearchIndex) { elem, results_others, results_in_args, - results_returned + results_returned, + maxLevDistance ); } } else if (parsedQuery.returned.length === 1) { @@ -1426,13 +1511,18 @@ function initSearch(rawSearchIndex) { elem = parsedQuery.returned[0]; for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { row = searchIndex[i]; - in_returned = checkReturned(row, elem, parsedQuery.typeFilter); - addIntoResults(results_others, row.id, i, -1, in_returned); + in_returned = checkReturned( + row, + elem, + parsedQuery.typeFilter, + maxLevDistance + ); + addIntoResults(results_others, row.id, i, -1, in_returned, maxLevDistance); } } } else if (parsedQuery.foundElems > 0) { for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { - handleArgs(searchIndex[i], i, results_others); + handleArgs(searchIndex[i], i, results_others, maxLevDistance); } } } @@ -1470,7 +1560,7 @@ function initSearch(rawSearchIndex) { * * @return {boolean} - Whether the result is valid or not */ - function validateResult(name, path, keys, parent) { + function validateResult(name, path, keys, parent, maxLevDistance) { if (!keys || !keys.length) { return true; } @@ -1485,7 +1575,7 @@ function initSearch(rawSearchIndex) { (parent !== undefined && parent.name !== undefined && parent.name.toLowerCase().indexOf(key) > -1) || // lastly check to see if the name was a levenshtein match - levenshtein(name, key) <= MAX_LEV_DISTANCE)) { + levenshtein(name, key) <= maxLevDistance)) { return false; } } @@ -1725,7 +1815,16 @@ function initSearch(rawSearchIndex) { let output = `

    Results${crates}

    `; if (results.query.error !== null) { - output += `

    Query parser error: "${results.query.error}".

    `; + const error = results.query.error; + error.forEach((value, index) => { + value = value.split("<").join("<").split(">").join(">"); + if (index % 2 !== 0) { + error[index] = `${value}`; + } else { + error[index] = value; + } + }); + output += `

    Query parser error: "${error.join("")}".

    `; output += "
    " + makeTabHeader(0, "In Names", ret_others[1]) + "
    "; @@ -1922,7 +2021,7 @@ function initSearch(rawSearchIndex) { * @type {Array} */ const searchWords = []; - let i, word; + const charA = "A".charCodeAt(0); let currentIndex = 0; let id = 0; @@ -1936,7 +2035,7 @@ function initSearch(rawSearchIndex) { /** * The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f` * are arrays with the same length. n[i] contains the name of an item. - * t[i] contains the type of that item (as a small integer that represents an + * t[i] contains the type of that item (as a string of characters that represent an * offset in `itemTypes`). d[i] contains the description of that item. * * q[i] contains the full path of the item, or an empty string indicating @@ -1963,7 +2062,7 @@ function initSearch(rawSearchIndex) { * doc: string, * a: Object, * n: Array, - * t: Array, + * t: String, * d: Array, * q: Array, * i: Array, @@ -1992,7 +2091,7 @@ function initSearch(rawSearchIndex) { searchIndex.push(crateRow); currentIndex += 1; - // an array of (Number) item types + // a String of one character item type codes const itemTypes = crateCorpus.t; // an array of (String) item names const itemNames = crateCorpus.n; @@ -2017,7 +2116,7 @@ function initSearch(rawSearchIndex) { // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = paths.length; - for (i = 0; i < len; ++i) { + for (let i = 0; i < len; ++i) { lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()}); paths[i] = {ty: paths[i][0], name: paths[i][1]}; } @@ -2031,19 +2130,17 @@ function initSearch(rawSearchIndex) { // faster analysis operations len = itemTypes.length; let lastPath = ""; - for (i = 0; i < len; ++i) { + for (let i = 0; i < len; ++i) { + let word = ""; // This object should have exactly the same set of fields as the "crateRow" // object defined above. if (typeof itemNames[i] === "string") { word = itemNames[i].toLowerCase(); - searchWords.push(word); - } else { - word = ""; - searchWords.push(""); } + searchWords.push(word); const row = { crate: crate, - ty: itemTypes[i], + ty: itemTypes.charCodeAt(i) - charA, name: itemNames[i], path: itemPaths[i] ? itemPaths[i] : lastPath, desc: itemDescs[i], diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 84df1b7d3..1cd552e7f 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -1,5 +1,5 @@ // Local js definitions: -/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme */ +/* global getSettingValue, getVirtualKey, updateLocalStorage, updateTheme */ /* global addClass, removeClass, onEach, onEachLazy, blurHandler, elemIsInParent */ /* global MAIN_ID, getVar, getSettingsButton */ @@ -19,7 +19,7 @@ case "theme": case "preferred-dark-theme": case "preferred-light-theme": - updateSystemTheme(); + updateTheme(); updateLightAndDark(); break; case "line-numbers": @@ -48,13 +48,13 @@ } function showLightAndDark() { - removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden"); - removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden"); + removeClass(document.getElementById("preferred-light-theme"), "hidden"); + removeClass(document.getElementById("preferred-dark-theme"), "hidden"); } function hideLightAndDark() { - addClass(document.getElementById("preferred-light-theme").parentElement, "hidden"); - addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden"); + addClass(document.getElementById("preferred-light-theme"), "hidden"); + addClass(document.getElementById("preferred-dark-theme"), "hidden"); } function updateLightAndDark() { @@ -80,17 +80,6 @@ toggle.onkeyup = handleKey; toggle.onkeyrelease = handleKey; }); - onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), elem => { - const select = elem.getElementsByTagName("select")[0]; - const settingId = select.id; - const settingValue = getSettingValue(settingId); - if (settingValue !== null) { - select.value = settingValue; - } - select.onchange = function() { - changeSetting(this.id, this.value); - }; - }); onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => { const settingId = elem.name; let settingValue = getSettingValue(settingId); @@ -127,38 +116,40 @@ let output = ""; for (const setting of settings) { - output += "
    "; const js_data_name = setting["js_name"]; const setting_name = setting["name"]; if (setting["options"] !== undefined) { // This is a select setting. output += `\ -
    -
    ${setting_name}
    -
    `; +
    +
    ${setting_name}
    +
    `; onEach(setting["options"], option => { const checked = option === setting["default"] ? " checked" : ""; const full = `${js_data_name}-${option.replace(/ /g,"-")}`; output += `\ -`; + `; }); - output += "
    "; + output += `\ +
    +
    `; } else { // This is a checkbox toggle. const checked = setting["default"] === true ? " checked" : ""; output += `\ -`; +
    \ + \ +
    `; } - output += "
    "; } return output; } diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 0e1c864e6..6c0f03b5b 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -117,8 +117,7 @@ function createSourceSidebar() { sidebar.appendChild(title); Object.keys(sourcesIndex).forEach(key => { sourcesIndex[key][NAME_OFFSET] = key; - hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "", - hasFoundFile); + hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "", hasFoundFile); }); container.appendChild(sidebar); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index db2db83ca..c72ac254f 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -51,7 +51,6 @@ function hasClass(elem, className) { return elem && elem.classList && elem.classList.contains(className); } -// eslint-disable-next-line no-unused-vars function addClass(elem, className) { if (!elem || !elem.classList) { return; @@ -153,79 +152,74 @@ function switchTheme(styleElem, mainStyleElem, newThemeName, saveTheme) { } } -// This function is called from "main.js". -// eslint-disable-next-line no-unused-vars -function useSystemTheme(value) { - if (value === undefined) { - value = true; - } - - updateLocalStorage("use-system-theme", value); - - // update the toggle if we're on the settings page - const toggle = document.getElementById("use-system-theme"); - if (toggle && toggle instanceof HTMLInputElement) { - toggle.checked = value; - } -} - -const updateSystemTheme = (function() { - if (!window.matchMedia) { - // fallback to the CSS computed value - return () => { - const cssTheme = getComputedStyle(document.documentElement) - .getPropertyValue("content"); - - switchTheme( - window.currentTheme, - window.mainTheme, - JSON.parse(cssTheme) || "light", - true - ); +const updateTheme = (function() { + /** + * Update the current theme to match whatever the current combination of + * * the preference for using the system theme + * (if this is the case, the value of preferred-light-theme, if the + * system theme is light, otherwise if dark, the value of + * preferred-dark-theme.) + * * the preferred theme + * … dictates that it should be. + */ + function updateTheme() { + const use = (theme, saveTheme) => { + switchTheme(window.currentTheme, window.mainTheme, theme, saveTheme); }; - } - - // only listen to (prefers-color-scheme: dark) because light is the default - const mql = window.matchMedia("(prefers-color-scheme: dark)"); - function handlePreferenceChange(mql) { - const use = theme => { - switchTheme(window.currentTheme, window.mainTheme, theme, true); - }; // maybe the user has disabled the setting in the meantime! if (getSettingValue("use-system-theme") !== "false") { const lightTheme = getSettingValue("preferred-light-theme") || "light"; const darkTheme = getSettingValue("preferred-dark-theme") || "dark"; - if (mql.matches) { - use(darkTheme); + if (isDarkMode()) { + use(darkTheme, true); } else { // prefers a light theme, or has no preference - use(lightTheme); + use(lightTheme, true); } // note: we save the theme so that it doesn't suddenly change when // the user disables "use-system-theme" and reloads the page or // navigates to another page } else { - use(getSettingValue("theme")); + use(getSettingValue("theme"), false); } } - mql.addListener(handlePreferenceChange); + // This is always updated below to a function () => bool. + let isDarkMode; - return () => { - handlePreferenceChange(mql); - }; -})(); + // Determine the function for isDarkMode, and if we have + // `window.matchMedia`, set up an event listener on the preferred color + // scheme. + // + // Otherwise, fall back to the prefers-color-scheme value CSS captured in + // the "content" property. + if (window.matchMedia) { + // only listen to (prefers-color-scheme: dark) because light is the default + const mql = window.matchMedia("(prefers-color-scheme: dark)"); -function switchToSavedTheme() { - switchTheme( - window.currentTheme, - window.mainTheme, - getSettingValue("theme") || "light", - false - ); -} + isDarkMode = () => mql.matches; + + if (mql.addEventListener) { + mql.addEventListener("change", updateTheme); + } else { + // This is deprecated, see: + // https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListener + mql.addListener(updateTheme); + } + } else { + // fallback to the CSS computed value + const cssContent = getComputedStyle(document.documentElement) + .getPropertyValue("content"); + // (Note: the double-quotes come from that this is a CSS value, which + // might be a length, string, etc.) + const cssColorScheme = cssContent || "\"light\""; + isDarkMode = () => (cssColorScheme === "\"dark\""); + } + + return updateTheme; +})(); if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { // update the preferred dark theme if the user is already using a dark theme @@ -235,13 +229,10 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { && darkThemes.indexOf(localStoredTheme) >= 0) { updateLocalStorage("preferred-dark-theme", localStoredTheme); } - - // call the function to initialize the theme at least once! - updateSystemTheme(); -} else { - switchToSavedTheme(); } +updateTheme(); + if (getSettingValue("source-sidebar-show") === "true") { // At this point in page load, `document.body` is not available yet. // Set a class on the `` element instead. @@ -259,6 +250,6 @@ if (getSettingValue("source-sidebar-show") === "true") { // specifically when talking to a remote website with no caching. window.addEventListener("pageshow", ev => { if (ev.persisted) { - setTimeout(switchToSavedTheme, 0); + setTimeout(updateTheme, 0); } }); diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index b48b82307..767b974cc 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -102,9 +102,6 @@ static_files! { scrape_examples_js => "static/js/scrape-examples.js", wheel_svg => "static/images/wheel.svg", clipboard_svg => "static/images/clipboard.svg", - down_arrow_svg => "static/images/down-arrow.svg", - toggle_minus_png => "static/images/toggle-minus.svg", - toggle_plus_png => "static/images/toggle-plus.svg", copyright => "static/COPYRIGHT.txt", license_apache => "static/LICENSE-APACHE.txt", license_mit => "static/LICENSE-MIT.txt", diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index fddda293b..7690d8f25 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -5,7 +5,6 @@ {#- -#} {#- -#} {#- -#} - {#- -#} {{page.title}} {#- -#} {#- -#} {#- -#} @@ -24,11 +23,13 @@ {%- for theme in themes -%} {#- -#} {%- endfor -%} + {%- if !layout.default_settings.is_empty() -%} {#- -#} + {%- endif -%} {#- -#} {%- if page.css_class.contains("crate") -%} {#- -#} diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index ee2880bf6..3a1867b7f 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -6,7 +6,7 @@ {{component.name}}:: {%- endfor -%} {{name}} {#- -#} -